From 81b987d9be1d0ebf69fc086241f4f6f33608262d Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Wed, 16 Nov 2022 17:12:57 +1000 Subject: [PATCH 01/10] root worker command --- pkg/cmd/worker/worker.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 pkg/cmd/worker/worker.go diff --git a/pkg/cmd/worker/worker.go b/pkg/cmd/worker/worker.go new file mode 100644 index 00000000..9d528320 --- /dev/null +++ b/pkg/cmd/worker/worker.go @@ -0,0 +1,27 @@ +package worker + +import ( + "fmt" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/constants/annotations" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/spf13/cobra" +) + +func NewCmdWorker(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "worker ", + Short: "Manage workers", + Long: `Work with Octopus Deploy workers.`, + Example: fmt.Sprintf(heredoc.Doc(` + $ %s worker list + $ %s tenant ls + `), constants.ExecutableName, constants.ExecutableName), + Annotations: map[string]string{ + annotations.IsCore: "true", + }, + } + + return cmd +} From e6506a9533019354a5b7ee3ff2bd5ca71318fa6c Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Thu, 17 Nov 2022 13:23:11 +1000 Subject: [PATCH 02/10] feat: worker listening-tentacle create feat: worker ssh create --- go.mod | 2 +- go.sum | 2 + pkg/cmd/root/root.go | 2 + pkg/cmd/target/azure-web-app/create/create.go | 14 +- pkg/cmd/target/cloud-region/create/create.go | 14 +- .../listening-tentacle/create/create.go | 2 +- .../listening-tentacle/listening-tentacle.go | 2 +- pkg/cmd/target/shared/web.go | 14 +- pkg/cmd/target/shared/workerpool.go | 20 +- pkg/cmd/target/shared/workerpool_test.go | 12 +- pkg/cmd/target/ssh/create/create.go | 2 +- .../listening-tentacle/create/create.go | 187 +++++++++ .../listening-tentacle/listening-tentacle.go | 23 ++ pkg/cmd/worker/shared/workerpool.go | 96 +++++ pkg/cmd/worker/ssh/create/create.go | 370 ++++++++++++++++++ pkg/cmd/worker/ssh/ssh.go | 23 ++ pkg/cmd/worker/worker.go | 5 + 17 files changed, 756 insertions(+), 34 deletions(-) create mode 100644 pkg/cmd/worker/listening-tentacle/create/create.go create mode 100644 pkg/cmd/worker/listening-tentacle/listening-tentacle.go create mode 100644 pkg/cmd/worker/shared/workerpool.go create mode 100644 pkg/cmd/worker/ssh/create/create.go create mode 100644 pkg/cmd/worker/ssh/ssh.go diff --git a/go.mod b/go.mod index 8b766586..a66cecaf 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/AlecAivazis/survey/v2 v2.3.5 github.com/MakeNowJust/heredoc/v2 v2.0.1 - github.com/OctopusDeploy/go-octopusdeploy/v2 v2.15.1 + github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.0 github.com/briandowns/spinner v1.19.0 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index 3933efec..1d5fd444 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/OctopusDeploy/go-octopusdeploy/v2 v2.14.0 h1:BMFtEXFvzp+m942qRBfautdP github.com/OctopusDeploy/go-octopusdeploy/v2 v2.14.0/go.mod h1:2j9rwRfb5qUs9PEJ3W331W84kRaNge5bed4D7JR1ruU= github.com/OctopusDeploy/go-octopusdeploy/v2 v2.15.1 h1:AJ/UhXqHpFZOK2EtdSBvqTseIDb9VxqXS9Rosneco8w= github.com/OctopusDeploy/go-octopusdeploy/v2 v2.15.1/go.mod h1:2j9rwRfb5qUs9PEJ3W331W84kRaNge5bed4D7JR1ruU= +github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.0 h1:Ndr4YhSshyQT9rkOZ+o3xNAEz77/AY9klVOwULGFli4= +github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.0/go.mod h1:2j9rwRfb5qUs9PEJ3W331W84kRaNge5bed4D7JR1ruU= github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E= github.com/briandowns/spinner v1.19.0/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index 369942e8..117176e9 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -15,6 +15,7 @@ import ( taskCmd "github.com/OctopusDeploy/cli/pkg/cmd/task" tenantCmd "github.com/OctopusDeploy/cli/pkg/cmd/tenant" "github.com/OctopusDeploy/cli/pkg/cmd/version" + workerCmd "github.com/OctopusDeploy/cli/pkg/cmd/worker" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/factory" "github.com/OctopusDeploy/cli/pkg/question" @@ -41,6 +42,7 @@ func NewCmdRoot(f factory.Factory, clientFactory apiclient.ClientFactory, askPro cmd.AddCommand(environmentCmd.NewCmdEnvironment(f)) cmd.AddCommand(packageCmd.NewCmdPackage(f)) cmd.AddCommand(deploymentTargetCmd.NewCmdDeploymentTarget(f)) + cmd.AddCommand(workerCmd.NewCmdWorker(f)) // core cmd.AddCommand(projectGroupCmd.NewCmdProjectGroup(f)) diff --git a/pkg/cmd/target/azure-web-app/create/create.go b/pkg/cmd/target/azure-web-app/create/create.go index dcbd35c1..23b90f59 100644 --- a/pkg/cmd/target/azure-web-app/create/create.go +++ b/pkg/cmd/target/azure-web-app/create/create.go @@ -43,7 +43,7 @@ type CreateFlags struct { *shared.CreateTargetEnvironmentFlags *shared.CreateTargetRoleFlags *shared.CreateTargetTenantFlags - *shared.CreateTargetWorkerPoolFlags + *shared.WorkerPoolFlags *shared.WebFlags } @@ -52,7 +52,7 @@ type CreateOptions struct { *shared.CreateTargetEnvironmentOptions *shared.CreateTargetRoleOptions *shared.CreateTargetTenantOptions - *shared.CreateTargetWorkerPoolOptions + *shared.WorkerPoolOptions *cmd.Dependencies GetAllAzureAccounts @@ -70,7 +70,7 @@ func NewCreateFlags() *CreateFlags { CreateTargetRoleFlags: shared.NewCreateTargetRoleFlags(), CreateTargetEnvironmentFlags: shared.NewCreateTargetEnvironmentFlags(), CreateTargetTenantFlags: shared.NewCreateTargetTenantFlags(), - CreateTargetWorkerPoolFlags: shared.NewCreateTargetWorkerPoolFlags(), + WorkerPoolFlags: shared.NewWorkerPoolFlags(), WebFlags: shared.NewWebFlags(), } } @@ -82,7 +82,7 @@ func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) CreateTargetRoleOptions: shared.NewCreateTargetRoleOptions(dependencies), CreateTargetEnvironmentOptions: shared.NewCreateTargetEnvironmentOptions(dependencies), CreateTargetTenantOptions: shared.NewCreateTargetTenantOptions(dependencies), - CreateTargetWorkerPoolOptions: shared.NewCreateTargetWorkerPoolOptions(dependencies), + WorkerPoolOptions: shared.NewWorkerPoolOptionsForCreateTarget(dependencies), GetAllAzureAccounts: func() ([]*accounts.AzureServicePrincipalAccount, error) { return getAllAzureAccounts(*dependencies.Client) }, @@ -121,7 +121,7 @@ func NewCmdCreate(f factory.Factory) *cobra.Command { shared.RegisterCreateTargetEnvironmentFlags(cmd, createFlags.CreateTargetEnvironmentFlags) shared.RegisterCreateTargetRoleFlags(cmd, createFlags.CreateTargetRoleFlags) shared.RegisterCreateTargetTenantFlags(cmd, createFlags.CreateTargetTenantFlags) - shared.RegisterCreateTargetWorkerPoolFlags(cmd, createFlags.CreateTargetWorkerPoolFlags) + shared.RegisterCreateTargetWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) shared.RegisterWebFlag(cmd, createFlags.WebFlags) return cmd } @@ -167,7 +167,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWeb(createdTarget, opts.Dependencies, opts.WebFlags, "Azure web app") + shared.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "Azure web app") return nil } @@ -198,7 +198,7 @@ func PromptMissing(opts *CreateOptions) error { return err } - err = shared.PromptForWorkerPool(opts.CreateTargetWorkerPoolOptions, opts.CreateTargetWorkerPoolFlags) + err = shared.PromptForWorkerPool(opts.WorkerPoolOptions, opts.WorkerPoolFlags) if err != nil { return err } diff --git a/pkg/cmd/target/cloud-region/create/create.go b/pkg/cmd/target/cloud-region/create/create.go index 668f8d56..b94cf01b 100644 --- a/pkg/cmd/target/cloud-region/create/create.go +++ b/pkg/cmd/target/cloud-region/create/create.go @@ -24,7 +24,7 @@ type CreateFlags struct { Name *flag.Flag[string] *shared.CreateTargetEnvironmentFlags *shared.CreateTargetRoleFlags - *shared.CreateTargetWorkerPoolFlags + *shared.WorkerPoolFlags *shared.CreateTargetTenantFlags *shared.WebFlags } @@ -33,7 +33,7 @@ type CreateOptions struct { *CreateFlags *shared.CreateTargetEnvironmentOptions *shared.CreateTargetRoleOptions - *shared.CreateTargetWorkerPoolOptions + *shared.WorkerPoolOptions *shared.CreateTargetTenantOptions *cmd.Dependencies } @@ -41,7 +41,7 @@ type CreateOptions struct { func NewCreateFlags() *CreateFlags { return &CreateFlags{ Name: flag.New[string](FlagName, false), - CreateTargetWorkerPoolFlags: shared.NewCreateTargetWorkerPoolFlags(), + WorkerPoolFlags: shared.NewWorkerPoolFlags(), CreateTargetEnvironmentFlags: shared.NewCreateTargetEnvironmentFlags(), CreateTargetRoleFlags: shared.NewCreateTargetRoleFlags(), CreateTargetTenantFlags: shared.NewCreateTargetTenantFlags(), @@ -53,7 +53,7 @@ func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) return &CreateOptions{ CreateFlags: createFlags, Dependencies: dependencies, - CreateTargetWorkerPoolOptions: shared.NewCreateTargetWorkerPoolOptions(dependencies), + WorkerPoolOptions: shared.NewWorkerPoolOptionsForCreateTarget(dependencies), CreateTargetEnvironmentOptions: shared.NewCreateTargetEnvironmentOptions(dependencies), CreateTargetRoleOptions: shared.NewCreateTargetRoleOptions(dependencies), CreateTargetTenantOptions: shared.NewCreateTargetTenantOptions(dependencies), @@ -81,7 +81,7 @@ func NewCmdCreate(f factory.Factory) *cobra.Command { flags.StringVarP(&createFlags.Name.Value, createFlags.Name.Name, "n", "", "A short, memorable, unique name for this Cloud Region.") shared.RegisterCreateTargetEnvironmentFlags(cmd, createFlags.CreateTargetEnvironmentFlags) shared.RegisterCreateTargetRoleFlags(cmd, createFlags.CreateTargetRoleFlags) - shared.RegisterCreateTargetWorkerPoolFlags(cmd, createFlags.CreateTargetWorkerPoolFlags) + shared.RegisterCreateTargetWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) shared.RegisterCreateTargetTenantFlags(cmd, createFlags.CreateTargetTenantFlags) shared.RegisterWebFlag(cmd, createFlags.WebFlags) @@ -126,7 +126,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWeb(createdTarget, opts.Dependencies, opts.WebFlags, "cloud region") + shared.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "cloud region") return nil } @@ -147,7 +147,7 @@ func PromptMissing(opts *CreateOptions) error { return err } - err = shared.PromptForWorkerPool(opts.CreateTargetWorkerPoolOptions, opts.CreateTargetWorkerPoolFlags) + err = shared.PromptForWorkerPool(opts.WorkerPoolOptions, opts.WorkerPoolFlags) if err != nil { return err } diff --git a/pkg/cmd/target/listening-tentacle/create/create.go b/pkg/cmd/target/listening-tentacle/create/create.go index e61705f7..4460ba3e 100644 --- a/pkg/cmd/target/listening-tentacle/create/create.go +++ b/pkg/cmd/target/listening-tentacle/create/create.go @@ -152,7 +152,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWeb(createdTarget, opts.Dependencies, opts.WebFlags, "listening tentacle") + shared.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "listening tentacle") return nil } diff --git a/pkg/cmd/target/listening-tentacle/listening-tentacle.go b/pkg/cmd/target/listening-tentacle/listening-tentacle.go index e89a140f..c05a5568 100644 --- a/pkg/cmd/target/listening-tentacle/listening-tentacle.go +++ b/pkg/cmd/target/listening-tentacle/listening-tentacle.go @@ -15,7 +15,7 @@ func NewCmdListeningTentacle(f factory.Factory) *cobra.Command { Use: "listening-tentacle ", Short: "Manage listening tentacle deployment targets", Long: "Work with Octopus Deploy listening tentacle deployment targets.", - Example: fmt.Sprintf("$ %s deployment-target listening-tenatacle list", constants.ExecutableName), + Example: fmt.Sprintf("$ %s deployment-target listening-tentacle list", constants.ExecutableName), } cmd.AddCommand(cmdList.NewCmdList(f)) diff --git a/pkg/cmd/target/shared/web.go b/pkg/cmd/target/shared/web.go index d9b947a0..feee8701 100644 --- a/pkg/cmd/target/shared/web.go +++ b/pkg/cmd/target/shared/web.go @@ -8,6 +8,7 @@ import ( "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" "github.com/pkg/browser" "github.com/spf13/cobra" + "io" ) const ( @@ -28,10 +29,19 @@ func RegisterWebFlag(cmd *cobra.Command, flags *WebFlags) { cmd.Flags().BoolVarP(&flags.Web.Value, flags.Web.Name, "w", false, "Open in web browser") } -func DoWeb(target *machines.DeploymentTarget, dependencies *cmd.Dependencies, flags *WebFlags, description string) { +func DoWebForTargets(target *machines.DeploymentTarget, dependencies *cmd.Dependencies, flags *WebFlags, description string) { url := fmt.Sprintf("%s/app#/%s/infrastructure/machines/%s", dependencies.Host, dependencies.Space.GetID(), target.GetID()) + doWeb(url, description, dependencies.Out, flags) +} + +func DoWebForWorkers(worker *machines.Worker, dependencies *cmd.Dependencies, flags *WebFlags, description string) { + url := fmt.Sprintf("%s/app#/%s/infrastructure/workers/%s", dependencies.Host, dependencies.Space.GetID(), worker.GetID()) + doWeb(url, description, dependencies.Out, flags) +} + +func doWeb(url string, description string, out io.Writer, flags *WebFlags) { link := output.Bluef(url) - fmt.Fprintf(dependencies.Out, "View this %s on Octopus Deploy: %s\n", description, link) + fmt.Fprintf(out, "View this %s on Octopus Deploy: %s\n", description, link) if flags.Web.Value { browser.OpenURL(url) } diff --git a/pkg/cmd/target/shared/workerpool.go b/pkg/cmd/target/shared/workerpool.go index abd793c8..774f2d39 100644 --- a/pkg/cmd/target/shared/workerpool.go +++ b/pkg/cmd/target/shared/workerpool.go @@ -16,23 +16,24 @@ const FlagWorkerPool = "worker-pool" type GetAllWorkerPoolsCallback func() ([]*workerpools.WorkerPoolListResult, error) -type CreateTargetWorkerPoolFlags struct { +type WorkerPoolFlags struct { WorkerPool *flag.Flag[string] } -type CreateTargetWorkerPoolOptions struct { +type WorkerPoolOptions struct { *cmd.Dependencies GetAllWorkerPoolsCallback } -func NewCreateTargetWorkerPoolFlags() *CreateTargetWorkerPoolFlags { - return &CreateTargetWorkerPoolFlags{ +func NewWorkerPoolFlags() *WorkerPoolFlags { + return &WorkerPoolFlags{ WorkerPool: flag.New[string](FlagWorkerPool, false), } + } -func NewCreateTargetWorkerPoolOptions(dependencies *cmd.Dependencies) *CreateTargetWorkerPoolOptions { - return &CreateTargetWorkerPoolOptions{ +func NewWorkerPoolOptionsForCreateTarget(dependencies *cmd.Dependencies) *WorkerPoolOptions { + return &WorkerPoolOptions{ Dependencies: dependencies, GetAllWorkerPoolsCallback: func() ([]*workerpools.WorkerPoolListResult, error) { return getAllWorkerPools(dependencies.Client) @@ -40,11 +41,11 @@ func NewCreateTargetWorkerPoolOptions(dependencies *cmd.Dependencies) *CreateTar } } -func RegisterCreateTargetWorkerPoolFlags(cmd *cobra.Command, flags *CreateTargetWorkerPoolFlags) { +func RegisterCreateTargetWorkerPoolFlags(cmd *cobra.Command, flags *WorkerPoolFlags) { cmd.Flags().StringVar(&flags.WorkerPool.Value, flags.WorkerPool.Name, "", "The worker pool for the deployment target, only required if not using the default worker pool") } -func PromptForWorkerPool(opts *CreateTargetWorkerPoolOptions, flags *CreateTargetWorkerPoolFlags) error { +func PromptForWorkerPool(opts *WorkerPoolOptions, flags *WorkerPoolFlags) error { if flags.WorkerPool.Value == "" { useDefaultPool := true err := opts.Ask(&survey.Confirm{ @@ -66,6 +67,9 @@ func PromptForWorkerPool(opts *CreateTargetWorkerPoolOptions, flags *CreateTarge return err } flags.WorkerPool.Value = selectedPool.Name + if err != nil { + return err + } } } diff --git a/pkg/cmd/target/shared/workerpool_test.go b/pkg/cmd/target/shared/workerpool_test.go index f169b52c..0af15257 100644 --- a/pkg/cmd/target/shared/workerpool_test.go +++ b/pkg/cmd/target/shared/workerpool_test.go @@ -13,10 +13,10 @@ func TestPromptForWorkerPool_FlagsSupplied(t *testing.T) { pa := []*testutil.PA{} asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) - flags := shared.NewCreateTargetWorkerPoolFlags() + flags := shared.NewWorkerPoolFlags() flags.WorkerPool.Value = "Head lifeguard" - opts := shared.NewCreateTargetWorkerPoolOptions(&cmd.Dependencies{Ask: asker}) + opts := shared.NewWorkerPoolOptionsForCreateTarget(&cmd.Dependencies{Ask: asker}) err := shared.PromptForWorkerPool(opts, flags) checkRemainingPrompts() assert.NoError(t, err) @@ -29,9 +29,9 @@ func TestPromptForWorkerPool_NoFlagsSupplied(t *testing.T) { } asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) - flags := shared.NewCreateTargetWorkerPoolFlags() + flags := shared.NewWorkerPoolFlags() - opts := shared.NewCreateTargetWorkerPoolOptions(&cmd.Dependencies{Ask: asker}) + opts := shared.NewWorkerPoolOptionsForCreateTarget(&cmd.Dependencies{Ask: asker}) opts.GetAllWorkerPoolsCallback = func() ([]*workerpools.WorkerPoolListResult, error) { poolWorker1 := &workerpools.WorkerPoolListResult{ ID: "WorkerPools-1", @@ -57,9 +57,9 @@ func TestPromptForWorkerPool_UseDefault(t *testing.T) { } asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) - flags := shared.NewCreateTargetWorkerPoolFlags() + flags := shared.NewWorkerPoolFlags() - opts := shared.NewCreateTargetWorkerPoolOptions(&cmd.Dependencies{Ask: asker}) + opts := shared.NewWorkerPoolOptionsForCreateTarget(&cmd.Dependencies{Ask: asker}) err := shared.PromptForWorkerPool(opts, flags) checkRemainingPrompts() diff --git a/pkg/cmd/target/ssh/create/create.go b/pkg/cmd/target/ssh/create/create.go index 2093980d..a83c18ba 100644 --- a/pkg/cmd/target/ssh/create/create.go +++ b/pkg/cmd/target/ssh/create/create.go @@ -199,7 +199,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWeb(createdTarget, opts.Dependencies, opts.WebFlags, "ssh") + shared.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "ssh") return nil } diff --git a/pkg/cmd/worker/listening-tentacle/create/create.go b/pkg/cmd/worker/listening-tentacle/create/create.go new file mode 100644 index 00000000..0f1043da --- /dev/null +++ b/pkg/cmd/worker/listening-tentacle/create/create.go @@ -0,0 +1,187 @@ +package create + +import ( + "fmt" + "github.com/AlecAivazis/survey/v2" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/cmd" + targetShared "github.com/OctopusDeploy/cli/pkg/cmd/target/shared" + workerShared "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/question" + "github.com/OctopusDeploy/cli/pkg/util/flag" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" + "github.com/spf13/cobra" + "net/url" +) + +const ( + FlagName = "name" + FlagThumbprint = "thumbprint" + FlagUrl = "url" +) + +type CreateFlags struct { + Name *flag.Flag[string] + Thumbprint *flag.Flag[string] + URL *flag.Flag[string] + *targetShared.CreateTargetProxyFlags + *targetShared.CreateTargetMachinePolicyFlags + *workerShared.WorkerPoolFlags + *targetShared.WebFlags +} + +type CreateOptions struct { + *CreateFlags + *targetShared.CreateTargetProxyOptions + *targetShared.CreateTargetMachinePolicyOptions + *workerShared.WorkerPoolOptions + *cmd.Dependencies +} + +func NewCreateFlags() *CreateFlags { + return &CreateFlags{ + Name: flag.New[string](FlagName, false), + Thumbprint: flag.New[string](FlagThumbprint, true), + URL: flag.New[string](FlagUrl, false), + CreateTargetProxyFlags: targetShared.NewCreateTargetProxyFlags(), + CreateTargetMachinePolicyFlags: targetShared.NewCreateTargetMachinePolicyFlags(), + WorkerPoolFlags: workerShared.NewWorkerPoolFlags(), + WebFlags: targetShared.NewWebFlags(), + } +} + +func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) *CreateOptions { + return &CreateOptions{ + CreateFlags: createFlags, + Dependencies: dependencies, + CreateTargetProxyOptions: targetShared.NewCreateTargetProxyOptions(dependencies), + CreateTargetMachinePolicyOptions: targetShared.NewCreateTargetMachinePolicyOptions(dependencies), + WorkerPoolOptions: workerShared.NewWorkerPoolOptionsForCreateWorker(dependencies), + } +} + +func NewCmdCreate(f factory.Factory) *cobra.Command { + createFlags := NewCreateFlags() + + cmd := &cobra.Command{ + Use: "create", + Short: "Create a listening tentacle worker", + Long: "Create a listening tentacle worker in Octopus Deploy", + Example: fmt.Sprintf(heredoc.Doc(` + $ %s worker listening-tentacle create + `), constants.ExecutableName), + RunE: func(c *cobra.Command, _ []string) error { + opts := NewCreateOptions(createFlags, cmd.NewDependencies(f, c)) + + return createRun(opts) + }, + } + + flags := cmd.Flags() + flags.StringVarP(&createFlags.Name.Value, createFlags.Name.Name, "n", "", "A short, memorable, unique name for this Listening Tentacle worker.") + flags.StringVar(&createFlags.Thumbprint.Value, createFlags.Thumbprint.Name, "", "The X509 certificate thumbprint that securely identifies the Tentacle.") + flags.StringVar(&createFlags.URL.Value, createFlags.URL.Name, "", "The network address at which the Tentacle can be reached.") + targetShared.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) + targetShared.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) + workerShared.RegisterCreateWorkerWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) + targetShared.RegisterWebFlag(cmd, createFlags.WebFlags) + + return cmd +} + +func createRun(opts *CreateOptions) error { + if !opts.NoPrompt { + if err := PromptMissing(opts); err != nil { + return err + } + } + + url, err := url.Parse(opts.URL.Value) + if err != nil { + return err + } + + endpoint := machines.NewListeningTentacleEndpoint(url, opts.Thumbprint.Value) + if opts.Proxy.Value != "" { + proxy, err := targetShared.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + if err != nil { + return err + } + endpoint.ProxyID = proxy.GetID() + } + + workerPoolIds, err := workerShared.FindWorkerPoolIds(opts.WorkerPoolOptions, opts.WorkerPoolFlags) + if err != nil { + return err + } + + worker := machines.NewWorker(opts.Name.Value, endpoint) + worker.WorkerPoolIDs = workerPoolIds + machinePolicy, err := targetShared.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) + if err != nil { + return err + } + worker.MachinePolicyID = machinePolicy.GetID() + + createdWorker, err := opts.Client.Workers.Add(worker) + if err != nil { + return err + } + + fmt.Fprintf(opts.Out, "Successfully created listening tentcle worker '%s'.\n", worker.Name) + if !opts.NoPrompt { + autoCmd := flag.GenerateAutomationCmd(opts.CmdPath, opts.Name, opts.URL, opts.Thumbprint, opts.Proxy, opts.MachinePolicy, opts.WorkerPools) + fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) + } + + targetShared.DoWebForWorkers(createdWorker, opts.Dependencies, opts.WebFlags, "listening tentacle worker") + + return nil +} + +func PromptMissing(opts *CreateOptions) error { + err := question.AskName(opts.Ask, "", "Listening Tentacle", &opts.Name.Value) + if err != nil { + return err + } + + err = workerShared.PromptForWorkerPools(opts.WorkerPoolOptions, opts.WorkerPoolFlags) + if err != nil { + return err + } + + err = targetShared.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) + if err != nil { + return err + } + + if opts.Thumbprint.Value == "" { + if err := opts.Ask(&survey.Input{ + Message: "Thumbprint", + Help: "The X509 certificate thumbprint that securely identifies the Tentacle.", + }, &opts.Thumbprint.Value, survey.WithValidator(survey.ComposeValidators( + survey.MinLength(40), + survey.MaxLength(40), + ))); err != nil { + return err + } + } + + if opts.URL.Value == "" { + if err := opts.Ask(&survey.Input{ + Message: "URL", + Help: "The network address at which the Tentacle can be reached.", + }, &opts.URL.Value, survey.WithValidator(survey.Required)); err != nil { + return err + } + } + + err = targetShared.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/worker/listening-tentacle/listening-tentacle.go b/pkg/cmd/worker/listening-tentacle/listening-tentacle.go new file mode 100644 index 00000000..c4641ced --- /dev/null +++ b/pkg/cmd/worker/listening-tentacle/listening-tentacle.go @@ -0,0 +1,23 @@ +package listening_tentacle + +import ( + "fmt" + + cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle/create" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/spf13/cobra" +) + +func NewCmdListeningTentacle(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "listening-tentacle ", + Short: "Manage listening tentacle workers", + Long: "Work with Octopus Deploy listening tentacle workers.", + Example: fmt.Sprintf("$ %s worker listening-tentacle list", constants.ExecutableName), + } + + cmd.AddCommand(cmdCreate.NewCmdCreate(f)) + + return cmd +} diff --git a/pkg/cmd/worker/shared/workerpool.go b/pkg/cmd/worker/shared/workerpool.go new file mode 100644 index 00000000..6407e1dc --- /dev/null +++ b/pkg/cmd/worker/shared/workerpool.go @@ -0,0 +1,96 @@ +package shared + +import ( + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/question" + "github.com/OctopusDeploy/cli/pkg/util" + "github.com/OctopusDeploy/cli/pkg/util/flag" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools" + "github.com/spf13/cobra" +) + +const FlagWorkerPool = "worker-pool" + +type GetAllWorkerPoolsCallback func() ([]*workerpools.WorkerPoolListResult, error) + +type WorkerPoolFlags struct { + WorkerPools *flag.Flag[[]string] +} + +type WorkerPoolOptions struct { + *cmd.Dependencies + GetAllWorkerPoolsCallback +} + +func NewWorkerPoolFlags() *WorkerPoolFlags { + return &WorkerPoolFlags{ + WorkerPools: flag.New[[]string](FlagWorkerPool, false), + } +} + +func RegisterCreateWorkerWorkerPoolFlags(cmd *cobra.Command, flags *WorkerPoolFlags) { + cmd.Flags().StringSliceVar(&flags.WorkerPools.Value, flags.WorkerPools.Name, []string{}, "The worker pools which the worker will be a member of") +} + +func NewWorkerPoolOptionsForCreateWorker(dependencies *cmd.Dependencies) *WorkerPoolOptions { + return &WorkerPoolOptions{ + Dependencies: dependencies, + GetAllWorkerPoolsCallback: func() ([]*workerpools.WorkerPoolListResult, error) { + return getAllWorkerPools(dependencies.Client) + }, + } +} + +func getAllWorkerPools(client *client.Client) ([]*workerpools.WorkerPoolListResult, error) { + res, err := client.WorkerPools.GetAll() + if err != nil { + return nil, err + } + + return util.SliceFilter(res, func(workerPool *workerpools.WorkerPoolListResult) bool { + return workerPool.CanAddWorkers + }), nil +} + +func FindWorkerPoolIds(opts *WorkerPoolOptions, flags *WorkerPoolFlags) ([]string, error) { + var ids []string + + lookup := make(map[string]string) + for _, i := range flags.WorkerPools.Value { + lookup[i] = i + } + + allWorkerPools, err := opts.GetAllWorkerPoolsCallback() + if err != nil { + return nil, err + } + for _, p := range allWorkerPools { + if _, ok := lookup[p.ID]; ok { + ids = append(ids, p.ID) + } else if _, ok := lookup[p.Name]; ok { + ids = append(ids, p.ID) + } + } + + return ids, nil +} + +func PromptForWorkerPools(opts *WorkerPoolOptions, flags *WorkerPoolFlags) error { + if util.Empty(flags.WorkerPools.Value) { + allWorkerPools, err := opts.GetAllWorkerPoolsCallback() + if err != nil { + return err + } + selectedPools, err := question.MultiSelectMap(opts.Ask, "Select the worker pools to assign the worker to", allWorkerPools, func(pool *workerpools.WorkerPoolListResult) string { return pool.Name }, true) + if err != nil { + return err + } + + for _, p := range selectedPools { + flags.WorkerPools.Value = append(flags.WorkerPools.Value, p.Name) + } + } + + return nil +} diff --git a/pkg/cmd/worker/ssh/create/create.go b/pkg/cmd/worker/ssh/create/create.go new file mode 100644 index 00000000..c40da392 --- /dev/null +++ b/pkg/cmd/worker/ssh/create/create.go @@ -0,0 +1,370 @@ +package create + +import ( + "fmt" + "github.com/AlecAivazis/survey/v2" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/cmd/target/shared" + workerShared "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/question" + "github.com/OctopusDeploy/cli/pkg/question/selectors" + "github.com/OctopusDeploy/cli/pkg/util/flag" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" + "github.com/spf13/cobra" + "strconv" + "strings" +) + +type GetAllAccountsForSshTarget func() ([]accounts.IAccount, error) + +const ( + FlagName = "name" + FlagFingerprint = "fingerprint" + FlagHost = "host" + FlagPort = "port" + FlagAccount = "account" + FlagRuntime = "runtime" + FlagPlatform = "platform" + + SelfContainedCalamari = "self-contained" + MonoCalamari = "mono" + + LinuxX64 = "linux-x64" + LinuxArm64 = "linux-arm64" + LinuxArm = "linux-arm" + OsxX64 = "osx-x64" +) + +type CreateFlags struct { + Name *flag.Flag[string] + Fingerprint *flag.Flag[string] + HostName *flag.Flag[string] + Port *flag.Flag[int] + Account *flag.Flag[string] + Runtime *flag.Flag[string] + Platform *flag.Flag[string] + *shared.CreateTargetProxyFlags + *shared.CreateTargetMachinePolicyFlags + *workerShared.WorkerPoolFlags + *shared.WebFlags +} + +type CreateOptions struct { + *CreateFlags + GetAllAccountsForSshTarget + *shared.CreateTargetProxyOptions + *shared.CreateTargetMachinePolicyOptions + *workerShared.WorkerPoolOptions + *cmd.Dependencies +} + +func NewCreateFlags() *CreateFlags { + return &CreateFlags{ + Name: flag.New[string](FlagName, false), + Account: flag.New[string](FlagAccount, false), + Fingerprint: flag.New[string](FlagFingerprint, true), + HostName: flag.New[string](FlagHost, false), + Port: flag.New[int](FlagPort, false), + Runtime: flag.New[string](FlagRuntime, false), + Platform: flag.New[string](FlagPlatform, false), + CreateTargetProxyFlags: shared.NewCreateTargetProxyFlags(), + CreateTargetMachinePolicyFlags: shared.NewCreateTargetMachinePolicyFlags(), + WorkerPoolFlags: workerShared.NewWorkerPoolFlags(), + WebFlags: shared.NewWebFlags(), + } +} + +func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) *CreateOptions { + return &CreateOptions{ + CreateFlags: createFlags, + Dependencies: dependencies, + CreateTargetProxyOptions: shared.NewCreateTargetProxyOptions(dependencies), + CreateTargetMachinePolicyOptions: shared.NewCreateTargetMachinePolicyOptions(dependencies), + WorkerPoolOptions: workerShared.NewWorkerPoolOptionsForCreateWorker(dependencies), + + GetAllAccountsForSshTarget: func() ([]accounts.IAccount, error) { + return getAllAccountsForSshTarget(dependencies.Client) + }, + } +} + +func NewCmdCreate(f factory.Factory) *cobra.Command { + createFlags := NewCreateFlags() + + cmd := &cobra.Command{ + Use: "create", + Short: "Create a SSH worker", + Long: "Create a SSH worker in Octopus Deploy", + Example: fmt.Sprintf(heredoc.Doc(` + $ %s worker ssh create + `), constants.ExecutableName), + RunE: func(c *cobra.Command, _ []string) error { + opts := NewCreateOptions(createFlags, cmd.NewDependencies(f, c)) + + return createRun(opts) + }, + } + + flags := cmd.Flags() + flags.StringVarP(&createFlags.Name.Value, createFlags.Name.Name, "n", "", "A short, memorable, unique name for this worker.") + flags.StringVar(&createFlags.Account.Value, createFlags.Account.Name, "", "The name or ID of the SSH key pair or username/password account") + flags.StringVar(&createFlags.HostName.Value, createFlags.HostName.Name, "", "The hostname or IP address of the worker to connect to.") + flags.StringVar(&createFlags.Fingerprint.Value, createFlags.Fingerprint.Name, "", "The host fingerprint of the worker.") + flags.IntVar(&createFlags.Port.Value, createFlags.Port.Name, 0, "The port to connect to the worker on.") + flags.StringVar(&createFlags.Runtime.Value, createFlags.Runtime.Name, "", fmt.Sprintf("The runtime to use to run Calamari on the worker. Options are '%s' or '%s'", SelfContainedCalamari, MonoCalamari)) + flags.StringVar(&createFlags.Platform.Value, createFlags.Platform.Name, "", fmt.Sprintf("The platform to use for the %s Calamari. Options are '%s', '%s', '%s' or '%s'", SelfContainedCalamari, LinuxX64, LinuxArm64, LinuxArm, OsxX64)) + + shared.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) + shared.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) + workerShared.RegisterCreateWorkerWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) + shared.RegisterWebFlag(cmd, createFlags.WebFlags) + + return cmd +} + +func createRun(opts *CreateOptions) error { + if !opts.NoPrompt { + if err := PromptMissing(opts); err != nil { + return err + } + } + + account, err := getAccount(opts) + if err != nil { + return err + } + + port := opts.Port.Value + if port == 0 { + port = 22 + } + endpoint := machines.NewSSHEndpoint(opts.HostName.Value, port, opts.Fingerprint.Value) + endpoint.AccountID = account.GetID() + + if opts.Runtime.Value == SelfContainedCalamari { + endpoint.DotNetCorePlatform = opts.Platform.Value + } + + if opts.Proxy.Value != "" { + proxy, err := shared.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + if err != nil { + return err + } + endpoint.ProxyID = proxy.GetID() + } + + workerPoolIds, err := workerShared.FindWorkerPoolIds(opts.WorkerPoolOptions, opts.WorkerPoolFlags) + if err != nil { + return err + } + + worker := machines.NewWorker(opts.Name.Value, endpoint) + worker.WorkerPoolIDs = workerPoolIds + machinePolicy, err := shared.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) + if err != nil { + return err + } + worker.MachinePolicyID = machinePolicy.GetID() + + createdWorker, err := opts.Client.Workers.Add(worker) + if err != nil { + return err + } + + fmt.Fprintf(opts.Out, "Successfully created SSH worker '%s'.\n", createdWorker.Name) + if !opts.NoPrompt { + autoCmd := flag.GenerateAutomationCmd(opts.CmdPath, opts.Name, opts.HostName, opts.Port, opts.Fingerprint, opts.Runtime, opts.Platform, opts.WorkerPools, opts.Account, opts.Proxy, opts.MachinePolicy) + fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) + } + + shared.DoWebForWorkers(createdWorker, opts.Dependencies, opts.WebFlags, "ssh") + + return nil +} + +func PromptMissing(opts *CreateOptions) error { + err := question.AskName(opts.Ask, "", "SSH", &opts.Name.Value) + if err != nil { + return err + } + + err = workerShared.PromptForWorkerPools(opts.WorkerPoolOptions, opts.WorkerPoolFlags) + if err != nil { + return err + } + + err = shared.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) + if err != nil { + return err + } + + err = PromptForAccount(opts) + if err != nil { + return err + } + + err = PromptForEndpoint(opts) + if err != nil { + return err + } + + err = shared.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + if err != nil { + return err + } + + err = PromptForDotNetConfig(opts) + if err != nil { + return err + } + + return nil +} + +func PromptForEndpoint(opts *CreateOptions) error { + if opts.HostName.Value == "" { + if err := opts.Ask(&survey.Input{ + Message: "Host", + Help: "The hostname or IP address at which the deployment target can be reached.", + }, &opts.HostName.Value, survey.WithValidator(survey.Required)); err != nil { + return err + } + } + + if opts.Port.Value == 0 { + var port string + if err := opts.Ask(&survey.Input{ + Message: "Port", + Help: "Port number to connect over SSH to the deployment target. Default is 22", + }, &port); err != nil { + return err + } + + if port == "" { + port = "22" + } + + if p, err := strconv.Atoi(port); err == nil { + opts.Port.Value = p + } else { + return err + } + } + + if opts.Fingerprint.Value == "" { + if err := opts.Ask(&survey.Input{ + Message: "Host fingerprint", + Help: "The host fingerprint of the SSH deployment target.", + }, &opts.Fingerprint.Value, survey.WithValidator(survey.Required)); err != nil { + return err + } + } + + return nil +} + +func PromptForDotNetConfig(opts *CreateOptions) error { + if opts.Runtime.Value == "" { + selectedRuntime, err := selectors.SelectOptions(opts.Ask, "Select the target runtime\n", getTargetRuntimeOptions) + if err != nil { + return err + } + opts.Runtime.Value = selectedRuntime.Value + } + + if opts.Runtime.Value == SelfContainedCalamari { + if opts.Platform.Value == "" { + selectedPlatform, err := selectors.SelectOptions(opts.Ask, "Select the target platform\n", getTargetPlatformOptions) + if err != nil { + return err + } + opts.Platform.Value = selectedPlatform.Value + } + } + + return nil +} + +func PromptForAccount(opts *CreateOptions) error { + var account accounts.IAccount + if opts.Account.Value == "" { + selectedAccount, err := selectors.Select( + opts.Ask, + "Select the account to use\n", + opts.GetAllAccountsForSshTarget, + func(a accounts.IAccount) string { + return a.GetName() + }) + if err != nil { + return err + } + account = selectedAccount + } else { + a, err := getAccount(opts) + if err != nil { + return err + } + account = a + } + + opts.Account.Value = account.GetName() + return nil +} + +func getAllAccountsForSshTarget(client *client.Client) ([]accounts.IAccount, error) { + allAccounts, err := client.Accounts.GetAll() + if err != nil { + return nil, err + } + + var accounts []accounts.IAccount + for _, a := range allAccounts { + if canBeUsedForSSH(a) { + accounts = append(accounts, a) + } + } + + return accounts, nil +} + +func canBeUsedForSSH(account accounts.IAccount) bool { + accountType := account.GetAccountType() + return accountType == accounts.AccountTypeSSHKeyPair || accountType == accounts.AccountTypeUsernamePassword +} + +func getAccount(opts *CreateOptions) (accounts.IAccount, error) { + idOrName := opts.Account.Value + allAccounts, err := opts.GetAllAccountsForSshTarget() + if err != nil { + return nil, err + } + + for _, a := range allAccounts { + if strings.EqualFold(a.GetID(), idOrName) || strings.EqualFold(a.GetName(), idOrName) { + return a, nil + } + } + + return nil, fmt.Errorf("cannot find account %s", idOrName) +} + +func getTargetRuntimeOptions() []*selectors.SelectOption[string] { + return []*selectors.SelectOption[string]{ + {Display: "Self-contained Calamari", Value: SelfContainedCalamari}, + {Display: "Calamari on Mono", Value: MonoCalamari}, + } +} + +func getTargetPlatformOptions() []*selectors.SelectOption[string] { + return []*selectors.SelectOption[string]{ + {Display: "Linux x64", Value: LinuxX64}, + {Display: "Linux ARM64", Value: LinuxArm64}, + {Display: "Linux ARM", Value: LinuxArm}, + {Display: "OSX x64", Value: OsxX64}, + } +} diff --git a/pkg/cmd/worker/ssh/ssh.go b/pkg/cmd/worker/ssh/ssh.go new file mode 100644 index 00000000..1db73632 --- /dev/null +++ b/pkg/cmd/worker/ssh/ssh.go @@ -0,0 +1,23 @@ +package ssh + +import ( + "fmt" + + cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh/create" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/spf13/cobra" +) + +func NewCmdSsh(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "ssh ", + Short: "Manage SSH workers", + Long: "Work with Octopus Deploy SSH workers.", + Example: fmt.Sprintf("$ %s worker SSH list", constants.ExecutableName), + } + + cmd.AddCommand(cmdCreate.NewCmdCreate(f)) + + return cmd +} diff --git a/pkg/cmd/worker/worker.go b/pkg/cmd/worker/worker.go index 9d528320..12b51bf7 100644 --- a/pkg/cmd/worker/worker.go +++ b/pkg/cmd/worker/worker.go @@ -3,6 +3,8 @@ package worker import ( "fmt" "github.com/MakeNowJust/heredoc/v2" + listeningTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle" + ssh "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/constants/annotations" "github.com/OctopusDeploy/cli/pkg/factory" @@ -23,5 +25,8 @@ func NewCmdWorker(f factory.Factory) *cobra.Command { }, } + cmd.AddCommand(listeningTentacle.NewCmdListeningTentacle(f)) + cmd.AddCommand(ssh.NewCmdSsh(f)) + return cmd } From a2a5711cb43814d8ead317977a4420288c637f32 Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Thu, 17 Nov 2022 14:24:07 +1000 Subject: [PATCH 03/10] move bits out to machines common --- pkg/cmd/target/azure-web-app/create/create.go | 9 ++-- pkg/cmd/target/cloud-region/create/create.go | 9 ++-- .../listening-tentacle/create/create.go | 37 ++++++------- pkg/cmd/target/ssh/create/create.go | 37 ++++++------- .../listening-tentacle/create/create.go | 54 +++++++++---------- pkg/cmd/worker/ssh/create/create.go | 54 +++++++++---------- .../machinepolicy.go | 2 +- .../machinepolicy_test.go | 16 +++--- .../target/shared => machinescommon}/proxy.go | 7 +-- .../shared => machinescommon}/proxy_test.go | 22 ++++---- .../target/shared => machinescommon}/web.go | 2 +- 11 files changed, 127 insertions(+), 122 deletions(-) rename pkg/{cmd/target/shared => machinescommon}/machinepolicy.go (99%) rename pkg/{cmd/target/shared => machinescommon}/machinepolicy_test.go (67%) rename pkg/{cmd/target/shared => machinescommon}/proxy.go (88%) rename pkg/{cmd/target/shared => machinescommon}/proxy_test.go (74%) rename pkg/{cmd/target/shared => machinescommon}/web.go (98%) diff --git a/pkg/cmd/target/azure-web-app/create/create.go b/pkg/cmd/target/azure-web-app/create/create.go index 23b90f59..ca68b71d 100644 --- a/pkg/cmd/target/azure-web-app/create/create.go +++ b/pkg/cmd/target/azure-web-app/create/create.go @@ -9,6 +9,7 @@ import ( "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/executionscommon" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/pkg/question" "github.com/OctopusDeploy/cli/pkg/question/selectors" "github.com/OctopusDeploy/cli/pkg/util" @@ -44,7 +45,7 @@ type CreateFlags struct { *shared.CreateTargetRoleFlags *shared.CreateTargetTenantFlags *shared.WorkerPoolFlags - *shared.WebFlags + *machinescommon.WebFlags } type CreateOptions struct { @@ -71,7 +72,7 @@ func NewCreateFlags() *CreateFlags { CreateTargetEnvironmentFlags: shared.NewCreateTargetEnvironmentFlags(), CreateTargetTenantFlags: shared.NewCreateTargetTenantFlags(), WorkerPoolFlags: shared.NewWorkerPoolFlags(), - WebFlags: shared.NewWebFlags(), + WebFlags: machinescommon.NewWebFlags(), } } @@ -122,7 +123,7 @@ func NewCmdCreate(f factory.Factory) *cobra.Command { shared.RegisterCreateTargetRoleFlags(cmd, createFlags.CreateTargetRoleFlags) shared.RegisterCreateTargetTenantFlags(cmd, createFlags.CreateTargetTenantFlags) shared.RegisterCreateTargetWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) - shared.RegisterWebFlag(cmd, createFlags.WebFlags) + machinescommon.RegisterWebFlag(cmd, createFlags.WebFlags) return cmd } @@ -167,7 +168,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "Azure web app") + machinescommon.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "Azure web app") return nil } diff --git a/pkg/cmd/target/cloud-region/create/create.go b/pkg/cmd/target/cloud-region/create/create.go index b94cf01b..57f9182a 100644 --- a/pkg/cmd/target/cloud-region/create/create.go +++ b/pkg/cmd/target/cloud-region/create/create.go @@ -8,6 +8,7 @@ import ( "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/executionscommon" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/pkg/question" "github.com/OctopusDeploy/cli/pkg/util" "github.com/OctopusDeploy/cli/pkg/util/flag" @@ -26,7 +27,7 @@ type CreateFlags struct { *shared.CreateTargetRoleFlags *shared.WorkerPoolFlags *shared.CreateTargetTenantFlags - *shared.WebFlags + *machinescommon.WebFlags } type CreateOptions struct { @@ -45,7 +46,7 @@ func NewCreateFlags() *CreateFlags { CreateTargetEnvironmentFlags: shared.NewCreateTargetEnvironmentFlags(), CreateTargetRoleFlags: shared.NewCreateTargetRoleFlags(), CreateTargetTenantFlags: shared.NewCreateTargetTenantFlags(), - WebFlags: shared.NewWebFlags(), + WebFlags: machinescommon.NewWebFlags(), } } @@ -83,7 +84,7 @@ func NewCmdCreate(f factory.Factory) *cobra.Command { shared.RegisterCreateTargetRoleFlags(cmd, createFlags.CreateTargetRoleFlags) shared.RegisterCreateTargetWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) shared.RegisterCreateTargetTenantFlags(cmd, createFlags.CreateTargetTenantFlags) - shared.RegisterWebFlag(cmd, createFlags.WebFlags) + machinescommon.RegisterWebFlag(cmd, createFlags.WebFlags) return cmd } @@ -126,7 +127,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "cloud region") + machinescommon.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "cloud region") return nil } diff --git a/pkg/cmd/target/listening-tentacle/create/create.go b/pkg/cmd/target/listening-tentacle/create/create.go index 4460ba3e..aa118d75 100644 --- a/pkg/cmd/target/listening-tentacle/create/create.go +++ b/pkg/cmd/target/listening-tentacle/create/create.go @@ -9,6 +9,7 @@ import ( "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/executionscommon" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/pkg/question" "github.com/OctopusDeploy/cli/pkg/util" "github.com/OctopusDeploy/cli/pkg/util/flag" @@ -28,20 +29,20 @@ type CreateFlags struct { Name *flag.Flag[string] Thumbprint *flag.Flag[string] URL *flag.Flag[string] - *shared.CreateTargetProxyFlags + *machinescommon.CreateTargetProxyFlags *shared.CreateTargetEnvironmentFlags *shared.CreateTargetRoleFlags - *shared.CreateTargetMachinePolicyFlags + *machinescommon.CreateTargetMachinePolicyFlags *shared.CreateTargetTenantFlags - *shared.WebFlags + *machinescommon.WebFlags } type CreateOptions struct { *CreateFlags - *shared.CreateTargetProxyOptions + *machinescommon.CreateTargetProxyOptions *shared.CreateTargetEnvironmentOptions *shared.CreateTargetRoleOptions - *shared.CreateTargetMachinePolicyOptions + *machinescommon.CreateTargetMachinePolicyOptions *shared.CreateTargetTenantOptions *cmd.Dependencies } @@ -52,11 +53,11 @@ func NewCreateFlags() *CreateFlags { Thumbprint: flag.New[string](FlagThumbprint, true), URL: flag.New[string](FlagUrl, false), CreateTargetRoleFlags: shared.NewCreateTargetRoleFlags(), - CreateTargetProxyFlags: shared.NewCreateTargetProxyFlags(), - CreateTargetMachinePolicyFlags: shared.NewCreateTargetMachinePolicyFlags(), + CreateTargetProxyFlags: machinescommon.NewCreateTargetProxyFlags(), + CreateTargetMachinePolicyFlags: machinescommon.NewCreateTargetMachinePolicyFlags(), CreateTargetEnvironmentFlags: shared.NewCreateTargetEnvironmentFlags(), CreateTargetTenantFlags: shared.NewCreateTargetTenantFlags(), - WebFlags: shared.NewWebFlags(), + WebFlags: machinescommon.NewWebFlags(), } } @@ -65,8 +66,8 @@ func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) CreateFlags: createFlags, Dependencies: dependencies, CreateTargetRoleOptions: shared.NewCreateTargetRoleOptions(dependencies), - CreateTargetProxyOptions: shared.NewCreateTargetProxyOptions(dependencies), - CreateTargetMachinePolicyOptions: shared.NewCreateTargetMachinePolicyOptions(dependencies), + CreateTargetProxyOptions: machinescommon.NewCreateTargetProxyOptions(dependencies), + CreateTargetMachinePolicyOptions: machinescommon.NewCreateTargetMachinePolicyOptions(dependencies), CreateTargetEnvironmentOptions: shared.NewCreateTargetEnvironmentOptions(dependencies), CreateTargetTenantOptions: shared.NewCreateTargetTenantOptions(dependencies), } @@ -95,10 +96,10 @@ func NewCmdCreate(f factory.Factory) *cobra.Command { flags.StringVar(&createFlags.URL.Value, createFlags.URL.Name, "", "The network address at which the Tentacle can be reached.") shared.RegisterCreateTargetEnvironmentFlags(cmd, createFlags.CreateTargetEnvironmentFlags) shared.RegisterCreateTargetRoleFlags(cmd, createFlags.CreateTargetRoleFlags) - shared.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) - shared.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) + machinescommon.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) + machinescommon.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) shared.RegisterCreateTargetTenantFlags(cmd, createFlags.CreateTargetTenantFlags) - shared.RegisterWebFlag(cmd, createFlags.WebFlags) + machinescommon.RegisterWebFlag(cmd, createFlags.WebFlags) return cmd } @@ -123,7 +124,7 @@ func createRun(opts *CreateOptions) error { endpoint := machines.NewListeningTentacleEndpoint(url, opts.Thumbprint.Value) if opts.Proxy.Value != "" { - proxy, err := shared.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + proxy, err := machinescommon.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) if err != nil { return err } @@ -131,7 +132,7 @@ func createRun(opts *CreateOptions) error { } deploymentTarget := machines.NewDeploymentTarget(opts.Name.Value, endpoint, environmentIds, shared.DistinctRoles(opts.Roles.Value)) - machinePolicy, err := shared.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) + machinePolicy, err := machinescommon.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) if err != nil { return err } @@ -152,7 +153,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "listening tentacle") + machinescommon.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "listening tentacle") return nil } @@ -194,12 +195,12 @@ func PromptMissing(opts *CreateOptions) error { } } - err = shared.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) + err = machinescommon.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) if err != nil { return err } - err = shared.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + err = machinescommon.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) if err != nil { return err } diff --git a/pkg/cmd/target/ssh/create/create.go b/pkg/cmd/target/ssh/create/create.go index a83c18ba..3371a484 100644 --- a/pkg/cmd/target/ssh/create/create.go +++ b/pkg/cmd/target/ssh/create/create.go @@ -9,6 +9,7 @@ import ( "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/executionscommon" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/pkg/question" "github.com/OctopusDeploy/cli/pkg/question/selectors" "github.com/OctopusDeploy/cli/pkg/util" @@ -50,21 +51,21 @@ type CreateFlags struct { Account *flag.Flag[string] Runtime *flag.Flag[string] Platform *flag.Flag[string] - *shared.CreateTargetProxyFlags + *machinescommon.CreateTargetProxyFlags *shared.CreateTargetEnvironmentFlags *shared.CreateTargetRoleFlags - *shared.CreateTargetMachinePolicyFlags + *machinescommon.CreateTargetMachinePolicyFlags *shared.CreateTargetTenantFlags - *shared.WebFlags + *machinescommon.WebFlags } type CreateOptions struct { *CreateFlags GetAllAccountsForSshTarget - *shared.CreateTargetProxyOptions + *machinescommon.CreateTargetProxyOptions *shared.CreateTargetEnvironmentOptions *shared.CreateTargetRoleOptions - *shared.CreateTargetMachinePolicyOptions + *machinescommon.CreateTargetMachinePolicyOptions *shared.CreateTargetTenantOptions *cmd.Dependencies } @@ -79,11 +80,11 @@ func NewCreateFlags() *CreateFlags { Runtime: flag.New[string](FlagRuntime, false), Platform: flag.New[string](FlagPlatform, false), CreateTargetRoleFlags: shared.NewCreateTargetRoleFlags(), - CreateTargetProxyFlags: shared.NewCreateTargetProxyFlags(), - CreateTargetMachinePolicyFlags: shared.NewCreateTargetMachinePolicyFlags(), + CreateTargetProxyFlags: machinescommon.NewCreateTargetProxyFlags(), + CreateTargetMachinePolicyFlags: machinescommon.NewCreateTargetMachinePolicyFlags(), CreateTargetEnvironmentFlags: shared.NewCreateTargetEnvironmentFlags(), CreateTargetTenantFlags: shared.NewCreateTargetTenantFlags(), - WebFlags: shared.NewWebFlags(), + WebFlags: machinescommon.NewWebFlags(), } } @@ -92,8 +93,8 @@ func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) CreateFlags: createFlags, Dependencies: dependencies, CreateTargetRoleOptions: shared.NewCreateTargetRoleOptions(dependencies), - CreateTargetProxyOptions: shared.NewCreateTargetProxyOptions(dependencies), - CreateTargetMachinePolicyOptions: shared.NewCreateTargetMachinePolicyOptions(dependencies), + CreateTargetProxyOptions: machinescommon.NewCreateTargetProxyOptions(dependencies), + CreateTargetMachinePolicyOptions: machinescommon.NewCreateTargetMachinePolicyOptions(dependencies), CreateTargetEnvironmentOptions: shared.NewCreateTargetEnvironmentOptions(dependencies), CreateTargetTenantOptions: shared.NewCreateTargetTenantOptions(dependencies), @@ -131,10 +132,10 @@ func NewCmdCreate(f factory.Factory) *cobra.Command { shared.RegisterCreateTargetEnvironmentFlags(cmd, createFlags.CreateTargetEnvironmentFlags) shared.RegisterCreateTargetRoleFlags(cmd, createFlags.CreateTargetRoleFlags) - shared.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) - shared.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) + machinescommon.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) + machinescommon.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) shared.RegisterCreateTargetTenantFlags(cmd, createFlags.CreateTargetTenantFlags) - shared.RegisterWebFlag(cmd, createFlags.WebFlags) + machinescommon.RegisterWebFlag(cmd, createFlags.WebFlags) return cmd } @@ -169,7 +170,7 @@ func createRun(opts *CreateOptions) error { } if opts.Proxy.Value != "" { - proxy, err := shared.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + proxy, err := machinescommon.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) if err != nil { return err } @@ -177,7 +178,7 @@ func createRun(opts *CreateOptions) error { } deploymentTarget := machines.NewDeploymentTarget(opts.Name.Value, endpoint, environmentIds, shared.DistinctRoles(opts.Roles.Value)) - machinePolicy, err := shared.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) + machinePolicy, err := machinescommon.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) if err != nil { return err } @@ -199,7 +200,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "ssh") + machinescommon.DoWebForTargets(createdTarget, opts.Dependencies, opts.WebFlags, "ssh") return nil } @@ -220,7 +221,7 @@ func PromptMissing(opts *CreateOptions) error { return err } - err = shared.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) + err = machinescommon.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) if err != nil { return err } @@ -235,7 +236,7 @@ func PromptMissing(opts *CreateOptions) error { return err } - err = shared.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + err = machinescommon.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) if err != nil { return err } diff --git a/pkg/cmd/worker/listening-tentacle/create/create.go b/pkg/cmd/worker/listening-tentacle/create/create.go index 0f1043da..c2e9f26b 100644 --- a/pkg/cmd/worker/listening-tentacle/create/create.go +++ b/pkg/cmd/worker/listening-tentacle/create/create.go @@ -5,10 +5,10 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc/v2" "github.com/OctopusDeploy/cli/pkg/cmd" - targetShared "github.com/OctopusDeploy/cli/pkg/cmd/target/shared" - workerShared "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/pkg/question" "github.com/OctopusDeploy/cli/pkg/util/flag" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" @@ -26,17 +26,17 @@ type CreateFlags struct { Name *flag.Flag[string] Thumbprint *flag.Flag[string] URL *flag.Flag[string] - *targetShared.CreateTargetProxyFlags - *targetShared.CreateTargetMachinePolicyFlags - *workerShared.WorkerPoolFlags - *targetShared.WebFlags + *machinescommon.CreateTargetProxyFlags + *machinescommon.CreateTargetMachinePolicyFlags + *shared.WorkerPoolFlags + *machinescommon.WebFlags } type CreateOptions struct { *CreateFlags - *targetShared.CreateTargetProxyOptions - *targetShared.CreateTargetMachinePolicyOptions - *workerShared.WorkerPoolOptions + *machinescommon.CreateTargetProxyOptions + *machinescommon.CreateTargetMachinePolicyOptions + *shared.WorkerPoolOptions *cmd.Dependencies } @@ -45,10 +45,10 @@ func NewCreateFlags() *CreateFlags { Name: flag.New[string](FlagName, false), Thumbprint: flag.New[string](FlagThumbprint, true), URL: flag.New[string](FlagUrl, false), - CreateTargetProxyFlags: targetShared.NewCreateTargetProxyFlags(), - CreateTargetMachinePolicyFlags: targetShared.NewCreateTargetMachinePolicyFlags(), - WorkerPoolFlags: workerShared.NewWorkerPoolFlags(), - WebFlags: targetShared.NewWebFlags(), + CreateTargetProxyFlags: machinescommon.NewCreateTargetProxyFlags(), + CreateTargetMachinePolicyFlags: machinescommon.NewCreateTargetMachinePolicyFlags(), + WorkerPoolFlags: shared.NewWorkerPoolFlags(), + WebFlags: machinescommon.NewWebFlags(), } } @@ -56,9 +56,9 @@ func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) return &CreateOptions{ CreateFlags: createFlags, Dependencies: dependencies, - CreateTargetProxyOptions: targetShared.NewCreateTargetProxyOptions(dependencies), - CreateTargetMachinePolicyOptions: targetShared.NewCreateTargetMachinePolicyOptions(dependencies), - WorkerPoolOptions: workerShared.NewWorkerPoolOptionsForCreateWorker(dependencies), + CreateTargetProxyOptions: machinescommon.NewCreateTargetProxyOptions(dependencies), + CreateTargetMachinePolicyOptions: machinescommon.NewCreateTargetMachinePolicyOptions(dependencies), + WorkerPoolOptions: shared.NewWorkerPoolOptions(dependencies), } } @@ -83,10 +83,10 @@ func NewCmdCreate(f factory.Factory) *cobra.Command { flags.StringVarP(&createFlags.Name.Value, createFlags.Name.Name, "n", "", "A short, memorable, unique name for this Listening Tentacle worker.") flags.StringVar(&createFlags.Thumbprint.Value, createFlags.Thumbprint.Name, "", "The X509 certificate thumbprint that securely identifies the Tentacle.") flags.StringVar(&createFlags.URL.Value, createFlags.URL.Name, "", "The network address at which the Tentacle can be reached.") - targetShared.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) - targetShared.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) - workerShared.RegisterCreateWorkerWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) - targetShared.RegisterWebFlag(cmd, createFlags.WebFlags) + machinescommon.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) + machinescommon.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) + shared.RegisterCreateWorkerWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) + machinescommon.RegisterWebFlag(cmd, createFlags.WebFlags) return cmd } @@ -105,21 +105,21 @@ func createRun(opts *CreateOptions) error { endpoint := machines.NewListeningTentacleEndpoint(url, opts.Thumbprint.Value) if opts.Proxy.Value != "" { - proxy, err := targetShared.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + proxy, err := machinescommon.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) if err != nil { return err } endpoint.ProxyID = proxy.GetID() } - workerPoolIds, err := workerShared.FindWorkerPoolIds(opts.WorkerPoolOptions, opts.WorkerPoolFlags) + workerPoolIds, err := shared.FindWorkerPoolIds(opts.WorkerPoolOptions, opts.WorkerPoolFlags) if err != nil { return err } worker := machines.NewWorker(opts.Name.Value, endpoint) worker.WorkerPoolIDs = workerPoolIds - machinePolicy, err := targetShared.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) + machinePolicy, err := machinescommon.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) if err != nil { return err } @@ -136,7 +136,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - targetShared.DoWebForWorkers(createdWorker, opts.Dependencies, opts.WebFlags, "listening tentacle worker") + machinescommon.DoWebForWorkers(createdWorker, opts.Dependencies, opts.WebFlags, "listening tentacle worker") return nil } @@ -147,12 +147,12 @@ func PromptMissing(opts *CreateOptions) error { return err } - err = workerShared.PromptForWorkerPools(opts.WorkerPoolOptions, opts.WorkerPoolFlags) + err = shared.PromptForWorkerPools(opts.WorkerPoolOptions, opts.WorkerPoolFlags) if err != nil { return err } - err = targetShared.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) + err = machinescommon.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) if err != nil { return err } @@ -178,7 +178,7 @@ func PromptMissing(opts *CreateOptions) error { } } - err = targetShared.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + err = machinescommon.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) if err != nil { return err } diff --git a/pkg/cmd/worker/ssh/create/create.go b/pkg/cmd/worker/ssh/create/create.go index c40da392..5b9dbae7 100644 --- a/pkg/cmd/worker/ssh/create/create.go +++ b/pkg/cmd/worker/ssh/create/create.go @@ -5,10 +5,10 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc/v2" "github.com/OctopusDeploy/cli/pkg/cmd" - "github.com/OctopusDeploy/cli/pkg/cmd/target/shared" - workerShared "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/pkg/question" "github.com/OctopusDeploy/cli/pkg/question/selectors" "github.com/OctopusDeploy/cli/pkg/util/flag" @@ -48,18 +48,18 @@ type CreateFlags struct { Account *flag.Flag[string] Runtime *flag.Flag[string] Platform *flag.Flag[string] - *shared.CreateTargetProxyFlags - *shared.CreateTargetMachinePolicyFlags - *workerShared.WorkerPoolFlags - *shared.WebFlags + *machinescommon.CreateTargetProxyFlags + *machinescommon.CreateTargetMachinePolicyFlags + *shared.WorkerPoolFlags + *machinescommon.WebFlags } type CreateOptions struct { *CreateFlags GetAllAccountsForSshTarget - *shared.CreateTargetProxyOptions - *shared.CreateTargetMachinePolicyOptions - *workerShared.WorkerPoolOptions + *machinescommon.CreateTargetProxyOptions + *machinescommon.CreateTargetMachinePolicyOptions + *shared.WorkerPoolOptions *cmd.Dependencies } @@ -72,10 +72,10 @@ func NewCreateFlags() *CreateFlags { Port: flag.New[int](FlagPort, false), Runtime: flag.New[string](FlagRuntime, false), Platform: flag.New[string](FlagPlatform, false), - CreateTargetProxyFlags: shared.NewCreateTargetProxyFlags(), - CreateTargetMachinePolicyFlags: shared.NewCreateTargetMachinePolicyFlags(), - WorkerPoolFlags: workerShared.NewWorkerPoolFlags(), - WebFlags: shared.NewWebFlags(), + CreateTargetProxyFlags: machinescommon.NewCreateTargetProxyFlags(), + CreateTargetMachinePolicyFlags: machinescommon.NewCreateTargetMachinePolicyFlags(), + WorkerPoolFlags: shared.NewWorkerPoolFlags(), + WebFlags: machinescommon.NewWebFlags(), } } @@ -83,9 +83,9 @@ func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) return &CreateOptions{ CreateFlags: createFlags, Dependencies: dependencies, - CreateTargetProxyOptions: shared.NewCreateTargetProxyOptions(dependencies), - CreateTargetMachinePolicyOptions: shared.NewCreateTargetMachinePolicyOptions(dependencies), - WorkerPoolOptions: workerShared.NewWorkerPoolOptionsForCreateWorker(dependencies), + CreateTargetProxyOptions: machinescommon.NewCreateTargetProxyOptions(dependencies), + CreateTargetMachinePolicyOptions: machinescommon.NewCreateTargetMachinePolicyOptions(dependencies), + WorkerPoolOptions: shared.NewWorkerPoolOptions(dependencies), GetAllAccountsForSshTarget: func() ([]accounts.IAccount, error) { return getAllAccountsForSshTarget(dependencies.Client) @@ -119,10 +119,10 @@ func NewCmdCreate(f factory.Factory) *cobra.Command { flags.StringVar(&createFlags.Runtime.Value, createFlags.Runtime.Name, "", fmt.Sprintf("The runtime to use to run Calamari on the worker. Options are '%s' or '%s'", SelfContainedCalamari, MonoCalamari)) flags.StringVar(&createFlags.Platform.Value, createFlags.Platform.Name, "", fmt.Sprintf("The platform to use for the %s Calamari. Options are '%s', '%s', '%s' or '%s'", SelfContainedCalamari, LinuxX64, LinuxArm64, LinuxArm, OsxX64)) - shared.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) - shared.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) - workerShared.RegisterCreateWorkerWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) - shared.RegisterWebFlag(cmd, createFlags.WebFlags) + machinescommon.RegisterCreateTargetProxyFlags(cmd, createFlags.CreateTargetProxyFlags) + machinescommon.RegisterCreateTargetMachinePolicyFlags(cmd, createFlags.CreateTargetMachinePolicyFlags) + shared.RegisterCreateWorkerWorkerPoolFlags(cmd, createFlags.WorkerPoolFlags) + machinescommon.RegisterWebFlag(cmd, createFlags.WebFlags) return cmd } @@ -151,21 +151,21 @@ func createRun(opts *CreateOptions) error { } if opts.Proxy.Value != "" { - proxy, err := shared.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + proxy, err := machinescommon.FindProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) if err != nil { return err } endpoint.ProxyID = proxy.GetID() } - workerPoolIds, err := workerShared.FindWorkerPoolIds(opts.WorkerPoolOptions, opts.WorkerPoolFlags) + workerPoolIds, err := shared.FindWorkerPoolIds(opts.WorkerPoolOptions, opts.WorkerPoolFlags) if err != nil { return err } worker := machines.NewWorker(opts.Name.Value, endpoint) worker.WorkerPoolIDs = workerPoolIds - machinePolicy, err := shared.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) + machinePolicy, err := machinescommon.FindMachinePolicy(opts.GetAllMachinePoliciesCallback, opts.MachinePolicy.Value) if err != nil { return err } @@ -182,7 +182,7 @@ func createRun(opts *CreateOptions) error { fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd) } - shared.DoWebForWorkers(createdWorker, opts.Dependencies, opts.WebFlags, "ssh") + machinescommon.DoWebForWorkers(createdWorker, opts.Dependencies, opts.WebFlags, "ssh") return nil } @@ -193,12 +193,12 @@ func PromptMissing(opts *CreateOptions) error { return err } - err = workerShared.PromptForWorkerPools(opts.WorkerPoolOptions, opts.WorkerPoolFlags) + err = shared.PromptForWorkerPools(opts.WorkerPoolOptions, opts.WorkerPoolFlags) if err != nil { return err } - err = shared.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) + err = machinescommon.PromptForMachinePolicy(opts.CreateTargetMachinePolicyOptions, opts.CreateTargetMachinePolicyFlags) if err != nil { return err } @@ -213,7 +213,7 @@ func PromptMissing(opts *CreateOptions) error { return err } - err = shared.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) + err = machinescommon.PromptForProxy(opts.CreateTargetProxyOptions, opts.CreateTargetProxyFlags) if err != nil { return err } diff --git a/pkg/cmd/target/shared/machinepolicy.go b/pkg/machinescommon/machinepolicy.go similarity index 99% rename from pkg/cmd/target/shared/machinepolicy.go rename to pkg/machinescommon/machinepolicy.go index 3f3a4811..e9d6a67b 100644 --- a/pkg/cmd/target/shared/machinepolicy.go +++ b/pkg/machinescommon/machinepolicy.go @@ -1,4 +1,4 @@ -package shared +package machinescommon import ( "fmt" diff --git a/pkg/cmd/target/shared/machinepolicy_test.go b/pkg/machinescommon/machinepolicy_test.go similarity index 67% rename from pkg/cmd/target/shared/machinepolicy_test.go rename to pkg/machinescommon/machinepolicy_test.go index 4858195a..8ceaf1d9 100644 --- a/pkg/cmd/target/shared/machinepolicy_test.go +++ b/pkg/machinescommon/machinepolicy_test.go @@ -1,8 +1,8 @@ -package shared_test +package machinescommon_test import ( "github.com/OctopusDeploy/cli/pkg/cmd" - "github.com/OctopusDeploy/cli/pkg/cmd/target/shared" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/test/testutil" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" "github.com/stretchr/testify/assert" @@ -13,12 +13,12 @@ func TestMachinePolicyFlagSupplied_ShouldNotPrompt(t *testing.T) { pa := []*testutil.PA{} asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) - flags := shared.NewCreateTargetMachinePolicyFlags() + flags := machinescommon.NewCreateTargetMachinePolicyFlags() flags.MachinePolicy.Value = "MachinePolicy-1" - opts := shared.NewCreateTargetMachinePolicyOptions(&cmd.Dependencies{Ask: asker}) + opts := machinescommon.NewCreateTargetMachinePolicyOptions(&cmd.Dependencies{Ask: asker}) - err := shared.PromptForMachinePolicy(opts, flags) + err := machinescommon.PromptForMachinePolicy(opts, flags) checkRemainingPrompts() assert.NoError(t, err) @@ -30,8 +30,8 @@ func TestNoFlag_ShouldPrompt(t *testing.T) { } asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) - flags := shared.NewCreateTargetMachinePolicyFlags() - opts := shared.NewCreateTargetMachinePolicyOptions(&cmd.Dependencies{Ask: asker}) + flags := machinescommon.NewCreateTargetMachinePolicyFlags() + opts := machinescommon.NewCreateTargetMachinePolicyOptions(&cmd.Dependencies{Ask: asker}) opts.GetAllMachinePoliciesCallback = func() ([]*machines.MachinePolicy, error) { return []*machines.MachinePolicy{ machines.NewMachinePolicy("Policy 1"), @@ -39,7 +39,7 @@ func TestNoFlag_ShouldPrompt(t *testing.T) { }, nil } - err := shared.PromptForMachinePolicy(opts, flags) + err := machinescommon.PromptForMachinePolicy(opts, flags) checkRemainingPrompts() assert.NoError(t, err) assert.Equal(t, "Policy 2", flags.MachinePolicy.Value) diff --git a/pkg/cmd/target/shared/proxy.go b/pkg/machinescommon/proxy.go similarity index 88% rename from pkg/cmd/target/shared/proxy.go rename to pkg/machinescommon/proxy.go index 3a9648ac..9e652c13 100644 --- a/pkg/cmd/target/shared/proxy.go +++ b/pkg/machinescommon/proxy.go @@ -1,9 +1,10 @@ -package shared +package machinescommon import ( "fmt" "github.com/AlecAivazis/survey/v2" "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/cmd/target/shared" "github.com/OctopusDeploy/cli/pkg/question/selectors" "github.com/OctopusDeploy/cli/pkg/util/flag" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" @@ -43,7 +44,7 @@ type CreateTargetProxyOptions struct { func NewCreateTargetProxyFlags() *CreateTargetProxyFlags { return &CreateTargetProxyFlags{ - Proxy: flag.New[string](FlagProxy, false), + Proxy: flag.New[string](shared.FlagProxy, false), } } @@ -57,7 +58,7 @@ func NewCreateTargetProxyOptions(dependencies *cmd.Dependencies) *CreateTargetPr } func RegisterCreateTargetProxyFlags(cmd *cobra.Command, proxyFlags *CreateTargetProxyFlags) { - cmd.Flags().StringVar(&proxyFlags.Proxy.Value, FlagProxy, "", "Select whether to use a proxy to connect to this Tentacle. If omitted, will connect directly.") + cmd.Flags().StringVar(&proxyFlags.Proxy.Value, shared.FlagProxy, "", "Select whether to use a proxy to connect to this Tentacle. If omitted, will connect directly.") } func FindProxy(opts *CreateTargetProxyOptions, flags *CreateTargetProxyFlags) (*proxies.Proxy, error) { diff --git a/pkg/cmd/target/shared/proxy_test.go b/pkg/machinescommon/proxy_test.go similarity index 74% rename from pkg/cmd/target/shared/proxy_test.go rename to pkg/machinescommon/proxy_test.go index 92f1e7cf..4ce38d77 100644 --- a/pkg/cmd/target/shared/proxy_test.go +++ b/pkg/machinescommon/proxy_test.go @@ -1,8 +1,8 @@ -package shared_test +package machinescommon_test import ( "github.com/OctopusDeploy/cli/pkg/cmd" - "github.com/OctopusDeploy/cli/pkg/cmd/target/shared" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/test/testutil" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/proxies" @@ -14,12 +14,12 @@ func TestProxyFlagSupplied_ShouldNotPrompt(t *testing.T) { pa := []*testutil.PA{} asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) - flags := shared.NewCreateTargetProxyFlags() + flags := machinescommon.NewCreateTargetProxyFlags() flags.Proxy.Value = "MachineProxy-1" - opts := shared.NewCreateTargetProxyOptions(&cmd.Dependencies{Ask: asker}) + opts := machinescommon.NewCreateTargetProxyOptions(&cmd.Dependencies{Ask: asker}) - err := shared.PromptForProxy(opts, flags) + err := machinescommon.PromptForProxy(opts, flags) checkRemainingPrompts() assert.NoError(t, err) @@ -32,8 +32,8 @@ func TestNoProxyFlag_ShouldPrompt(t *testing.T) { } asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) - flags := shared.NewCreateTargetProxyFlags() - opts := shared.NewCreateTargetProxyOptions(&cmd.Dependencies{Ask: asker}) + flags := machinescommon.NewCreateTargetProxyFlags() + opts := machinescommon.NewCreateTargetProxyOptions(&cmd.Dependencies{Ask: asker}) opts.GetAllProxiesCallback = func() ([]*proxies.Proxy, error) { return []*proxies.Proxy{ proxies.NewProxy("Proxy 1", "example.com", "user", core.NewSensitiveValue("password")), @@ -41,7 +41,7 @@ func TestNoProxyFlag_ShouldPrompt(t *testing.T) { }, nil } - err := shared.PromptForProxy(opts, flags) + err := machinescommon.PromptForProxy(opts, flags) checkRemainingPrompts() assert.NoError(t, err) assert.Equal(t, "Proxy 2", flags.Proxy.Value) @@ -53,8 +53,8 @@ func TestNoProxyFlag_DirectConnection(t *testing.T) { } asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) - flags := shared.NewCreateTargetProxyFlags() - opts := shared.NewCreateTargetProxyOptions(&cmd.Dependencies{Ask: asker}) + flags := machinescommon.NewCreateTargetProxyFlags() + opts := machinescommon.NewCreateTargetProxyOptions(&cmd.Dependencies{Ask: asker}) opts.GetAllProxiesCallback = func() ([]*proxies.Proxy, error) { return []*proxies.Proxy{ proxies.NewProxy("Proxy 1", "example.com", "user", core.NewSensitiveValue("password")), @@ -62,7 +62,7 @@ func TestNoProxyFlag_DirectConnection(t *testing.T) { }, nil } - err := shared.PromptForProxy(opts, flags) + err := machinescommon.PromptForProxy(opts, flags) checkRemainingPrompts() assert.NoError(t, err) assert.Empty(t, flags.Proxy.Value) diff --git a/pkg/cmd/target/shared/web.go b/pkg/machinescommon/web.go similarity index 98% rename from pkg/cmd/target/shared/web.go rename to pkg/machinescommon/web.go index feee8701..70a58bdc 100644 --- a/pkg/cmd/target/shared/web.go +++ b/pkg/machinescommon/web.go @@ -1,4 +1,4 @@ -package shared +package machinescommon import ( "fmt" From 6cecc9ba630927df5e5e4db5c3a8a60594e8dd60 Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Thu, 17 Nov 2022 14:24:34 +1000 Subject: [PATCH 04/10] test for worker pool selection for workers --- pkg/cmd/worker/shared/workerpool.go | 2 +- pkg/cmd/worker/shared/workerpool_test.go | 53 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 pkg/cmd/worker/shared/workerpool_test.go diff --git a/pkg/cmd/worker/shared/workerpool.go b/pkg/cmd/worker/shared/workerpool.go index 6407e1dc..c60e0ed9 100644 --- a/pkg/cmd/worker/shared/workerpool.go +++ b/pkg/cmd/worker/shared/workerpool.go @@ -33,7 +33,7 @@ func RegisterCreateWorkerWorkerPoolFlags(cmd *cobra.Command, flags *WorkerPoolFl cmd.Flags().StringSliceVar(&flags.WorkerPools.Value, flags.WorkerPools.Name, []string{}, "The worker pools which the worker will be a member of") } -func NewWorkerPoolOptionsForCreateWorker(dependencies *cmd.Dependencies) *WorkerPoolOptions { +func NewWorkerPoolOptions(dependencies *cmd.Dependencies) *WorkerPoolOptions { return &WorkerPoolOptions{ Dependencies: dependencies, GetAllWorkerPoolsCallback: func() ([]*workerpools.WorkerPoolListResult, error) { diff --git a/pkg/cmd/worker/shared/workerpool_test.go b/pkg/cmd/worker/shared/workerpool_test.go new file mode 100644 index 00000000..19addb13 --- /dev/null +++ b/pkg/cmd/worker/shared/workerpool_test.go @@ -0,0 +1,53 @@ +package shared_test + +import ( + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" + "github.com/OctopusDeploy/cli/test/testutil" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestPromptForWorkerPool_FlagsSupplied(t *testing.T) { + pa := []*testutil.PA{} + + asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) + flags := shared.NewWorkerPoolFlags() + flags.WorkerPools.Value = []string{"Head lifeguard"} + + opts := shared.NewWorkerPoolOptions(&cmd.Dependencies{Ask: asker}) + err := shared.PromptForWorkerPools(opts, flags) + checkRemainingPrompts() + assert.NoError(t, err) +} + +func TestPromptForWorkerPool_NoFlagsSupplied(t *testing.T) { + pa := []*testutil.PA{ + testutil.NewMultiSelectPrompt("Select the worker pools to assign the worker to", "", []string{"Groundskeeper", "Swim instructor"}, []string{"Groundskeeper", "Swim instructor"}), + } + + asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa) + flags := shared.NewWorkerPoolFlags() + + opts := shared.NewWorkerPoolOptions(&cmd.Dependencies{Ask: asker}) + opts.GetAllWorkerPoolsCallback = func() ([]*workerpools.WorkerPoolListResult, error) { + poolWorker1 := &workerpools.WorkerPoolListResult{ + ID: "WorkerPools-1", + Name: "Groundskeeper", + WorkerPoolType: workerpools.WorkerPoolTypeStatic, + CanAddWorkers: true, + } + poolWorker2 := &workerpools.WorkerPoolListResult{ + ID: "WorkerPools-2", + Name: "Swim instructor", + WorkerPoolType: workerpools.WorkerPoolTypeStatic, + CanAddWorkers: true, + } + return []*workerpools.WorkerPoolListResult{poolWorker1, poolWorker2}, nil + } + err := shared.PromptForWorkerPools(opts, flags) + checkRemainingPrompts() + assert.NoError(t, err) + assert.Equal(t, []string{"Groundskeeper", "Swim instructor"}, flags.WorkerPools.Value) +} From 0fc225d7a7c227c746809b6f028ae01d73c6d49d Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Thu, 17 Nov 2022 15:24:40 +1000 Subject: [PATCH 05/10] move comms maps to common machines location --- pkg/cmd/target/list/list.go | 5 +++-- pkg/{cmd/target/shared => machinescommon}/comms.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) rename pkg/{cmd/target/shared => machinescommon}/comms.go (97%) diff --git a/pkg/cmd/target/list/list.go b/pkg/cmd/target/list/list.go index 1abfac92..d4459a66 100644 --- a/pkg/cmd/target/list/list.go +++ b/pkg/cmd/target/list/list.go @@ -7,6 +7,7 @@ import ( "github.com/OctopusDeploy/cli/pkg/cmd/target/shared" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/machinescommon" "github.com/OctopusDeploy/cli/pkg/output" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" "github.com/spf13/cobra" @@ -81,7 +82,7 @@ func ListRun(opts *ListOptions) error { return TargetAsJson{ Id: item.GetID(), Name: item.Name, - Type: shared.CommunicationStyleToDeploymentTargetTypeMap[item.Endpoint.GetCommunicationStyle()], + Type: machinescommon.CommunicationStyleToDeploymentTargetTypeMap[item.Endpoint.GetCommunicationStyle()], Roles: item.Roles, Environments: environments, Tenants: tenants, @@ -93,7 +94,7 @@ func ListRun(opts *ListOptions) error { Row: func(item *machines.DeploymentTarget) []string { environmentNames := resolveValues(item.EnvironmentIDs, environmentMap) tenantNames := resolveValues(item.TenantIDs, tenantMap) - return []string{output.Bold(item.Name), shared.CommunicationStyleToDescriptionMap[item.Endpoint.GetCommunicationStyle()], formatAsList(item.Roles), formatAsList(environmentNames), formatAsList(tenantNames), formatAsList(item.TenantTags)} + return []string{output.Bold(item.Name), machinescommon.CommunicationStyleToDescriptionMap[item.Endpoint.GetCommunicationStyle()], formatAsList(item.Roles), formatAsList(environmentNames), formatAsList(tenantNames), formatAsList(item.TenantTags)} }, }, Basic: func(item *machines.DeploymentTarget) string { diff --git a/pkg/cmd/target/shared/comms.go b/pkg/machinescommon/comms.go similarity index 97% rename from pkg/cmd/target/shared/comms.go rename to pkg/machinescommon/comms.go index a9da1f1c..e578f657 100644 --- a/pkg/cmd/target/shared/comms.go +++ b/pkg/machinescommon/comms.go @@ -1,4 +1,4 @@ -package shared +package machinescommon var CommunicationStyleToDescriptionMap = map[string]string{ "TentaclePassive": "Listening Tentacle", From 54a2aba8cdb1b9c0814fa5620e10b9300d9bf14e Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Thu, 17 Nov 2022 15:24:51 +1000 Subject: [PATCH 06/10] feat: worker list --- pkg/cmd/worker/list/list.go | 125 ++++++++++++++++++++++++++++++++ pkg/cmd/worker/shared/worker.go | 40 ++++++++++ pkg/cmd/worker/worker.go | 4 +- 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 pkg/cmd/worker/list/list.go create mode 100644 pkg/cmd/worker/shared/worker.go diff --git a/pkg/cmd/worker/list/list.go b/pkg/cmd/worker/list/list.go new file mode 100644 index 00000000..561997f6 --- /dev/null +++ b/pkg/cmd/worker/list/list.go @@ -0,0 +1,125 @@ +package list + +import ( + "fmt" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/machinescommon" + "github.com/OctopusDeploy/cli/pkg/output" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" + "github.com/spf13/cobra" + "strings" +) + +type ListOptions struct { + *cobra.Command + *cmd.Dependencies + *shared.GetWorkersOptions +} + +type Entity struct { + Id string `json:"Id"` + Name string `json:"Name"` +} + +func NewListOptions(dependencies *cmd.Dependencies, command *cobra.Command, query machines.WorkersQuery) *ListOptions { + return &ListOptions{ + Command: command, + Dependencies: dependencies, + GetWorkersOptions: shared.NewGetWorkersOptionsForAllWorkers(dependencies), + } +} + +func NewCmdList(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List workers in an instance of Octopus Deploy", + Long: "List workers in an instance of Octopus Deploy.", + Aliases: []string{"ls"}, + Example: fmt.Sprintf(heredoc.Doc(` + $ %s workers list + `), constants.ExecutableName), + RunE: func(c *cobra.Command, args []string) error { + return ListRun(NewListOptions(cmd.NewDependencies(f, c), c, machines.WorkersQuery{})) + }, + } + + return cmd +} + +func ListRun(opts *ListOptions) error { + allTargets, err := opts.GetWorkersCallback() + if err != nil { + return err + } + + type TargetAsJson struct { + Id string `json:"Id"` + Name string `json:"Name"` + Type string `json:"Type"` + WorkerPools []Entity `json:"WorkerPools"` + } + + workerPoolMap, err := GetWorkerPoolMap(opts, err) + if err != nil { + return err + } + + return output.PrintArray(allTargets, opts.Command, output.Mappers[*machines.Worker]{ + Json: func(item *machines.Worker) any { + + return TargetAsJson{ + Id: item.GetID(), + Name: item.Name, + Type: machinescommon.CommunicationStyleToDeploymentTargetTypeMap[item.Endpoint.GetCommunicationStyle()], + WorkerPools: resolveEntities(item.WorkerPoolIDs, workerPoolMap), + } + }, + Table: output.TableDefinition[*machines.Worker]{ + Header: []string{"NAME", "TYPE", "WORKER POOLS"}, + Row: func(item *machines.Worker) []string { + poolNames := resolveValues(item.WorkerPoolIDs, workerPoolMap) + return []string{output.Bold(item.Name), machinescommon.CommunicationStyleToDescriptionMap[item.Endpoint.GetCommunicationStyle()], formatAsList(poolNames)} + }, + }, + Basic: func(item *machines.Worker) string { + return item.Name + }, + }) +} + +func resolveValues(keys []string, lookup map[string]string) []string { + var values []string + for _, key := range keys { + values = append(values, lookup[key]) + } + return values +} + +func resolveEntities(keys []string, lookup map[string]string) []Entity { + var entities []Entity + for _, k := range keys { + entities = append(entities, Entity{Id: k, Name: lookup[k]}) + } + + return entities +} + +func GetWorkerPoolMap(opts *ListOptions, err error) (map[string]string, error) { + workerPoolMap := make(map[string]string) + allEnvs, err := opts.Client.WorkerPools.GetAll() + if err != nil { + return nil, err + } + for _, e := range allEnvs { + workerPoolMap[e.ID] = e.Name + } + return workerPoolMap, nil +} + +func formatAsList(items []string) string { + return strings.Join(items, ",") +} diff --git a/pkg/cmd/worker/shared/worker.go b/pkg/cmd/worker/shared/worker.go new file mode 100644 index 00000000..21b7647c --- /dev/null +++ b/pkg/cmd/worker/shared/worker.go @@ -0,0 +1,40 @@ +package shared + +import ( + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" + "math" +) + +type GetWorkersCallback func() ([]*machines.Worker, error) + +type GetWorkersOptions struct { + GetWorkersCallback +} + +func NewGetWorkersOptions(dependencies *cmd.Dependencies, query machines.WorkersQuery) *GetWorkersOptions { + return &GetWorkersOptions{ + GetWorkersCallback: func() ([]*machines.Worker, error) { + return GetAllWorkers(*dependencies.Client, query) + }, + } +} + +func NewGetWorkersOptionsForAllWorkers(dependencies *cmd.Dependencies) *GetWorkersOptions { + return &GetWorkersOptions{ + GetWorkersCallback: func() ([]*machines.Worker, error) { + return GetAllWorkers(*dependencies.Client, machines.WorkersQuery{}) + }, + } +} + +func GetAllWorkers(client client.Client, query machines.WorkersQuery) ([]*machines.Worker, error) { + query.Skip = 0 + query.Take = math.MaxInt32 + res, err := client.Workers.Get(query) + if err != nil { + return nil, err + } + return res.Items, nil +} diff --git a/pkg/cmd/worker/worker.go b/pkg/cmd/worker/worker.go index 12b51bf7..bd8dc499 100644 --- a/pkg/cmd/worker/worker.go +++ b/pkg/cmd/worker/worker.go @@ -3,8 +3,9 @@ package worker import ( "fmt" "github.com/MakeNowJust/heredoc/v2" + cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/list" listeningTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle" - ssh "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/constants/annotations" "github.com/OctopusDeploy/cli/pkg/factory" @@ -27,6 +28,7 @@ func NewCmdWorker(f factory.Factory) *cobra.Command { cmd.AddCommand(listeningTentacle.NewCmdListeningTentacle(f)) cmd.AddCommand(ssh.NewCmdSsh(f)) + cmd.AddCommand(cmdList.NewCmdList(f)) return cmd } From f5ac8f508bb97ed7a46f8ee2b8c7037dafff2536 Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Thu, 17 Nov 2022 16:32:02 +1000 Subject: [PATCH 07/10] feat: worker list feat: worker ssh list feat: worker listening-tentacle list feat: worker polling-tentacle list --- pkg/cmd/worker/list/list.go | 6 ++-- .../worker/listening-tentacle/list/list.go | 33 +++++++++++++++++++ .../listening-tentacle/listening-tentacle.go | 2 ++ pkg/cmd/worker/polling-tentacle/list/list.go | 33 +++++++++++++++++++ .../polling-tentacle/polling-tentacle.go | 23 +++++++++++++ pkg/cmd/worker/shared/worker.go | 28 +++++++++++----- pkg/cmd/worker/ssh/list/list.go | 33 +++++++++++++++++++ pkg/cmd/worker/ssh/ssh.go | 2 ++ pkg/cmd/worker/worker.go | 2 ++ 9 files changed, 150 insertions(+), 12 deletions(-) create mode 100644 pkg/cmd/worker/listening-tentacle/list/list.go create mode 100644 pkg/cmd/worker/polling-tentacle/list/list.go create mode 100644 pkg/cmd/worker/polling-tentacle/polling-tentacle.go create mode 100644 pkg/cmd/worker/ssh/list/list.go diff --git a/pkg/cmd/worker/list/list.go b/pkg/cmd/worker/list/list.go index 561997f6..162b779b 100644 --- a/pkg/cmd/worker/list/list.go +++ b/pkg/cmd/worker/list/list.go @@ -25,11 +25,11 @@ type Entity struct { Name string `json:"Name"` } -func NewListOptions(dependencies *cmd.Dependencies, command *cobra.Command, query machines.WorkersQuery) *ListOptions { +func NewListOptions(dependencies *cmd.Dependencies, command *cobra.Command, filter func(*machines.Worker) bool) *ListOptions { return &ListOptions{ Command: command, Dependencies: dependencies, - GetWorkersOptions: shared.NewGetWorkersOptionsForAllWorkers(dependencies), + GetWorkersOptions: shared.NewGetWorkersOptions(dependencies, filter), } } @@ -43,7 +43,7 @@ func NewCmdList(f factory.Factory) *cobra.Command { $ %s workers list `), constants.ExecutableName), RunE: func(c *cobra.Command, args []string) error { - return ListRun(NewListOptions(cmd.NewDependencies(f, c), c, machines.WorkersQuery{})) + return ListRun(NewListOptions(cmd.NewDependencies(f, c), c, nil)) }, } diff --git a/pkg/cmd/worker/listening-tentacle/list/list.go b/pkg/cmd/worker/listening-tentacle/list/list.go new file mode 100644 index 00000000..d7bb43b4 --- /dev/null +++ b/pkg/cmd/worker/listening-tentacle/list/list.go @@ -0,0 +1,33 @@ +package list + +import ( + "fmt" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/list" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" + "github.com/spf13/cobra" +) + +func NewCmdList(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List Listening Tentacle workers in an instance of Octopus Deploy", + Long: "List Listening Tentacle workers in an instance of Octopus Deploy.", + Aliases: []string{"ls"}, + Example: fmt.Sprintf(heredoc.Doc(` + $ %s worker listening-tentacle list + `), constants.ExecutableName), + RunE: func(c *cobra.Command, args []string) error { + dependencies := cmd.NewDependencies(f, c) + options := list.NewListOptions(dependencies, c, func(worker *machines.Worker) bool { + return worker.Endpoint.GetCommunicationStyle() == "TentaclePassive" + }) + return list.ListRun(options) + }, + } + + return cmd +} diff --git a/pkg/cmd/worker/listening-tentacle/listening-tentacle.go b/pkg/cmd/worker/listening-tentacle/listening-tentacle.go index c4641ced..178ef092 100644 --- a/pkg/cmd/worker/listening-tentacle/listening-tentacle.go +++ b/pkg/cmd/worker/listening-tentacle/listening-tentacle.go @@ -4,6 +4,7 @@ import ( "fmt" cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle/create" + cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle/list" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/factory" "github.com/spf13/cobra" @@ -18,6 +19,7 @@ func NewCmdListeningTentacle(f factory.Factory) *cobra.Command { } cmd.AddCommand(cmdCreate.NewCmdCreate(f)) + cmd.AddCommand(cmdList.NewCmdList(f)) return cmd } diff --git a/pkg/cmd/worker/polling-tentacle/list/list.go b/pkg/cmd/worker/polling-tentacle/list/list.go new file mode 100644 index 00000000..29844417 --- /dev/null +++ b/pkg/cmd/worker/polling-tentacle/list/list.go @@ -0,0 +1,33 @@ +package list + +import ( + "fmt" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/list" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" + "github.com/spf13/cobra" +) + +func NewCmdList(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List Polling Tentacle workers in an instance of Octopus Deploy", + Long: "List Polling Tentacle workers in an instance of Octopus Deploy.", + Aliases: []string{"ls"}, + Example: fmt.Sprintf(heredoc.Doc(` + $ %s worker polling-tentacle list + `), constants.ExecutableName), + RunE: func(c *cobra.Command, args []string) error { + dependencies := cmd.NewDependencies(f, c) + options := list.NewListOptions(dependencies, c, func(worker *machines.Worker) bool { + return worker.Endpoint.GetCommunicationStyle() == "TentacleActive" + }) + return list.ListRun(options) + }, + } + + return cmd +} diff --git a/pkg/cmd/worker/polling-tentacle/polling-tentacle.go b/pkg/cmd/worker/polling-tentacle/polling-tentacle.go new file mode 100644 index 00000000..f7e8e1a4 --- /dev/null +++ b/pkg/cmd/worker/polling-tentacle/polling-tentacle.go @@ -0,0 +1,23 @@ +package polling_tentacle + +import ( + "fmt" + + cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/polling-tentacle/list" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/spf13/cobra" +) + +func NewCmdPollingTentacle(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "polling-tentacle ", + Short: "Manage polling tentacle workers", + Long: "Work with Octopus Deploy polling tentacle workers.", + Example: fmt.Sprintf("$ %s worker polling-tentacle list", constants.ExecutableName), + } + + cmd.AddCommand(cmdList.NewCmdList(f)) + + return cmd +} diff --git a/pkg/cmd/worker/shared/worker.go b/pkg/cmd/worker/shared/worker.go index 21b7647c..09a76fca 100644 --- a/pkg/cmd/worker/shared/worker.go +++ b/pkg/cmd/worker/shared/worker.go @@ -4,7 +4,6 @@ import ( "github.com/OctopusDeploy/cli/pkg/cmd" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" - "math" ) type GetWorkersCallback func() ([]*machines.Worker, error) @@ -13,10 +12,10 @@ type GetWorkersOptions struct { GetWorkersCallback } -func NewGetWorkersOptions(dependencies *cmd.Dependencies, query machines.WorkersQuery) *GetWorkersOptions { +func NewGetWorkersOptions(dependencies *cmd.Dependencies, filter func(*machines.Worker) bool) *GetWorkersOptions { return &GetWorkersOptions{ GetWorkersCallback: func() ([]*machines.Worker, error) { - return GetAllWorkers(*dependencies.Client, query) + return GetWorkers(*dependencies.Client, filter) }, } } @@ -24,17 +23,28 @@ func NewGetWorkersOptions(dependencies *cmd.Dependencies, query machines.Workers func NewGetWorkersOptionsForAllWorkers(dependencies *cmd.Dependencies) *GetWorkersOptions { return &GetWorkersOptions{ GetWorkersCallback: func() ([]*machines.Worker, error) { - return GetAllWorkers(*dependencies.Client, machines.WorkersQuery{}) + return GetWorkers(*dependencies.Client, nil) }, } } -func GetAllWorkers(client client.Client, query machines.WorkersQuery) ([]*machines.Worker, error) { - query.Skip = 0 - query.Take = math.MaxInt32 - res, err := client.Workers.Get(query) +func GetWorkers(client client.Client, filter func(*machines.Worker) bool) ([]*machines.Worker, error) { + allWorkers, err := client.Workers.GetAll() if err != nil { return nil, err } - return res.Items, nil + + if filter == nil { + return allWorkers, nil + } + + var workers []*machines.Worker + for _, w := range allWorkers { + filterResult := filter(w) + if filterResult { + workers = append(workers, w) + } + } + + return workers, nil } diff --git a/pkg/cmd/worker/ssh/list/list.go b/pkg/cmd/worker/ssh/list/list.go new file mode 100644 index 00000000..6c1a3e1b --- /dev/null +++ b/pkg/cmd/worker/ssh/list/list.go @@ -0,0 +1,33 @@ +package list + +import ( + "fmt" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/list" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" + "github.com/spf13/cobra" +) + +func NewCmdList(f factory.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List SSH workers in an instance of Octopus Deploy", + Long: "List SSH workers in an instance of Octopus Deploy.", + Aliases: []string{"ls"}, + Example: fmt.Sprintf(heredoc.Doc(` + $ %s worker ssh list + `), constants.ExecutableName), + RunE: func(c *cobra.Command, args []string) error { + dependencies := cmd.NewDependencies(f, c) + options := list.NewListOptions(dependencies, c, func(worker *machines.Worker) bool { + return worker.Endpoint.GetCommunicationStyle() == "Ssh" + }) + return list.ListRun(options) + }, + } + + return cmd +} diff --git a/pkg/cmd/worker/ssh/ssh.go b/pkg/cmd/worker/ssh/ssh.go index 1db73632..760ec0cb 100644 --- a/pkg/cmd/worker/ssh/ssh.go +++ b/pkg/cmd/worker/ssh/ssh.go @@ -4,6 +4,7 @@ import ( "fmt" cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh/create" + cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh/list" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/factory" "github.com/spf13/cobra" @@ -18,6 +19,7 @@ func NewCmdSsh(f factory.Factory) *cobra.Command { } cmd.AddCommand(cmdCreate.NewCmdCreate(f)) + cmd.AddCommand(cmdList.NewCmdList(f)) return cmd } diff --git a/pkg/cmd/worker/worker.go b/pkg/cmd/worker/worker.go index bd8dc499..21446417 100644 --- a/pkg/cmd/worker/worker.go +++ b/pkg/cmd/worker/worker.go @@ -5,6 +5,7 @@ import ( "github.com/MakeNowJust/heredoc/v2" cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/list" listeningTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle" + pollingTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/polling-tentacle" "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh" "github.com/OctopusDeploy/cli/pkg/constants" "github.com/OctopusDeploy/cli/pkg/constants/annotations" @@ -27,6 +28,7 @@ func NewCmdWorker(f factory.Factory) *cobra.Command { } cmd.AddCommand(listeningTentacle.NewCmdListeningTentacle(f)) + cmd.AddCommand(pollingTentacle.NewCmdPollingTentacle(f)) cmd.AddCommand(ssh.NewCmdSsh(f)) cmd.AddCommand(cmdList.NewCmdList(f)) From 0e818d4450bd3953313a106d51ed3f9e402185b0 Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Fri, 18 Nov 2022 13:39:33 +1000 Subject: [PATCH 08/10] feat: worker delete --- pkg/cmd/worker/delete/delete.go | 88 +++++++++++++++++++++++++++++++++ pkg/cmd/worker/shared/worker.go | 14 ++++++ pkg/cmd/worker/worker.go | 2 + 3 files changed, 104 insertions(+) create mode 100644 pkg/cmd/worker/delete/delete.go diff --git a/pkg/cmd/worker/delete/delete.go b/pkg/cmd/worker/delete/delete.go new file mode 100644 index 00000000..1b29f458 --- /dev/null +++ b/pkg/cmd/worker/delete/delete.go @@ -0,0 +1,88 @@ +package delete + +import ( + "fmt" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/cmd" + "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared" + "github.com/OctopusDeploy/cli/pkg/constants" + "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/question" + "github.com/OctopusDeploy/cli/pkg/question/selectors" + "github.com/OctopusDeploy/cli/pkg/util" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines" + "github.com/spf13/cobra" +) + +type DeleteOptions struct { + *cmd.Dependencies + *shared.GetWorkersOptions +} + +func NewDeleteOptions(dependencies *cmd.Dependencies) *DeleteOptions { + return &DeleteOptions{ + Dependencies: dependencies, + GetWorkersOptions: shared.NewGetWorkersOptions(dependencies, nil), + } +} + +func NewCmdDelete(f factory.Factory) *cobra.Command { + var skipConfirmation bool + cmd := &cobra.Command{ + Use: "delete { | }", + Short: "Delete a worker in an instance of Octopus Deploy", + Long: "Delete a worker in an instance of Octopus Deploy", + Aliases: []string{"del", "rm", "remove"}, + Example: fmt.Sprintf(heredoc.Doc(` + $ %s worker delete + $ %s worker rm + `), constants.ExecutableName, constants.ExecutableName), + RunE: func(c *cobra.Command, args []string) error { + deps := cmd.NewDependencies(f, c) + + if util.Empty(args) { + opts := NewDeleteOptions(deps) + return deleteRun(opts) + } + + idOrName := args[0] + opts := NewDeleteOptions(deps) + worker, err := opts.GetWorkerCallback(idOrName) + if err != nil { + return err + } + + if worker == nil { + return fmt.Errorf("cannot find a worker with name or ID of '%s'", idOrName) + } + + if !skipConfirmation { // TODO NO_PROMPT env var or whatever we do there + return question.DeleteWithConfirmation(f.Ask, "deployment target", worker.Name, worker.GetID(), func() error { + return delete(opts.Client, worker) + }) + } + + return delete(opts.Client, worker) + }, + } + + question.RegisterConfirmDeletionFlag(cmd, &skipConfirmation, "deployment target") + + return cmd +} + +func deleteRun(opts *DeleteOptions) error { + worker, err := selectors.Select(opts.Ask, "Select the worker you wish to delete:", opts.GetWorkersCallback, func(target *machines.Worker) string { return target.Name }) + if err != nil { + return err + } + + return question.DeleteWithConfirmation(opts.Ask, "worker", worker.Name, worker.GetID(), func() error { + return delete(opts.Client, worker) + }) +} + +func delete(client *client.Client, itemToDelete *machines.Worker) error { + return client.Workers.DeleteByID(itemToDelete.GetID()) +} diff --git a/pkg/cmd/worker/shared/worker.go b/pkg/cmd/worker/shared/worker.go index 09a76fca..c9dadbc4 100644 --- a/pkg/cmd/worker/shared/worker.go +++ b/pkg/cmd/worker/shared/worker.go @@ -7,9 +7,11 @@ import ( ) type GetWorkersCallback func() ([]*machines.Worker, error) +type GetWorkerCallback func(identifer string) (*machines.Worker, error) type GetWorkersOptions struct { GetWorkersCallback + GetWorkerCallback } func NewGetWorkersOptions(dependencies *cmd.Dependencies, filter func(*machines.Worker) bool) *GetWorkersOptions { @@ -17,6 +19,9 @@ func NewGetWorkersOptions(dependencies *cmd.Dependencies, filter func(*machines. GetWorkersCallback: func() ([]*machines.Worker, error) { return GetWorkers(*dependencies.Client, filter) }, + GetWorkerCallback: func(identifier string) (*machines.Worker, error) { + return GetWorker(*dependencies.Client, identifier) + }, } } @@ -48,3 +53,12 @@ func GetWorkers(client client.Client, filter func(*machines.Worker) bool) ([]*ma return workers, nil } + +func GetWorker(client client.Client, identifier string) (*machines.Worker, error) { + worker, err := client.Workers.GetByIdentifier(identifier) + if err != nil { + return nil, err + } + + return worker, nil +} diff --git a/pkg/cmd/worker/worker.go b/pkg/cmd/worker/worker.go index 21446417..63fe59b1 100644 --- a/pkg/cmd/worker/worker.go +++ b/pkg/cmd/worker/worker.go @@ -3,6 +3,7 @@ package worker import ( "fmt" "github.com/MakeNowJust/heredoc/v2" + cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/worker/delete" cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/list" listeningTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle" pollingTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/polling-tentacle" @@ -31,6 +32,7 @@ func NewCmdWorker(f factory.Factory) *cobra.Command { cmd.AddCommand(pollingTentacle.NewCmdPollingTentacle(f)) cmd.AddCommand(ssh.NewCmdSsh(f)) cmd.AddCommand(cmdList.NewCmdList(f)) + cmd.AddCommand(cmdDelete.NewCmdDelete(f)) return cmd } From 29ab0150b46198b239cf007c5a51277abb33e8d6 Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Fri, 18 Nov 2022 14:10:47 +1000 Subject: [PATCH 09/10] updated reference for go-octopusdeploy --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a66cecaf..2ae20f4f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/AlecAivazis/survey/v2 v2.3.5 github.com/MakeNowJust/heredoc/v2 v2.0.1 - github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.0 + github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.1 github.com/briandowns/spinner v1.19.0 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index 1d5fd444..84d8057b 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/OctopusDeploy/go-octopusdeploy/v2 v2.15.1 h1:AJ/UhXqHpFZOK2EtdSBvqTse github.com/OctopusDeploy/go-octopusdeploy/v2 v2.15.1/go.mod h1:2j9rwRfb5qUs9PEJ3W331W84kRaNge5bed4D7JR1ruU= github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.0 h1:Ndr4YhSshyQT9rkOZ+o3xNAEz77/AY9klVOwULGFli4= github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.0/go.mod h1:2j9rwRfb5qUs9PEJ3W331W84kRaNge5bed4D7JR1ruU= +github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.1 h1:F5LgixmKnsYBduOAScsmLyWtZpNuKkqJkafspzeYg6w= +github.com/OctopusDeploy/go-octopusdeploy/v2 v2.16.1/go.mod h1:2j9rwRfb5qUs9PEJ3W331W84kRaNge5bed4D7JR1ruU= github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E= github.com/briandowns/spinner v1.19.0/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= From 61ed16c2e3c33ac9db433ecb3b861b989c153691 Mon Sep 17 00:00:00 2001 From: Ben Pearce Date: Fri, 18 Nov 2022 15:25:44 +1000 Subject: [PATCH 10/10] extracted constants --- pkg/cmd/target/delete/delete.go | 8 +++++--- pkg/cmd/worker/delete/delete.go | 11 +++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pkg/cmd/target/delete/delete.go b/pkg/cmd/target/delete/delete.go index 280aa3ef..4061a678 100644 --- a/pkg/cmd/target/delete/delete.go +++ b/pkg/cmd/target/delete/delete.go @@ -15,6 +15,8 @@ import ( "github.com/spf13/cobra" ) +const resourceDescription = "deployment target" + type DeleteOptions struct { *cmd.Dependencies *shared.GetTargetsOptions @@ -66,7 +68,7 @@ func NewCmdDelete(f factory.Factory) *cobra.Command { } if !skipConfirmation { // TODO NO_PROMPT env var or whatever we do there - return question.DeleteWithConfirmation(f.Ask, "deployment target", itemToDelete.Name, itemToDelete.GetID(), func() error { + return question.DeleteWithConfirmation(f.Ask, resourceDescription, itemToDelete.Name, itemToDelete.GetID(), func() error { return delete(opts.Client, itemToDelete) }) } @@ -75,7 +77,7 @@ func NewCmdDelete(f factory.Factory) *cobra.Command { }, } - question.RegisterConfirmDeletionFlag(cmd, &skipConfirmation, "deployment target") + question.RegisterConfirmDeletionFlag(cmd, &skipConfirmation, resourceDescription) return cmd } @@ -86,7 +88,7 @@ func deleteRun(opts *DeleteOptions) error { return err } - return question.DeleteWithConfirmation(opts.Ask, "deployment target", itemToDelete.Name, itemToDelete.GetID(), func() error { + return question.DeleteWithConfirmation(opts.Ask, resourceDescription, itemToDelete.Name, itemToDelete.GetID(), func() error { return delete(opts.Client, itemToDelete) }) } diff --git a/pkg/cmd/worker/delete/delete.go b/pkg/cmd/worker/delete/delete.go index 1b29f458..0b731c45 100644 --- a/pkg/cmd/worker/delete/delete.go +++ b/pkg/cmd/worker/delete/delete.go @@ -15,6 +15,8 @@ import ( "github.com/spf13/cobra" ) +const resourceDescription = "worker" + type DeleteOptions struct { *cmd.Dependencies *shared.GetWorkersOptions @@ -29,6 +31,7 @@ func NewDeleteOptions(dependencies *cmd.Dependencies) *DeleteOptions { func NewCmdDelete(f factory.Factory) *cobra.Command { var skipConfirmation bool + cmd := &cobra.Command{ Use: "delete { | }", Short: "Delete a worker in an instance of Octopus Deploy", @@ -58,7 +61,7 @@ func NewCmdDelete(f factory.Factory) *cobra.Command { } if !skipConfirmation { // TODO NO_PROMPT env var or whatever we do there - return question.DeleteWithConfirmation(f.Ask, "deployment target", worker.Name, worker.GetID(), func() error { + return question.DeleteWithConfirmation(f.Ask, resourceDescription, worker.Name, worker.GetID(), func() error { return delete(opts.Client, worker) }) } @@ -67,18 +70,18 @@ func NewCmdDelete(f factory.Factory) *cobra.Command { }, } - question.RegisterConfirmDeletionFlag(cmd, &skipConfirmation, "deployment target") + question.RegisterConfirmDeletionFlag(cmd, &skipConfirmation, resourceDescription) return cmd } func deleteRun(opts *DeleteOptions) error { - worker, err := selectors.Select(opts.Ask, "Select the worker you wish to delete:", opts.GetWorkersCallback, func(target *machines.Worker) string { return target.Name }) + worker, err := selectors.Select(opts.Ask, "Select the worker you wish to delete:", opts.GetWorkersCallback, func(worker *machines.Worker) string { return worker.Name }) if err != nil { return err } - return question.DeleteWithConfirmation(opts.Ask, "worker", worker.Name, worker.GetID(), func() error { + return question.DeleteWithConfirmation(opts.Ask, resourceDescription, worker.Name, worker.GetID(), func() error { return delete(opts.Client, worker) }) }