Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var ApplyCmd = &cli.Command{ Name: "apply", Usage: "Update an existing stack definition", Description: "Update an existing stack definition from a TOML or JSON file.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "file", Usage: "Path to the TOML or JSON definition file", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } req, err := loadStackDef(ctx, cmd.GetStringArg("file"), client) if err != nil { fmt.Println("Error reading definition:", err) os.Exit(1) } existing, err := client.GetStackDefinitionByName(ctx, req.Name) if err != nil { fmt.Println("Error checking for existing definition:", err) os.Exit(1) } if existing == nil { fmt.Printf("Stack definition %q not found. Use 'create-def' to create it.\n", req.Name) os.Exit(1) } _, err = client.UpdateStackDefinition(ctx, existing.Id, req) if err != nil { fmt.Println("Error updating stack definition:", err) os.Exit(1) } fmt.Printf("Stack definition %q updated.\n", req.Name) return nil }, }
View Source
var CreateCmd = &cli.Command{ Name: "create", Usage: "Create spaces from a stack definition", Description: "Create spaces from a stack definition, prefixed and grouped under a stack name.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "definition", Usage: "Name of the stack definition to use", Required: true, }, &cli.StringArg{ Name: "prefix", Usage: "Prefix for space names (spaces are named prefix-key)", Required: true, }, &cli.StringArg{ Name: "name", Usage: "Stack name to group spaces under (defaults to prefix)", Required: false, }, }, Run: func(ctx context.Context, cmd *cli.Command) error { defName := cmd.GetStringArg("definition") prefix := cmd.GetStringArg("prefix") stackName := cmd.GetStringArg("name") if stackName == "" { stackName = prefix } client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } def, err := client.GetStackDefinitionByName(ctx, defName) if err != nil { fmt.Println("Error looking up stack definition:", err) os.Exit(1) } if def == nil { fmt.Printf("Stack definition %q not found.\n", defName) os.Exit(1) } // Resolve template names to IDs for each component type createdSpace struct { key string id string space *apiclient.StackDefSpace } spaces := make([]createdSpace, 0, len(def.Spaces)) if exists, err := client.StackExists(ctx, stackName); err != nil { fmt.Println("Error checking for existing stack:", err) os.Exit(1) } else if exists { fmt.Printf("Stack %q already exists. Use a different stack name or delete the existing stack first.\n", stackName) os.Exit(1) } for i := range def.Spaces { comp := &def.Spaces[i] spaceName := prefix + "-" + comp.Name templateId := comp.TemplateId customFields := make([]apiclient.CustomFieldValue, 0, len(comp.CustomFields)) for _, cf := range comp.CustomFields { customFields = append(customFields, apiclient.CustomFieldValue{ Name: cf.Name, Value: cf.Value, }) } spaceId, _, err := client.CreateSpace(ctx, &apiclient.SpaceRequest{ Name: spaceName, TemplateId: templateId, Stack: stackName, StackPrefix: prefix, Description: comp.Description, Shell: comp.Shell, CustomFields: customFields, }) if err != nil { fmt.Printf("Error creating space %q: %v\n", spaceName, err) for _, s := range spaces { client.DeleteSpace(ctx, s.id) } os.Exit(1) } spaces = append(spaces, createdSpace{key: comp.Name, id: spaceId, space: comp}) fmt.Printf(" Created space %q (%s)\n", spaceName, spaceId) } keyToID := make(map[string]string) for _, s := range spaces { keyToID[s.key] = s.id } for _, s := range spaces { if len(s.space.DependsOn) == 0 { continue } depIDs := make([]string, 0, len(s.space.DependsOn)) for _, depKey := range s.space.DependsOn { if id, ok := keyToID[depKey]; ok { depIDs = append(depIDs, id) } else { fmt.Printf(" Warning: dependency %q not found for space %q\n", depKey, s.key) } } if len(depIDs) > 0 { spaceName := prefix + "-" + s.key _, err := client.UpdateSpace(ctx, s.id, &apiclient.SpaceRequest{ Name: spaceName, DependsOn: depIDs, Stack: stackName, }) if err != nil { fmt.Printf(" Warning: failed to set dependencies for %q: %v\n", spaceName, err) } } } for _, s := range spaces { if len(s.space.PortForwards) == 0 { continue } forwards := make([]apiclient.PortForwardRequest, 0, len(s.space.PortForwards)) for _, pf := range s.space.PortForwards { targetID, ok := keyToID[pf.ToSpace] if !ok { fmt.Printf(" Warning: port forward target %q not found for space %q\n", pf.ToSpace, s.key) continue } forwards = append(forwards, apiclient.PortForwardRequest{ LocalPort: pf.LocalPort, Space: targetID, RemotePort: pf.RemotePort, Persistent: true, }) } if len(forwards) > 0 { _, _, err := client.ApplyPorts(ctx, s.id, &apiclient.PortApplyRequest{Forwards: forwards}) if err != nil { fmt.Printf(" Warning: failed to apply port forwards for space %q: %v\n", s.key, err) } } } fmt.Printf("\nStack %q created from definition %q with %d space(s).\n", stackName, defName, len(spaces)) fmt.Printf("Run 'knot stack start %s' to start all spaces.\n", stackName) return nil }, }
View Source
var CreateDefCmd = &cli.Command{ Name: "create-def", Usage: "Create a new stack definition", Description: "Create a new stack definition from a TOML or JSON file.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "file", Usage: "Path to the TOML or JSON definition file", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } req, err := loadStackDef(ctx, cmd.GetStringArg("file"), client) if err != nil { fmt.Println("Error reading definition:", err) os.Exit(1) } existing, err := client.GetStackDefinitionByName(ctx, req.Name) if err != nil { fmt.Println("Error checking for existing definition:", err) os.Exit(1) } if existing != nil { fmt.Printf("Stack definition %q already exists. Use 'apply' to update it.\n", req.Name) os.Exit(1) } _, _, err = client.CreateStackDefinition(ctx, req) if err != nil { fmt.Println("Error creating stack definition:", err) os.Exit(1) } fmt.Printf("Stack definition %q created.\n", req.Name) return nil }, }
View Source
var DeleteCmd = &cli.Command{ Name: "delete", Usage: "Delete a stack and all its spaces", Description: "Delete all spaces belonging to the named stack.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "name", Usage: "Name of the stack to delete", Required: true, }, }, MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "yes", Aliases: []string{"y"}, Usage: "Skip confirmation prompt", }, }, Run: func(ctx context.Context, cmd *cli.Command) error { name := cmd.GetStringArg("name") client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } user, err := client.WhoAmI(ctx) if err != nil { fmt.Println("Error getting user:", err) os.Exit(1) } spaces, _, err := client.GetSpaces(ctx, user.Id, false) if err != nil { fmt.Println("Error getting spaces:", err) os.Exit(1) } // Collect spaces belonging to this stack type stackSpace struct { id string name string } var stackSpaces []stackSpace for _, s := range spaces.Spaces { if s.Stack == name { stackSpaces = append(stackSpaces, stackSpace{id: s.Id, name: s.Name}) } } if len(stackSpaces) == 0 { fmt.Printf("No spaces found for stack %q.\n", name) return nil } if !cmd.GetBool("yes") { fmt.Printf("Delete stack %q and its %d space(s)?\n", name, len(stackSpaces)) for _, s := range stackSpaces { fmt.Printf(" - %s\n", s.name) } fmt.Print("\n[y/N] ") reader := bufio.NewReader(os.Stdin) answer, _ := reader.ReadString('\n') if strings.ToLower(strings.TrimSpace(answer)) != "y" { fmt.Println("Aborted.") return nil } } _, err = client.DeleteStack(ctx, name) if err != nil { fmt.Printf("Error deleting stack %q: %v\n", name, err) os.Exit(1) } fmt.Printf("Stack %q deleting.\n", name) return nil }, }
View Source
var DeleteDefCmd = &cli.Command{ Name: "delete-def", Usage: "Delete a stack definition", Description: "Delete a stack definition by name.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "name", Usage: "Name of the stack definition to delete", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { name := cmd.GetStringArg("name") client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } def, err := client.GetStackDefinitionByName(ctx, name) if err != nil { fmt.Println("Error looking up stack definition:", err) os.Exit(1) } if def == nil { fmt.Printf("Stack definition %q not found.\n", name) os.Exit(1) } _, err = client.DeleteStackDefinition(ctx, def.Id) if err != nil { fmt.Println("Error deleting stack definition:", err) os.Exit(1) } fmt.Printf("Stack definition %q deleted.\n", name) return nil }, }
View Source
var DisableCmd = &cli.Command{ Name: "disable-def", Usage: "Disable a stack definition", Description: "Disable a stack definition so it cannot be used to create stacks.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "name", Usage: "Name of the stack definition to disable", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { name := cmd.GetStringArg("name") client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } def, err := client.GetStackDefinitionByName(ctx, name) if err != nil { fmt.Println("Error looking up stack definition:", err) os.Exit(1) } if def == nil { fmt.Printf("Stack definition %q not found.\n", name) os.Exit(1) } if !def.Active { fmt.Printf("Stack definition %q is already disabled.\n", name) return nil } _, err = client.UpdateStackDefinition(ctx, def.Id, &apiclient.StackDefinitionRequest{ Name: def.Name, Description: def.Description, Active: false, Scope: def.Scope, Groups: def.Groups, Zones: def.Zones, Spaces: def.Spaces, }) if err != nil { fmt.Println("Error disabling stack definition:", err) os.Exit(1) } fmt.Printf("Stack definition %q disabled.\n", name) return nil }, }
View Source
var EnableCmd = &cli.Command{ Name: "enable-def", Usage: "Enable a stack definition", Description: "Enable a stack definition so it can be used to create stacks.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "name", Usage: "Name of the stack definition to enable", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { name := cmd.GetStringArg("name") client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } def, err := client.GetStackDefinitionByName(ctx, name) if err != nil { fmt.Println("Error looking up stack definition:", err) os.Exit(1) } if def == nil { fmt.Printf("Stack definition %q not found.\n", name) os.Exit(1) } if def.Active { fmt.Printf("Stack definition %q is already enabled.\n", name) return nil } _, err = client.UpdateStackDefinition(ctx, def.Id, &apiclient.StackDefinitionRequest{ Name: def.Name, Description: def.Description, Active: true, Scope: def.Scope, Groups: def.Groups, Zones: def.Zones, Spaces: def.Spaces, }) if err != nil { fmt.Println("Error enabling stack definition:", err) os.Exit(1) } fmt.Printf("Stack definition %q enabled.\n", name) return nil }, }
View Source
var ListCmd = &cli.Command{ Name: "list", Usage: "List stacks and their status", Description: "Lists all stacks for the logged in user and the status and health of their spaces.", MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } user, err := client.WhoAmI(context.Background()) if err != nil { fmt.Println("Error getting user:", err) os.Exit(1) } spaces, _, err := client.GetSpaces(context.Background(), user.Id, false) if err != nil { fmt.Println("Error getting spaces:", err) os.Exit(1) } order := []string{} stacks := map[string][][]string{} for _, space := range spaces.Spaces { if space.Stack == "" { continue } if _, seen := stacks[space.Stack]; !seen { order = append(order, space.Stack) stacks[space.Stack] = [][]string{} } stacks[space.Stack] = append(stacks[space.Stack], []string{ fmt.Sprintf("%s (%s)", space.Name, stackSpaceStatus(space)), stackSpaceHealth(space), }) } if len(order) == 0 { fmt.Println("No stacks found.") return nil } data := [][]string{{"Stack", "Spaces", "Health"}} for _, name := range order { first := true for _, entry := range stacks[name] { if first { data = append(data, []string{name, entry[0], entry[1]}) first = false } else { data = append(data, []string{"", entry[0], entry[1]}) } } } util.PrintTable(data) return nil }, }
View Source
var ListDefsCmd = &cli.Command{ Name: "list-defs", Usage: "List stack definitions", Description: "List all stack definitions with details.", MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "details", Usage: "Show space details for each definition", }, }, Run: func(ctx context.Context, cmd *cli.Command) error { client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } list, _, err := client.GetStackDefinitions(ctx) if err != nil { fmt.Println("Error listing stack definitions:", err) os.Exit(1) } if list.Count == 0 { fmt.Println("No stack definitions found.") return nil } details := cmd.GetBool("details") if !details { data := [][]string{{"Name", "Scope", "Zones", "Spaces", "Active", "Description"}} for _, d := range list.Definitions { zones := "all" if len(d.Zones) > 0 { zones = strings.Join(d.Zones, ",") } active := "yes" if !d.Active { active = "no" } data = append(data, []string{ d.Name, d.Scope, zones, fmt.Sprintf("%d", len(d.Spaces)), active, d.Description, }) } util.PrintTable(data) return nil } for _, d := range list.Definitions { zones := "all zones" if len(d.Zones) > 0 { zones = strings.Join(d.Zones, ", ") } fmt.Printf("%s (%s, %s)", d.Name, d.Scope, zones) if d.Description != "" { fmt.Printf(" — %s", d.Description) } fmt.Println() for _, s := range d.Spaces { depends := "(none)" if len(s.DependsOn) > 0 { depends = strings.Join(s.DependsOn, ", ") } forwards := "(none)" if len(s.PortForwards) > 0 { parts := make([]string, 0, len(s.PortForwards)) for _, pf := range s.PortForwards { parts = append(parts, fmt.Sprintf("%s:%d → %s:%d", s.Name, pf.LocalPort, pf.ToSpace, pf.RemotePort)) } forwards = strings.Join(parts, ", ") } fmt.Printf(" %-6s %-20s depends: [%s] forwards: %s\n", s.Name, s.TemplateId, depends, forwards) } } return nil }, }
View Source
var RestartCmd = &cli.Command{ Name: "restart", Usage: "Restart a stack", Description: "Restart all spaces in the named stack.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "stack", Usage: "The name of the stack to restart", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { stackName := cmd.GetStringArg("stack") fmt.Println("Restarting stack: ", stackName) client, err := cmdutil.GetClient(cmd) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } _, err = client.RestartStack(context.Background(), stackName) if err != nil { return fmt.Errorf("Error restarting stack: %w", err) } fmt.Println("Stack restarted: ", stackName) return nil }, }
View Source
var StackCmd = &cli.Command{ Name: "stack", Usage: "Manage stacks", Description: "Manage your stacks from the command line.", Flags: []cli.Flag{ &cli.StringFlag{ Name: "server", Aliases: []string{"s"}, Usage: "The address of the remote server to manage stacks on.", EnvVars: []string{config.CONFIG_ENV_PREFIX + "_SERVER"}, Global: true, }, &cli.StringFlag{ Name: "token", Aliases: []string{"t"}, Usage: "The token to use for authentication.", EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TOKEN"}, Global: true, }, &cli.BoolFlag{ Name: "tls-skip-verify", Usage: "Skip TLS verification when talking to server.", ConfigPath: []string{"tls.skip_verify"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TLS_SKIP_VERIFY"}, DefaultValue: true, Global: true, }, &cli.StringFlag{ Name: "alias", Aliases: []string{"a"}, Usage: "The server alias to use.", DefaultValue: "default", Global: true, }, }, Commands: []*cli.Command{ ValidateCmd, CreateDefCmd, ApplyCmd, DeleteDefCmd, EnableCmd, DisableCmd, ListDefsCmd, ListCmd, CreateCmd, StartCmd, StopCmd, RestartCmd, DeleteCmd, }, }
View Source
var StartCmd = &cli.Command{ Name: "start", Usage: "Start a stack", Description: "Start all spaces in the named stack.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "stack", Usage: "The name of the stack to start", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { stackName := cmd.GetStringArg("stack") fmt.Println("Starting stack: ", stackName) client, err := cmdutil.GetClient(cmd) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } _, err = client.StartStack(context.Background(), stackName) if err != nil { return fmt.Errorf("Error starting stack: %w", err) } fmt.Println("Stack started: ", stackName) return nil }, }
View Source
var StopCmd = &cli.Command{ Name: "stop", Usage: "Stop a stack", Description: "Stop all spaces in the named stack.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "stack", Usage: "The name of the stack to stop", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { stackName := cmd.GetStringArg("stack") fmt.Println("Stopping stack: ", stackName) client, err := cmdutil.GetClient(cmd) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } _, err = client.StopStack(context.Background(), stackName) if err != nil { return fmt.Errorf("Error stopping stack: %w", err) } fmt.Println("Stack stopped: ", stackName) return nil }, }
View Source
var ValidateCmd = &cli.Command{ Name: "validate", Usage: "Validate a stack definition file", Description: "Validate a stack definition TOML or JSON file without creating it.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "file", Usage: "Path to the TOML or JSON definition file", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { client, err := cmdutil.GetClient(cmd) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } req, err := loadStackDef(ctx, cmd.GetStringArg("file"), client) if err != nil { fmt.Println("Error reading definition:", err) os.Exit(1) } result, _, err := client.ValidateStackDefinition(ctx, req) if err != nil { fmt.Println("Error validating definition:", err) os.Exit(1) } if result.Valid { fmt.Println("Stack definition is valid.") return nil } fmt.Printf("Stack definition has %d error(s):\n", len(result.Errors)) for _, e := range result.Errors { if e.Space != "" { fmt.Printf(" [%s] %s: %s\n", e.Space, e.Field, e.Message) } else { fmt.Printf(" %s: %s\n", e.Field, e.Message) } } os.Exit(1) return nil }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.