Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var AcquireCmd = &cli.Command{ Name: "acquire", Usage: "Acquire a pool member exclusively", Description: "Check out one member of a pool for exclusive use until the lease expires (or is released). Method calls and pool-name port routing skip the member while held; reach it by its own space name or by pinning calls with its space id.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "pool", Usage: "The name or ID of the pool", Required: true, }, }, Flags: []cli.Flag{ &cli.StringFlag{ Name: "time", Usage: "How long to hold the member, e.g. 90s, 5m, 2h; 'none' for a lease that never expires (unlimited pools). Defaults to the pool's configured maximum.", }, &cli.StringFlag{ Name: "wait", Usage: "How long to wait for a member if none is free yet (e.g. while one is being replaced or starting), e.g. 30s, 2m (max 5m). Defaults to 10s; use --wait 0s to fail immediately.", }, leaseJSONFlag(), }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { poolName := cmd.GetStringArg("pool") duration, err := parseLeaseDuration(cmd.GetString("time")) if err != nil { return err } wait := 10 if value := cmd.GetString("wait"); value != "" { wait, err = parseLeaseDuration(value) if err != nil { return fmt.Errorf("invalid --wait: %w", err) } } client, err := cmdutil.GetClient(cmd) if err != nil { return err } if !cmd.GetBool("json") { fmt.Printf("Acquiring a member of pool %q...\n", poolName) } lease, code, err := client.AcquirePoolLease(context.Background(), poolName, &apiclient.PoolLeaseAcquireRequest{ DurationSeconds: duration, WaitSeconds: wait, }) if err != nil { return handleLeaseError(cmd, code, err, fmt.Sprintf("no free member available in pool %q", poolName)) } if cmd.GetBool("json") { return printLeaseJSON(lease) } printLease(lease, "acquired") return nil }, }
View Source
var CreateCmd = &cli.Command{ Name: "create", Usage: "Create a pool", Description: "Create a pool of identical spaces from a template. Set --lease-time to enable exclusive member leases (acquire / release).", Arguments: []cli.Argument{ &cli.StringArg{ Name: "pool", Usage: "The name of the new pool", Required: true, }, &cli.StringArg{ Name: "template", Usage: "The name or ID of the template members are created from", Required: true, }, }, Flags: []cli.Flag{ &cli.IntFlag{ Name: "count", Usage: "The number of spaces to keep in the pool.", DefaultValue: 1, }, &cli.BoolFlag{ Name: "start", Usage: "Start the pool immediately.", }, &cli.StringFlag{ Name: "lease-time", Usage: "Enable exclusive member leases: the maximum lease length, e.g. 30m, 2h; 'none' for leases that last until released. Leases are disabled when not set.", }, &cli.StringFlag{ Name: "lease-extensions", Usage: "How many times a lease may be extended: a number, or 'unlimited'. Defaults to 0 (no extensions).", }, &cli.StringSliceFlag{ Name: "custom-field", Usage: "Custom field given to every member, as name=value (can be specified multiple times).", }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { poolName := cmd.GetStringArg("pool") templateName := cmd.GetStringArg("template") count := cmd.GetInt("count") if count < 1 { return fmt.Errorf("count must be at least 1") } leaseTime, err := parseLeaseDuration(cmd.GetString("lease-time")) if err != nil { return fmt.Errorf("invalid --lease-time: %w", err) } leaseExtensions := 0 switch value := cmd.GetString("lease-extensions"); value { case "": case "unlimited": leaseExtensions = -1 default: leaseExtensions, err = strconv.Atoi(value) if err != nil || leaseExtensions < -1 { return fmt.Errorf("invalid --lease-extensions %q (use a number or unlimited)", value) } } if leaseTime == 0 && leaseExtensions != 0 { return fmt.Errorf("--lease-extensions needs --lease-time to enable leases") } customFields, err := cmdutil.ParseCustomFields(cmd.GetStringSlice("custom-field")) if err != nil { return err } client, err := cmdutil.GetClient(cmd) if err != nil { return err } templateId := templateName if template, err := client.GetTemplateByName(context.Background(), templateName); err == nil && template != nil { templateId = template.TemplateId } fmt.Printf("Creating pool %q from template %q...\n", poolName, templateName) response, _, err := client.CreatePool(context.Background(), &apiclient.PoolRequest{ Name: poolName, TemplateId: templateId, DesiredCount: count, Active: cmd.GetBool("start"), LeaseMaxTime: leaseTime, LeaseMaxExtensions: leaseExtensions, CustomFields: customFields, }) if err != nil { return fmt.Errorf("couldn't create pool %q: %s", poolName, cmdutil.CleanAPIError(err)) } state := "created stopped" if cmd.GetBool("start") { state = "started" } fmt.Printf("Pool %q %s with %d space(s), ID: %s\n", poolName, state, count, response.Id) if leaseTime != 0 { fmt.Println("Leases enabled: use 'knot pool acquire " + poolName + "' to check out a member.") } return nil }, }
View Source
var DeleteCmd = &cli.Command{ Name: "delete", Usage: "Delete a pool", Description: "Delete a stopped pool and all its member spaces.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "pool", Usage: "The name or ID of the pool to delete", Required: true, }, }, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "yes", Aliases: []string{"y"}, Usage: "Skip confirmation prompt.", }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { poolName := cmd.GetStringArg("pool") if !cmd.GetBool("yes") { fmt.Printf("Delete pool %q and all its spaces? [y/N] ", poolName) var resp string fmt.Scanln(&resp) if resp != "y" && resp != "Y" && resp != "yes" { fmt.Println("Cancelled.") return nil } } client, err := cmdutil.GetClient(cmd) if err != nil { return err } _, err = client.DeletePool(context.Background(), poolName) if err != nil { return fmt.Errorf("couldn't delete pool %q: %w", poolName, err) } fmt.Fprintln(os.Stderr, "Pool deleted:", poolName) return nil }, }
View Source
var ExtendCmd = &cli.Command{ Name: "extend", Usage: "Extend the lease on a pool member", Description: "Renew a lease: the new deadline becomes now + the given duration (or never, on unlimited pools). Bounded by the pool's maximum extension count.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The held member's name or space id — the same identifier acquire returned", Required: true, }, }, Flags: []cli.Flag{ &cli.StringFlag{ Name: "time", Usage: "How long to hold the member from now, e.g. 90s, 5m, 2h; 'none' for no expiry (unlimited pools). Defaults to the pool's configured maximum.", }, leaseJSONFlag(), }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { spaceArg := cmd.GetStringArg("space") duration, err := parseLeaseDuration(cmd.GetString("time")) if err != nil { return err } client, err := cmdutil.GetClient(cmd) if err != nil { return err } lease, code, err := client.ExtendSpaceLease(context.Background(), spaceArg, &apiclient.PoolLeaseExtendRequest{ DurationSeconds: duration, }) if err != nil { return handleLeaseError(cmd, code, err, fmt.Sprintf("lease on %s cannot be extended (limit reached or already ended)", spaceArg)) } if cmd.GetBool("json") { return printLeaseJSON(lease) } printLease(lease, "extended") return nil }, }
View Source
var LeasesCmd = &cli.Command{ Name: "leases", Usage: "List a pool's leases", Description: "Lists the pool's exclusively held leases — active plus draining (ended, waiting for in-flight work to finish).", Arguments: []cli.Argument{ &cli.StringArg{ Name: "pool", Usage: "The name or ID of the pool", Required: true, }, }, Flags: []cli.Flag{ leaseJSONFlag(), }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { poolName := cmd.GetStringArg("pool") client, err := cmdutil.GetClient(cmd) if err != nil { return err } leases, code, err := client.GetPoolLeases(context.Background(), poolName) if err != nil { return handleLeaseError(cmd, code, err, "") } if cmd.GetBool("json") { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(leases) } if len(leases.Leases) == 0 { fmt.Printf("No leases held on pool %q.\n", poolName) return nil } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "MEMBER\tSTATE\tEXPIRES\tEXTENSIONS\tHOLDER") for _, lease := range leases.Leases { fmt.Fprintf(w, "%s\t%s\t%s\t%d/%s\t%s\n", lease.SpaceName, lease.State, formatLeaseExpiry(lease.ExpiresAt), lease.ExtensionsUsed, formatMaxExtensions(lease.MaxExtensions), lease.Username, ) } w.Flush() return nil }, }
View Source
var ListCmd = &cli.Command{ Name: "list", Usage: "List your pools", Description: "Lists all pools you own with their status and member count.", MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { client, err := cmdutil.GetClient(cmd) if err != nil { return err } pools, code, err := client.GetPools(context.Background()) if err != nil { if code == 404 { fmt.Println("No pools found.") return nil } return fmt.Errorf("couldn't list pools: %w", err) } if len(pools.Pools) == 0 { fmt.Println("No pools found.") return nil } templateNames := map[string]string{} if templates, _, err := client.GetTemplates(context.Background()); err == nil && templates != nil { for _, t := range templates.Templates { templateNames[t.Id] = t.Name } } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "NAME\tSTATE\tALIVE/DESIRED\tTEMPLATE") for _, p := range pools.Pools { state := "stopped" if p.Active { state = "active" } template := p.TemplateId if name, ok := templateNames[p.TemplateId]; ok { template = name } fmt.Fprintf(w, "%s\t%s\t%d/%d\t%s\n", p.Name, state, p.AliveMembers, p.DesiredCount, template) } w.Flush() return nil }, }
View Source
var PoolCmd = &cli.Command{ Name: "pool", Usage: "Manage space pools", Description: "Manage your space pools from the command line.", Flags: []cli.Flag{ &cli.StringFlag{ Name: "server", Aliases: []string{"s"}, Usage: "The address of the remote server to manage pools 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{ ListCmd, CreateCmd, StartCmd, StopCmd, DeleteCmd, SetSizeCmd, UpdateCmd, AcquireCmd, ExtendCmd, ReleaseCmd, LeasesCmd, }, }
View Source
var ReleaseCmd = &cli.Command{ Name: "release", Usage: "Release a leased pool member", Description: "End the lease held on a pool member. The member returns to the pool once any in-flight work has finished (normally within one sweep, ~15s). With --destroy the member is deleted instead and a fresh replacement is created, so the next acquire gets a clean space.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The held member's name or space id — the same identifier acquire returned", Required: true, }, }, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "destroy", Usage: "Destroy the member instead of returning it — a fresh replacement is created, so the next acquire gets a clean space.", }, leaseJSONFlag(), }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { spaceArg := cmd.GetStringArg("space") destroy := cmd.GetBool("destroy") client, err := cmdutil.GetClient(cmd) if err != nil { return err } lease, code, err := client.ReleaseSpaceLease(context.Background(), spaceArg, destroy) if err != nil { return handleLeaseError(cmd, code, err, "") } if cmd.GetBool("json") { return printLeaseJSON(lease) } if destroy { fmt.Printf("Member %s destroyed; a fresh replacement is starting in pool %q.\n", spaceArg, lease.PoolName) } else { fmt.Printf("Member %s released; it returns to pool %q after in-flight work drains.\n", spaceArg, lease.PoolName) } return nil }, }
View Source
var SetSizeCmd = &cli.Command{ Name: "set-size", Usage: "Set the desired space count for a pool", Description: "Update the target number of spaces for a pool. The sweep loop handles creating or removing members.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "pool", Usage: "The name or ID of the pool", Required: true, }, &cli.IntArg{ Name: "count", Usage: "The desired number of spaces (minimum 1)", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { poolName := cmd.GetStringArg("pool") desired := cmd.GetIntArg("count") if desired < 1 { return fmt.Errorf("count must be at least 1") } fmt.Printf("Setting pool %q size to %d...\n", poolName, desired) client, err := cmdutil.GetClient(cmd) if err != nil { return err } _, err = client.SetPoolSize(context.Background(), poolName, desired) if err != nil { return fmt.Errorf("couldn't set the size of pool %q: %w", poolName, err) } fmt.Printf("Pool %q size set to %d.\n", poolName, desired) return nil }, }
View Source
var StartCmd = &cli.Command{ Name: "start", Usage: "Start a pool", Description: "Start a stopped pool and all its member spaces.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "pool", Usage: "The name or ID of the pool to start", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { poolName := cmd.GetStringArg("pool") fmt.Println("Starting pool:", poolName) client, err := cmdutil.GetClient(cmd) if err != nil { return err } _, err = client.StartPool(context.Background(), poolName) if err != nil { return fmt.Errorf("couldn't start pool %q: %w", poolName, err) } fmt.Println("Pool started:", poolName) return nil }, }
View Source
var StopCmd = &cli.Command{ Name: "stop", Usage: "Stop a pool", Description: "Stop a running pool and all its member spaces without deleting them.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "pool", Usage: "The name or ID of the pool to stop", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { poolName := cmd.GetStringArg("pool") fmt.Println("Stopping pool:", poolName) client, err := cmdutil.GetClient(cmd) if err != nil { return err } _, err = client.StopPool(context.Background(), poolName) if err != nil { return fmt.Errorf("couldn't stop pool %q: %w", poolName, err) } fmt.Println("Pool stopped:", poolName) return nil }, }
View Source
var UpdateCmd = &cli.Command{ Name: "update", Usage: "Update the custom fields given to pool members", Description: `Set or remove the custom field values given to every member of a pool. Members pick up the change the next time they are created or started; restart the pool (stop, then start) to apply it to all of them. With no flags, shows the pool's current custom field values.`, Arguments: []cli.Argument{ &cli.StringArg{ Name: "pool", Usage: "The name or ID of the pool", Required: true, }, }, Flags: []cli.Flag{ &cli.StringSliceFlag{ Name: "custom-field", Usage: "Set a custom field as name=value (can be specified multiple times).", }, &cli.StringSliceFlag{ Name: "unset-custom-field", Usage: "Remove a custom field, so members get the template default (can be specified multiple times).", }, &cli.BoolFlag{ Name: "clear-custom-fields", Usage: "Remove every custom field before applying --custom-field.", }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { poolName := cmd.GetStringArg("pool") set, err := cmdutil.ParseCustomFields(cmd.GetStringSlice("custom-field")) if err != nil { return err } unset := cmd.GetStringSlice("unset-custom-field") clearAll := cmd.GetBool("clear-custom-fields") client, err := cmdutil.GetClient(cmd) if err != nil { return err } pool, _, err := client.GetPool(context.Background(), poolName) if err != nil { return fmt.Errorf("couldn't get pool %q: %s", poolName, cmdutil.CleanAPIError(err)) } masked := map[string]bool{} if template, _, err := client.GetTemplate(context.Background(), pool.TemplateId); err == nil && template != nil { for _, field := range template.CustomFields { if field.Type == "masked" { masked[field.Name] = true } } } if len(set) == 0 && len(unset) == 0 && !clearAll { printPoolCustomFields(pool.CustomFields, masked) return nil } fields := []apiclient.CustomFieldValue{} if !clearAll { for _, field := range pool.CustomFields { if !slices.Contains(unset, field.Name) { fields = append(fields, field) } } } for _, field := range set { i := slices.IndexFunc(fields, func(f apiclient.CustomFieldValue) bool { return f.Name == field.Name }) if i >= 0 { fields[i].Value = field.Value } else { fields = append(fields, field) } } if _, err := client.PatchPool(context.Background(), poolName, &apiclient.PoolUpdateRequest{CustomFields: &fields}); err != nil { return fmt.Errorf("couldn't update pool %q: %s", poolName, cmdutil.CleanAPIError(err)) } fmt.Printf("Pool %q updated; members pick up the change when they are next created or started.\n", poolName) printPoolCustomFields(fields, masked) return nil }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.