From b84001c07ddba300d8b9b997356f2fe437e5ee4f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 10 Jun 2022 09:05:43 -0400 Subject: [PATCH] Add CLI for workers (#2164) --- api/workers/option.gen.go | 13 + api/workers/worker.gen.go | 21 ++ internal/api/genapi/input.go | 9 +- internal/cmd/commands.go | 38 +++ .../cmd/commands/accountscmd/accounts.gen.go | 5 +- .../authmethodscmd/authmethods.gen.go | 5 +- .../commands/authtokenscmd/authtokens.gen.go | 6 + .../credentiallibraries.gen.go | 5 +- .../credentialstores.gen.go | 5 +- .../hostcatalogscmd/hostcatalogs.gen.go | 5 +- internal/cmd/commands/hostscmd/hosts.gen.go | 5 +- .../cmd/commands/hostsetscmd/hostsets.gen.go | 5 +- .../managedgroupscmd/managedgroups.gen.go | 5 +- internal/cmd/commands/server/server.go | 12 +- .../server/worker_tags_reload_test.go | 12 +- .../cmd/commands/sessionscmd/sessions.gen.go | 6 + .../cmd/commands/targetscmd/targets.gen.go | 5 +- internal/cmd/commands/workerscmd/funcs.go | 215 ++++++++++++ .../commands/workerscmd/worker-led_funcs.go | 63 ++++ .../workerscmd/worker-led_workers.gen.go | 244 ++++++++++++++ .../cmd/commands/workerscmd/workers.gen.go | 314 ++++++++++++++++++ internal/cmd/gencli/input.go | 44 ++- internal/cmd/gencli/templates.go | 10 +- 23 files changed, 1019 insertions(+), 33 deletions(-) create mode 100644 internal/cmd/commands/workerscmd/funcs.go create mode 100644 internal/cmd/commands/workerscmd/worker-led_funcs.go create mode 100644 internal/cmd/commands/workerscmd/worker-led_workers.gen.go create mode 100644 internal/cmd/commands/workerscmd/workers.gen.go diff --git a/api/workers/option.gen.go b/api/workers/option.gen.go index 22be13b4b5..29ba168a30 100644 --- a/api/workers/option.gen.go +++ b/api/workers/option.gen.go @@ -1,6 +1,7 @@ package workers import ( + "strconv" "strings" "github.com/hashicorp/boundary/api" @@ -21,6 +22,7 @@ type options struct { withAutomaticVersioning bool withSkipCurlOutput bool withFilter string + withRecursive bool } func getDefaultOptions() options { @@ -44,6 +46,9 @@ func getOpts(opt ...Option) (options, []api.Option) { if opts.withFilter != "" { opts.queryMap["filter"] = opts.withFilter } + if opts.withRecursive { + opts.queryMap["recursive"] = strconv.FormatBool(opts.withRecursive) + } return opts, apiOpts } @@ -74,6 +79,14 @@ func WithFilter(filter string) Option { } } +// WithRecursive tells the API to use recursion for listing operations on this +// resource +func WithRecursive(recurse bool) Option { + return func(o *options) { + o.withRecursive = true + } +} + func WithAddress(inAddress string) Option { return func(o *options) { o.postMap["address"] = inAddress diff --git a/api/workers/worker.gen.go b/api/workers/worker.gen.go index 7064228a37..f108a31914 100644 --- a/api/workers/worker.gen.go +++ b/api/workers/worker.gen.go @@ -3,6 +3,7 @@ package workers import ( "context" + "errors" "fmt" "net/url" "time" @@ -193,6 +194,26 @@ func (c *Client) Update(ctx context.Context, id string, version uint32, opt ...O opts, apiOpts := getOpts(opt...) + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, errors.New("zero version number passed into Update request and automatic versioning not specified") + } + existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...) + if existingErr != nil { + if api.AsServerError(existingErr) != nil { + return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) + } + return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingTarget == nil { + return nil, errors.New("nil resource response found when performing initial check-and-set read") + } + if existingTarget.Item == nil { + return nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Item.Version + } + opts.postMap["version"] = version req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("workers/%s", url.PathEscape(id)), opts.postMap, apiOpts...) diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 46d90b06a5..52032262db 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -738,6 +738,10 @@ var inputStructs = []*structInfo{ fieldFilter: []string{"private_key"}, recursiveListing: true, }, + { + inProto: &workers.WorkerProvidedConfiguration{}, + outFile: "workers/worker_provided_configuration.gen.go", + }, { inProto: &workers.Worker{}, outFile: "workers/worker.gen.go", @@ -769,9 +773,6 @@ var inputStructs = []*structInfo{ pluralResourceName: "workers", createResponseTypes: true, recursiveListing: true, - }, - { - inProto: &workers.WorkerProvidedConfiguration{}, - outFile: "workers/worker_provided_configuration.gen.go", + versionEnabled: true, }, } diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index eb54312c5b..466a027520 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/commands/targetscmd" "github.com/hashicorp/boundary/internal/cmd/commands/userscmd" "github.com/hashicorp/boundary/internal/cmd/commands/version" + "github.com/hashicorp/boundary/internal/cmd/commands/workerscmd" "github.com/mitchellh/cli" ) @@ -1028,5 +1029,42 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "remove-accounts", }, nil }, + + "workers create": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "workers create worker-led": func() (cli.Command, error) { + return &workerscmd.WorkerLedCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "workers read": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "workers update": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "workers delete": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "workers list": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, } } diff --git a/internal/cmd/commands/accountscmd/accounts.gen.go b/internal/cmd/commands/accountscmd/accounts.gen.go index 3e1ee17684..c350ab2cd9 100644 --- a/internal/cmd/commands/accountscmd/accounts.gen.go +++ b/internal/cmd/commands/accountscmd/accounts.gen.go @@ -117,7 +117,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/authmethodscmd/authmethods.gen.go b/internal/cmd/commands/authmethodscmd/authmethods.gen.go index 967da8645b..6eec33d34f 100644 --- a/internal/cmd/commands/authmethodscmd/authmethods.gen.go +++ b/internal/cmd/commands/authmethodscmd/authmethods.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/authtokenscmd/authtokens.gen.go b/internal/cmd/commands/authtokenscmd/authtokens.gen.go index 2cf42c934d..e7454237ad 100644 --- a/internal/cmd/commands/authtokenscmd/authtokens.gen.go +++ b/internal/cmd/commands/authtokenscmd/authtokens.gen.go @@ -115,6 +115,12 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp + case "create": + return cli.RunResultHelp + + case "update": + return cli.RunResultHelp + } c.plural = "auth token" diff --git a/internal/cmd/commands/credentiallibrariescmd/credentiallibraries.gen.go b/internal/cmd/commands/credentiallibrariescmd/credentiallibraries.gen.go index 2a8d809e06..e83a1c9d33 100644 --- a/internal/cmd/commands/credentiallibrariescmd/credentiallibraries.gen.go +++ b/internal/cmd/commands/credentiallibrariescmd/credentiallibraries.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/credentialstorescmd/credentialstores.gen.go b/internal/cmd/commands/credentialstorescmd/credentialstores.gen.go index 162d883110..54366424a8 100644 --- a/internal/cmd/commands/credentialstorescmd/credentialstores.gen.go +++ b/internal/cmd/commands/credentialstorescmd/credentialstores.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/hostcatalogscmd/hostcatalogs.gen.go b/internal/cmd/commands/hostcatalogscmd/hostcatalogs.gen.go index 439956b522..2dbe07472c 100644 --- a/internal/cmd/commands/hostcatalogscmd/hostcatalogs.gen.go +++ b/internal/cmd/commands/hostcatalogscmd/hostcatalogs.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/hostscmd/hosts.gen.go b/internal/cmd/commands/hostscmd/hosts.gen.go index 3212c337c2..4316dba34e 100644 --- a/internal/cmd/commands/hostscmd/hosts.gen.go +++ b/internal/cmd/commands/hostscmd/hosts.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/hostsetscmd/hostsets.gen.go b/internal/cmd/commands/hostsetscmd/hostsets.gen.go index fc4bd76c0c..5df30d0bef 100644 --- a/internal/cmd/commands/hostsetscmd/hostsets.gen.go +++ b/internal/cmd/commands/hostsetscmd/hostsets.gen.go @@ -117,7 +117,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/managedgroupscmd/managedgroups.gen.go b/internal/cmd/commands/managedgroupscmd/managedgroups.gen.go index 913dfcac0c..0a2656650c 100644 --- a/internal/cmd/commands/managedgroupscmd/managedgroups.gen.go +++ b/internal/cmd/commands/managedgroupscmd/managedgroups.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index ef519e8823..848d21e35d 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -538,9 +538,15 @@ func (c *Command) ParseFlagsAndConfig(args []string) int { c.UI.Error("Controller has no name set. It must be the unique name of this instance.") return base.CommandUserError } - if c.Config.Worker != nil && c.Config.Worker.Name == "" { - c.UI.Error("Worker has no name set. It must be the unique name of this instance.") - return base.CommandUserError + if c.Config.Worker != nil { + if c.Config.Worker.Name == "" { + c.UI.Error("Worker has no name set. It must be the unique name of this instance.") + return base.CommandUserError + } + if c.Config.Worker.AuthStoragePath == "" { + c.UI.Error("No worker auth KMS specified and no worker auth storage path specified.") + return base.CommandUserError + } } return base.CommandSuccess diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index 5dce67d62c..01114b811b 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -14,6 +14,8 @@ import ( "crypto/rand" "encoding/base64" "fmt" + "os" + "path/filepath" "sync" "testing" "time" @@ -50,6 +52,7 @@ worker { tags { type = ["dev", "local"] } + auth_storage_path = "%s" } ` @@ -61,6 +64,7 @@ worker { tags { foo = ["bar", "baz"] } + auth_storage_path = "%s" } ` @@ -74,10 +78,14 @@ func TestServer_ReloadWorkerTags(t *testing.T) { testController := controller.NewTestController(t, controller.WithWorkerAuthKms(workerAuthWrapper), controller.WithRootKms(rootWrapper), controller.WithRecoveryKms(recoveryWrapper)) defer testController.Shutdown() + authStoragePath, err := os.MkdirTemp("", "") + require.NoError(err) + t.Cleanup(func() { os.RemoveAll(authStoragePath) }) + wg := &sync.WaitGroup{} cmd := testServerCommand(t, testServerCommandOpts{}) - cmd.presetConfig = atomic.NewString(fmt.Sprintf(workerBaseConfig+tag1Config, key, testController.ClusterAddrs()[0])) + cmd.presetConfig = atomic.NewString(fmt.Sprintf(workerBaseConfig+tag1Config, key, testController.ClusterAddrs()[0], filepath.Join(authStoragePath, "tag1"))) wg.Add(1) go func() { @@ -110,7 +118,7 @@ func TestServer_ReloadWorkerTags(t *testing.T) { time.Sleep(10 * time.Second) fetchWorkerTags("test", "type", []string{"dev", "local"}) - cmd.presetConfig.Store(fmt.Sprintf(workerBaseConfig+tag2Config, key, testController.ClusterAddrs()[0])) + cmd.presetConfig.Store(fmt.Sprintf(workerBaseConfig+tag2Config, key, testController.ClusterAddrs()[0], filepath.Join(authStoragePath, "tag2"))) cmd.SighupCh <- struct{}{} select { diff --git a/internal/cmd/commands/sessionscmd/sessions.gen.go b/internal/cmd/commands/sessionscmd/sessions.gen.go index 372fec9d6e..550b4246bf 100644 --- a/internal/cmd/commands/sessionscmd/sessions.gen.go +++ b/internal/cmd/commands/sessionscmd/sessions.gen.go @@ -110,6 +110,12 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp + case "create": + return cli.RunResultHelp + + case "update": + return cli.RunResultHelp + } c.plural = "session" diff --git a/internal/cmd/commands/targetscmd/targets.gen.go b/internal/cmd/commands/targetscmd/targets.gen.go index 10e00939d0..c4ac75fe68 100644 --- a/internal/cmd/commands/targetscmd/targets.gen.go +++ b/internal/cmd/commands/targetscmd/targets.gen.go @@ -123,7 +123,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/workerscmd/funcs.go b/internal/cmd/commands/workerscmd/funcs.go new file mode 100644 index 0000000000..1e959eb3a9 --- /dev/null +++ b/internal/cmd/commands/workerscmd/funcs.go @@ -0,0 +1,215 @@ +package workerscmd + +import ( + "fmt" + "time" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func (c *Command) printListTable(items []*workers.Worker) string { + if len(items) == 0 { + return "No workers found" + } + + var output []string + output = []string{ + "", + "Worker information:", + } + for i, item := range items { + if i > 0 { + output = append(output, "") + } + if item.Id != "" { + output = append(output, + fmt.Sprintf(" ID: %s", item.Id), + ) + } else { + output = append(output, + fmt.Sprintf(" ID: %s", "(not available)"), + ) + } + if c.FlagRecursive && item.ScopeId != "" { + output = append(output, + fmt.Sprintf(" Scope ID: %s", item.ScopeId), + ) + } + if item.Version > 0 { + output = append(output, + fmt.Sprintf(" Version: %d", item.Version), + ) + } + if item.Name != "" { + output = append(output, + fmt.Sprintf(" Name: %s", item.Name), + ) + } + if item.Description != "" { + output = append(output, + fmt.Sprintf(" Description: %s", item.Description), + ) + } + if item.CanonicalAddress != "" { + output = append(output, + fmt.Sprintf(" Canonical Address: %s", item.CanonicalAddress), + ) + } + if !item.LastStatusTime.IsZero() { + output = append(output, + fmt.Sprintf(" Last Status Time: %s", item.LastStatusTime.Format(time.RFC1123)), + ) + } + if true { + output = append(output, + fmt.Sprintf(" Active Connection Count: %d", item.ActiveConnectionCount), + ) + } + + if len(item.AuthorizedActions) > 0 { + output = append(output, + " Authorized Actions:", + base.WrapSlice(6, item.AuthorizedActions), + ) + } + } + + return base.WrapForHelpText(output) +} + +func printItemTable(result api.GenericResult) string { + item := result.GetItem().(*workers.Worker) + nonAttributeMap := map[string]interface{}{} + if item.Id != "" { + nonAttributeMap["ID"] = item.Id + } + if item.Version != 0 { + nonAttributeMap["Version"] = item.Version + } + if !item.CreatedTime.IsZero() { + nonAttributeMap["Created Time"] = item.CreatedTime.Local().Format(time.RFC1123) + } + if !item.UpdatedTime.IsZero() { + nonAttributeMap["Updated Time"] = item.UpdatedTime.Local().Format(time.RFC1123) + } + if item.Name != "" { + nonAttributeMap["Name"] = item.Name + } + if item.Description != "" { + nonAttributeMap["Description"] = item.Description + } + if !item.LastStatusTime.IsZero() { + nonAttributeMap["Last Status Time"] = item.LastStatusTime + } + nonAttributeMap["Active Connection Count"] = item.ActiveConnectionCount + + maxLength := base.MaxAttributesLength(nonAttributeMap, nil, nil) + + ret := []string{ + "", + "Worker information:", + base.WrapMap(2, maxLength+2, nonAttributeMap), + } + + if item.Scope != nil { + ret = append(ret, + "", + " Scope:", + base.ScopeInfoForOutput(item.Scope, maxLength), + ) + } + + var workerProvidedTags map[string][]string + var workerProvidedAddress string + if item.WorkerProvidedConfiguration != nil { + config := item.WorkerProvidedConfiguration + configMap := make(map[string]any) + if config.Address != "" { + configMap["Address"] = config.Address + workerProvidedAddress = config.Address + } + if config.Name != "" { + configMap["Name"] = config.Name + } + ret = append(ret, + "", + " Worker-Provided Configuration:", + base.WrapMap(4, maxLength, configMap), + ) + workerProvidedTags = config.Tags + } + + if len(item.Address) > 0 || len(item.CanonicalAddress) > 0 || len(workerProvidedAddress) > 0 { + ret = append(ret, + "", + " Address:", + ) + if len(item.Address) > 0 { + ret = append(ret, + " Item (via API):", + " "+item.Address, + ) + } + if len(workerProvidedAddress) > 0 { + ret = append(ret, + " Worker Configuration:", + " "+workerProvidedAddress, + ) + } + if len(item.CanonicalAddress) > 0 { + ret = append(ret, + " Canonical:", + " "+item.CanonicalAddress, + ) + } + } + + if len(item.Tags) > 0 || len(item.CanonicalTags) > 0 || len(workerProvidedTags) > 0 { + ret = append(ret, + "", + " Tags:", + ) + if len(item.Tags) > 0 { + tagMap := make(map[string]any, len(item.Tags)) + for k, v := range item.Tags { + tagMap[k] = v + } + ret = append(ret, + " Item (via API):", + base.WrapMap(6, 2, tagMap), + ) + } + if len(workerProvidedTags) > 0 { + tagMap := make(map[string]any, len(workerProvidedTags)) + for k, v := range workerProvidedTags { + tagMap[k] = v + } + ret = append(ret, + " Worker Configuration:", + base.WrapMap(6, 2, tagMap), + ) + } + if len(item.CanonicalTags) > 0 { + tagMap := make(map[string]any, len(item.CanonicalTags)) + for k, v := range item.CanonicalTags { + tagMap[k] = v + } + ret = append(ret, + " Canonical:", + base.WrapMap(6, 2, tagMap), + ) + } + } + + if len(item.AuthorizedActions) > 0 { + ret = append(ret, + "", + " Authorized Actions:", + base.WrapSlice(4, item.AuthorizedActions), + ) + } + + return base.WrapForHelpText(ret) +} diff --git a/internal/cmd/commands/workerscmd/worker-led_funcs.go b/internal/cmd/commands/workerscmd/worker-led_funcs.go new file mode 100644 index 0000000000..5c07dda6a6 --- /dev/null +++ b/internal/cmd/commands/workerscmd/worker-led_funcs.go @@ -0,0 +1,63 @@ +package workerscmd + +import ( + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func init() { + extraWorkerLedActionsFlagsMapFunc = extraWorkerLedActionsFlagsMapFuncImpl + extraWorkerLedFlagsFunc = extraWorkerLedFlagsFuncImpl + executeExtraWorkerLedActions = executeExtraWorkerLedActionsImpl +} + +type extraWorkerLedCmdVars struct { + flagWorkerGeneratedAuthToken string +} + +func extraWorkerLedActionsFlagsMapFuncImpl() map[string][]string { + return map[string][]string{ + "create": {"worker-generated-auth-token"}, + } +} + +func (c *WorkerLedCommand) extraWorkerLedHelpFunc(helpMap map[string]func() string) string { + var helpStr string + switch c.Func { + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary workers create worker-led [options] [args]", + "", + " Create a worker using the worker-led approach by providing an auth token from the worker. Example:", + "", + ` $ boundary workers create worker-led -name us-east-1-1 -worker-generated-auth-token "`, + "", + "", + }) + } + return helpStr + c.Flags().Help() +} + +func extraWorkerLedFlagsFuncImpl(c *WorkerLedCommand, set *base.FlagSets, f *base.FlagSet) { + f = set.NewFlagSet("Worker Creation Options") + + for _, name := range flagsWorkerLedMap[c.Func] { + switch name { + case "worker-generated-auth-token": + f.StringVar(&base.StringVar{ + Name: "worker-generated-auth-token", + Target: &c.flagWorkerGeneratedAuthToken, + Usage: "The auth token provided by the worker to use to register it to Boundary", + }) + } + } +} + +func executeExtraWorkerLedActionsImpl(c *WorkerLedCommand, origResult api.GenericResult, origError error, workerClient *workers.Client, version uint32, opts []workers.Option) (api.GenericResult, error) { + switch c.Func { + case "create": + return workerClient.CreateWorkerLed(c.Context, c.flagWorkerGeneratedAuthToken, c.FlagScopeId, opts...) + } + return origResult, origError +} diff --git a/internal/cmd/commands/workerscmd/worker-led_workers.gen.go b/internal/cmd/commands/workerscmd/worker-led_workers.gen.go new file mode 100644 index 0000000000..caa2c17e9d --- /dev/null +++ b/internal/cmd/commands/workerscmd/worker-led_workers.gen.go @@ -0,0 +1,244 @@ +// Code generated by "make cli"; DO NOT EDIT. +package workerscmd + +import ( + "errors" + "fmt" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initWorkerLedFlags() { + flagsOnce.Do(func() { + extraFlags := extraWorkerLedActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsWorkerLedMap[k] = append(flagsWorkerLedMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*WorkerLedCommand)(nil) + _ cli.CommandAutocomplete = (*WorkerLedCommand)(nil) +) + +type WorkerLedCommand struct { + *base.Command + + Func string + + plural string + + extraWorkerLedCmdVars +} + +func (c *WorkerLedCommand) AutocompleteArgs() complete.Predictor { + initWorkerLedFlags() + return complete.PredictAnything +} + +func (c *WorkerLedCommand) AutocompleteFlags() complete.Flags { + initWorkerLedFlags() + return c.Flags().Completions() +} + +func (c *WorkerLedCommand) Synopsis() string { + if extra := extraWorkerLedSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "worker" + + synopsisStr = fmt.Sprintf("%s %s", "worker-led-type", synopsisStr) + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *WorkerLedCommand) Help() string { + initWorkerLedFlags() + + var helpStr string + helpMap := common.HelpMap("worker") + + switch c.Func { + + default: + + helpStr = c.extraWorkerLedHelpFunc(helpMap) + + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsWorkerLedMap = map[string][]string{ + + "create": {"scope-id", "name", "description"}, +} + +func (c *WorkerLedCommand) Flags() *base.FlagSets { + if len(flagsWorkerLedMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "worker-led-type worker", flagsWorkerLedMap, c.Func) + + extraWorkerLedFlagsFunc(c, set, f) + + return set +} + +func (c *WorkerLedCommand) Run(args []string) int { + initWorkerLedFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + + case "update": + return cli.RunResultHelp + + } + + c.plural = "worker-led-type worker" + switch c.Func { + case "list": + c.plural = "worker-led-type workers" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsWorkerLedMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []workers.Option + + if strutil.StrListContains(flagsWorkerLedMap[c.Func], "scope-id") { + switch c.Func { + + case "create": + if c.FlagScopeId == "" { + c.PrintCliError(errors.New("Scope ID must be passed in via -scope-id or BOUNDARY_SCOPE_ID")) + return base.CommandUserError + } + + } + } + + client, err := c.Client() + if c.WrapperCleanupFunc != nil { + defer func() { + if err := c.WrapperCleanupFunc(); err != nil { + c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err)) + } + }() + } + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %w", err)) + return base.CommandCliError + } + workersClient := workers.NewClient(client) + + switch c.FlagName { + case "": + case "null": + opts = append(opts, workers.DefaultName()) + default: + opts = append(opts, workers.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, workers.DefaultDescription()) + default: + opts = append(opts, workers.WithDescription(c.FlagDescription)) + } + + switch c.FlagRecursive { + case true: + opts = append(opts, workers.WithRecursive(true)) + } + + if c.FlagFilter != "" { + opts = append(opts, workers.WithFilter(c.FlagFilter)) + } + + var version uint32 + + if ok := extraWorkerLedFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + switch c.Func { + + } + + result, err = executeExtraWorkerLedActions(c, result, err, workersClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomWorkerLedActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + extraWorkerLedActionsFlagsMapFunc = func() map[string][]string { return nil } + extraWorkerLedSynopsisFunc = func(*WorkerLedCommand) string { return "" } + extraWorkerLedFlagsFunc = func(*WorkerLedCommand, *base.FlagSets, *base.FlagSet) {} + extraWorkerLedFlagsHandlingFunc = func(*WorkerLedCommand, *base.FlagSets, *[]workers.Option) bool { return true } + executeExtraWorkerLedActions = func(_ *WorkerLedCommand, inResult api.GenericResult, inErr error, _ *workers.Client, _ uint32, _ []workers.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomWorkerLedActionOutput = func(*WorkerLedCommand) (bool, error) { return false, nil } +) diff --git a/internal/cmd/commands/workerscmd/workers.gen.go b/internal/cmd/commands/workerscmd/workers.gen.go new file mode 100644 index 0000000000..a75ad588c9 --- /dev/null +++ b/internal/cmd/commands/workerscmd/workers.gen.go @@ -0,0 +1,314 @@ +// Code generated by "make cli"; DO NOT EDIT. +package workerscmd + +import ( + "errors" + "fmt" + "sync" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initFlags() { + flagsOnce.Do(func() { + extraFlags := extraActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsMap[k] = append(flagsMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*Command)(nil) + _ cli.CommandAutocomplete = (*Command)(nil) +) + +type Command struct { + *base.Command + + Func string + + plural string +} + +func (c *Command) AutocompleteArgs() complete.Predictor { + initFlags() + return complete.PredictAnything +} + +func (c *Command) AutocompleteFlags() complete.Flags { + initFlags() + return c.Flags().Completions() +} + +func (c *Command) Synopsis() string { + if extra := extraSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "worker" + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *Command) Help() string { + initFlags() + + var helpStr string + helpMap := common.HelpMap("worker") + + switch c.Func { + + case "read": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + case "update": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + case "delete": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + case "list": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + default: + + helpStr = helpMap["base"]() + + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsMap = map[string][]string{ + + "read": {"id"}, + + "update": {"id", "name", "description", "version"}, + + "delete": {"id"}, + + "list": {"scope-id", "filter", "recursive"}, +} + +func (c *Command) Flags() *base.FlagSets { + if len(flagsMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "worker", flagsMap, c.Func) + + extraFlagsFunc(c, set, f) + + return set +} + +func (c *Command) Run(args []string) int { + initFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + + case "create": + return cli.RunResultHelp + + } + + c.plural = "worker" + switch c.Func { + case "list": + c.plural = "workers" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []workers.Option + + if strutil.StrListContains(flagsMap[c.Func], "scope-id") { + switch c.Func { + + case "list": + if c.FlagScopeId == "" { + c.PrintCliError(errors.New("Scope ID must be passed in via -scope-id or BOUNDARY_SCOPE_ID")) + return base.CommandUserError + } + + } + } + + client, err := c.Client() + if c.WrapperCleanupFunc != nil { + defer func() { + if err := c.WrapperCleanupFunc(); err != nil { + c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err)) + } + }() + } + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %w", err)) + return base.CommandCliError + } + workersClient := workers.NewClient(client) + + switch c.FlagName { + case "": + case "null": + opts = append(opts, workers.DefaultName()) + default: + opts = append(opts, workers.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, workers.DefaultDescription()) + default: + opts = append(opts, workers.WithDescription(c.FlagDescription)) + } + + switch c.FlagRecursive { + case true: + opts = append(opts, workers.WithRecursive(true)) + } + + if c.FlagFilter != "" { + opts = append(opts, workers.WithFilter(c.FlagFilter)) + } + + var version uint32 + + switch c.Func { + + case "update": + switch c.FlagVersion { + case 0: + opts = append(opts, workers.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + + } + + if ok := extraFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + var listResult api.GenericListResult + + switch c.Func { + + case "read": + result, err = workersClient.Read(c.Context, c.FlagId, opts...) + + case "update": + result, err = workersClient.Update(c.Context, c.FlagId, version, opts...) + + case "delete": + result, err = workersClient.Delete(c.Context, c.FlagId, opts...) + + case "list": + listResult, err = workersClient.List(c.Context, c.FlagScopeId, opts...) + + } + + result, err = executeExtraActions(c, result, err, workersClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + + case "delete": + switch base.Format(c.UI) { + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + + case "table": + c.UI.Output("The delete operation completed successfully.") + } + + return base.CommandSuccess + + case "list": + switch base.Format(c.UI) { + case "json": + if ok := c.PrintJsonItems(listResult); !ok { + return base.CommandCliError + } + + case "table": + listedItems := listResult.GetItems().([]*workers.Worker) + c.UI.Output(c.printListTable(listedItems)) + } + + return base.CommandSuccess + + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + flagsOnce = new(sync.Once) + + extraActionsFlagsMapFunc = func() map[string][]string { return nil } + extraSynopsisFunc = func(*Command) string { return "" } + extraFlagsFunc = func(*Command, *base.FlagSets, *base.FlagSet) {} + extraFlagsHandlingFunc = func(*Command, *base.FlagSets, *[]workers.Option) bool { return true } + executeExtraActions = func(_ *Command, inResult api.GenericResult, inErr error, _ *workers.Client, _ uint32, _ []workers.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomActionOutput = func(*Command) (bool, error) { return false, nil } +) diff --git a/internal/cmd/gencli/input.go b/internal/cmd/gencli/input.go index 0c57d38271..cc64b3d855 100644 --- a/internal/cmd/gencli/input.go +++ b/internal/cmd/gencli/input.go @@ -30,10 +30,6 @@ type cmdInfo struct { // output env var and print HasExampleCliOutput bool - // IsAbstractType triggers some behavior specialized for abstract types, - // e.g. those that have subcommands for create/update - IsAbstractType bool - // HasId controls whether to add ID emptiness checking. Note that some // commands that allow name/scope id or name/scope name handling may skip // this in favor of custom logic. @@ -79,6 +75,10 @@ type cmdInfo struct { // IsPluginType controls whether standard plugin flags are generated IsPluginType bool + + // SkipClientCallActions allows skipping creation of an actual client + // call for an action in favor of custom logic in extra actions + SkipClientCallActions []string } var inputStructs = map[string][]*cmdInfo{ @@ -89,7 +89,6 @@ var inputStructs = map[string][]*cmdInfo{ StdActions: []string{"read", "delete", "list"}, HasExtraCommandVars: true, HasExtraHelpFunc: true, - IsAbstractType: true, Container: "AuthMethod", HasId: true, HasName: true, @@ -130,7 +129,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.AuthMethod.String(), Pkg: "authmethods", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, HasExtraHelpFunc: true, Container: "Scope", HasId: true, @@ -179,7 +177,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.CredentialStore.String(), Pkg: "credentialstores", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, HasExtraHelpFunc: true, Container: "Scope", HasId: true, @@ -206,7 +203,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.CredentialLibrary.String(), Pkg: "credentiallibraries", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, HasExtraHelpFunc: true, Container: "CredentialStore", HasId: true, @@ -246,7 +242,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.HostCatalog.String(), Pkg: "hostcatalogs", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, HasExtraHelpFunc: true, Container: "Scope", HasId: true, @@ -290,7 +285,6 @@ var inputStructs = map[string][]*cmdInfo{ StdActions: []string{"read", "delete", "list"}, HasExtraCommandVars: true, HasExtraHelpFunc: true, - IsAbstractType: true, Container: "HostCatalog", HasId: true, HasName: true, @@ -332,7 +326,6 @@ var inputStructs = map[string][]*cmdInfo{ Pkg: "hosts", StdActions: []string{"read", "delete", "list"}, HasExtraHelpFunc: true, - IsAbstractType: true, Container: "HostCatalog", HasId: true, HasName: true, @@ -358,7 +351,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.ManagedGroup.String(), Pkg: "managedgroups", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, Container: "AuthMethod", HasId: true, HasName: true, @@ -425,7 +417,6 @@ var inputStructs = map[string][]*cmdInfo{ HasExtraCommandVars: true, HasExtraHelpFunc: true, HasExampleCliOutput: true, - IsAbstractType: true, HasName: true, HasDescription: true, Container: "Scope", @@ -461,4 +452,31 @@ var inputStructs = map[string][]*cmdInfo{ VersionedActions: []string{"update", "add-accounts", "remove-accounts", "set-accounts"}, }, }, + "workers": { + { + ResourceType: resource.Worker.String(), + Pkg: "workers", + StdActions: []string{"read", "update", "delete", "list"}, + HasId: true, + Container: "Scope", + HasName: true, + HasDescription: true, + VersionedActions: []string{"update"}, + }, + { + ResourceType: resource.Worker.String(), + Pkg: "workers", + StdActions: []string{"create"}, + SubActionPrefix: "worker-led", + HasExtraCommandVars: true, + SkipNormalHelp: true, + HasExtraHelpFunc: true, + HasId: true, + HasName: true, + Container: "Scope", + HasDescription: true, + NeedsSubtypeInCreate: true, + SkipClientCallActions: []string{"create"}, + }, + }, } diff --git a/internal/cmd/gencli/templates.go b/internal/cmd/gencli/templates.go index 230e70a06c..4cb5fd21ef 100644 --- a/internal/cmd/gencli/templates.go +++ b/internal/cmd/gencli/templates.go @@ -229,8 +229,12 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Run(args []string) int { switch c.Func { case "": return cli.RunResultHelp - {{ if .IsAbstractType }} - case "create", "update": + {{ if (not (hasAction .StdActions "create" ) )}} + case "create": + return cli.RunResultHelp + {{ end }} + {{ if (not (hasAction .StdActions "update" ) )}} + case "update": return cli.RunResultHelp {{ end }} } @@ -403,9 +407,11 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Run(args []string) int { switch c.Func { {{ range $i, $action := $input.StdActions }} {{ if eq $action "create" }} + {{ if ( not ( hasAction $input.SkipClientCallActions "create") ) }} case "create": result, err = {{ $input.Pkg }}Client.Create(c.Context, {{ if (and $input.SubActionPrefix $input.NeedsSubtypeInCreate) }}"{{ $input.SubActionPrefix }}",{{ end }} c.Flag{{ $input.Container }}Id, opts...) {{ end }} + {{ end }} {{ if eq $action "read" }} case "read": result, err = {{ $input.Pkg }}Client.Read(c.Context, c.FlagId, opts...)