From e9f53820505779c43c3a48f165c71d0319eb86d0 Mon Sep 17 00:00:00 2001 From: penghaoh Date: Mon, 15 Mar 2021 16:54:37 -0700 Subject: [PATCH 1/2] feat(cli): allow to reject any ssm plugin install/update --- internal/pkg/cli/exec.go | 1 + internal/pkg/cli/flag.go | 1 + internal/pkg/cli/svc_exec.go | 19 ++++++++++++++----- internal/pkg/cli/svc_exec_test.go | 30 ++++++++++++++++++++++-------- internal/pkg/cli/task_exec.go | 10 ++++++++-- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/internal/pkg/cli/exec.go b/internal/pkg/cli/exec.go index e428dcba436..ec5b0d8121d 100644 --- a/internal/pkg/cli/exec.go +++ b/internal/pkg/cli/exec.go @@ -15,4 +15,5 @@ type execVars struct { taskID string containerName string skipConfirmation bool + yes *bool } diff --git a/internal/pkg/cli/flag.go b/internal/pkg/cli/flag.go index 15341fc11fb..90ccc26af63 100644 --- a/internal/pkg/cli/flag.go +++ b/internal/pkg/cli/flag.go @@ -155,6 +155,7 @@ const ( pipelineFlagDescription = "Name of the pipeline." profileFlagDescription = "Name of the profile." yesFlagDescription = "Skips confirmation prompt." + execYesFlagDescription = "Optional. Whether to install/update the Session Manager Plugin." jsonFlagDescription = "Optional. Outputs in JSON format." imageTagFlagDescription = `Optional. The container image tag.` diff --git a/internal/pkg/cli/svc_exec.go b/internal/pkg/cli/svc_exec.go index cd05efd3f46..021b3aa7905 100644 --- a/internal/pkg/cli/svc_exec.go +++ b/internal/pkg/cli/svc_exec.go @@ -100,7 +100,7 @@ func (o *svcExecOpts) Validate() error { return err } } - return validateSSMBinary(o.prompter, o.ssmPluginManager, o.skipConfirmation) + return validateSSMBinary(o.prompter, o.ssmPluginManager, o.yes) } // Ask asks for fields that are required but not passed in. @@ -207,7 +207,10 @@ func (o *svcExecOpts) selectContainer() string { return o.name } -func validateSSMBinary(prompt prompter, manager ssmPluginManager, skipConfirmation bool) error { +func validateSSMBinary(prompt prompter, manager ssmPluginManager, yes *bool) error { + if yes != nil && !aws.BoolValue(yes) { + return nil + } err := manager.ValidateBinary() if err == nil { return nil @@ -215,7 +218,7 @@ func validateSSMBinary(prompt prompter, manager ssmPluginManager, skipConfirmati switch v := err.(type) { case *exec.ErrSSMPluginNotExist: // If ssm plugin is not install, prompt users to install the plugin. - if !skipConfirmation { + if yes == nil { confirmInstall, err := prompt.Confirm(ssmPluginInstallPrompt, ssmPluginInstallPromptHelp) if err != nil { return fmt.Errorf("prompt to confirm installing the plugin: %w", err) @@ -230,7 +233,7 @@ func validateSSMBinary(prompt prompter, manager ssmPluginManager, skipConfirmati return nil case *exec.ErrOutdatedSSMPlugin: // If ssm plugin is not up to date, prompt users to update the plugin. - if !skipConfirmation { + if yes == nil { confirmUpdate, err := prompt.Confirm( fmt.Sprintf(ssmPluginUpdatePrompt, v.CurrentVersion, v.LatestVersion), "") if err != nil { @@ -271,6 +274,12 @@ func buildSvcExecCmd() *cobra.Command { if err != nil { return err } + if cmd.Flags().Changed(yesFlag) { + opts.yes = aws.Bool(false) + if opts.skipConfirmation { + opts.yes = aws.Bool(true) + } + } if err := opts.Validate(); err != nil { return err } @@ -286,7 +295,7 @@ func buildSvcExecCmd() *cobra.Command { cmd.Flags().StringVarP(&vars.command, commandFlag, commandFlagShort, defaultCommand, execCommandFlagDescription) cmd.Flags().StringVar(&vars.taskID, taskIDFlag, "", taskIDFlagDescription) cmd.Flags().StringVar(&vars.containerName, containerFlag, "", containerFlagDescription) - cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, yesFlagDescription) + cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, execYesFlagDescription) cmd.SetUsageTemplate(template.Usage) return cmd diff --git a/internal/pkg/cli/svc_exec_test.go b/internal/pkg/cli/svc_exec_test.go index 5689d299a03..0f1a9ccef9e 100644 --- a/internal/pkg/cli/svc_exec_test.go +++ b/internal/pkg/cli/svc_exec_test.go @@ -37,8 +37,8 @@ func TestSvcExec_Validate(t *testing.T) { ) mockErr := errors.New("some error") testCases := map[string]struct { - skipConfirmation bool - setupMocks func(mocks execSvcMocks) + yes *bool + setupMocks func(mocks execSvcMocks) wantedError error }{ @@ -76,6 +76,20 @@ func TestSvcExec_Validate(t *testing.T) { wantedError: fmt.Errorf("some error"), }, + "skip without installing/updating if yes flag is set to be false": { + yes: aws.Bool(false), + setupMocks: func(m execSvcMocks) { + gomock.InOrder( + m.storeSvc.EXPECT().GetApplication("my-app").Return(&config.Application{ + Name: "my-app", + }, nil), + m.storeSvc.EXPECT().GetEnvironment("my-app", "my-env").Return(&config.Environment{ + Name: "my-env", + }, nil), + m.storeSvc.EXPECT().GetService("my-app", "my-svc").Return(&config.Workload{}, nil), + ) + }, + }, "should bubble error if cannot validate ssm plugin": { setupMocks: func(m execSvcMocks) { gomock.InOrder( @@ -251,8 +265,8 @@ func TestSvcExec_Validate(t *testing.T) { wantedError: nil, }, - "valid case with ssm plugin updating and skip confirming": { - skipConfirmation: true, + "valid case with ssm plugin updating and skip confirming to install": { + yes: aws.Bool(true), setupMocks: func(m execSvcMocks) { gomock.InOrder( m.storeSvc.EXPECT().GetApplication("my-app").Return(&config.Application{ @@ -294,10 +308,10 @@ func TestSvcExec_Validate(t *testing.T) { execSvcs := &svcExecOpts{ execVars: execVars{ - name: inputSvc, - appName: inputApp, - envName: inputEnv, - skipConfirmation: tc.skipConfirmation, + name: inputSvc, + appName: inputApp, + envName: inputEnv, + yes: tc.yes, }, store: mockStoreReader, ssmPluginManager: mockSSMPluginManager, diff --git a/internal/pkg/cli/task_exec.go b/internal/pkg/cli/task_exec.go index 6e0ef92b7c8..b1ae1e10bc5 100644 --- a/internal/pkg/cli/task_exec.go +++ b/internal/pkg/cli/task_exec.go @@ -89,7 +89,7 @@ func (o *taskExecOpts) Validate() error { return err } } - return validateSSMBinary(o.prompter, o.ssmPluginManager, o.skipConfirmation) + return validateSSMBinary(o.prompter, o.ssmPluginManager, o.yes) } // Ask asks for fields that are required but not passed in. @@ -208,6 +208,12 @@ func buildTaskExecCmd() *cobra.Command { if err != nil { return err } + if cmd.Flags().Changed(yesFlag) { + opts.yes = aws.Bool(false) + if opts.skipConfirmation { + opts.yes = aws.Bool(true) + } + } if err := opts.Validate(); err != nil { return err } @@ -223,7 +229,7 @@ func buildTaskExecCmd() *cobra.Command { cmd.Flags().StringVarP(&vars.command, commandFlag, commandFlagShort, defaultCommand, execCommandFlagDescription) cmd.Flags().StringVar(&vars.taskID, taskIDFlag, "", taskIDFlagDescription) cmd.Flags().BoolVar(&vars.useDefault, taskDefaultFlag, false, taskExecDefaultFlagDescription) - cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, yesFlagDescription) + cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, execYesFlagDescription) cmd.SetUsageTemplate(template.Usage) return cmd From cd407b9526a33c39fcd4d515e6fc2c73833b83d5 Mon Sep 17 00:00:00 2001 From: penghaoh Date: Mon, 15 Mar 2021 18:11:07 -0700 Subject: [PATCH 2/2] Address feedback --- internal/pkg/cli/exec.go | 3 +-- internal/pkg/cli/flag.go | 2 +- internal/pkg/cli/svc_exec.go | 19 ++++++++++--------- internal/pkg/cli/svc_exec_test.go | 16 ++++++++-------- internal/pkg/cli/task_exec.go | 11 ++++++----- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/internal/pkg/cli/exec.go b/internal/pkg/cli/exec.go index ec5b0d8121d..41a9553e7a7 100644 --- a/internal/pkg/cli/exec.go +++ b/internal/pkg/cli/exec.go @@ -14,6 +14,5 @@ type execVars struct { command string taskID string containerName string - skipConfirmation bool - yes *bool + skipConfirmation *bool // If nil, we will prompt to upgrade the ssm plugin. } diff --git a/internal/pkg/cli/flag.go b/internal/pkg/cli/flag.go index 90ccc26af63..3c3ea7d10b8 100644 --- a/internal/pkg/cli/flag.go +++ b/internal/pkg/cli/flag.go @@ -155,7 +155,7 @@ const ( pipelineFlagDescription = "Name of the pipeline." profileFlagDescription = "Name of the profile." yesFlagDescription = "Skips confirmation prompt." - execYesFlagDescription = "Optional. Whether to install/update the Session Manager Plugin." + execYesFlagDescription = "Optional. Whether to update the Session Manager Plugin." jsonFlagDescription = "Optional. Outputs in JSON format." imageTagFlagDescription = `Optional. The container image tag.` diff --git a/internal/pkg/cli/svc_exec.go b/internal/pkg/cli/svc_exec.go index 021b3aa7905..0d6e44ebae0 100644 --- a/internal/pkg/cli/svc_exec.go +++ b/internal/pkg/cli/svc_exec.go @@ -100,7 +100,7 @@ func (o *svcExecOpts) Validate() error { return err } } - return validateSSMBinary(o.prompter, o.ssmPluginManager, o.yes) + return validateSSMBinary(o.prompter, o.ssmPluginManager, o.skipConfirmation) } // Ask asks for fields that are required but not passed in. @@ -207,8 +207,8 @@ func (o *svcExecOpts) selectContainer() string { return o.name } -func validateSSMBinary(prompt prompter, manager ssmPluginManager, yes *bool) error { - if yes != nil && !aws.BoolValue(yes) { +func validateSSMBinary(prompt prompter, manager ssmPluginManager, skipConfirmation *bool) error { + if skipConfirmation != nil && !aws.BoolValue(skipConfirmation) { return nil } err := manager.ValidateBinary() @@ -218,7 +218,7 @@ func validateSSMBinary(prompt prompter, manager ssmPluginManager, yes *bool) err switch v := err.(type) { case *exec.ErrSSMPluginNotExist: // If ssm plugin is not install, prompt users to install the plugin. - if yes == nil { + if skipConfirmation == nil { confirmInstall, err := prompt.Confirm(ssmPluginInstallPrompt, ssmPluginInstallPromptHelp) if err != nil { return fmt.Errorf("prompt to confirm installing the plugin: %w", err) @@ -233,7 +233,7 @@ func validateSSMBinary(prompt prompter, manager ssmPluginManager, yes *bool) err return nil case *exec.ErrOutdatedSSMPlugin: // If ssm plugin is not up to date, prompt users to update the plugin. - if yes == nil { + if skipConfirmation == nil { confirmUpdate, err := prompt.Confirm( fmt.Sprintf(ssmPluginUpdatePrompt, v.CurrentVersion, v.LatestVersion), "") if err != nil { @@ -261,6 +261,7 @@ https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-wor // buildSvcExecCmd builds the command for execute a running container in a service. func buildSvcExecCmd() *cobra.Command { vars := execVars{} + var skipPrompt bool cmd := &cobra.Command{ Use: "exec", Short: "Execute a command in a running container part of a service.", @@ -275,9 +276,9 @@ func buildSvcExecCmd() *cobra.Command { return err } if cmd.Flags().Changed(yesFlag) { - opts.yes = aws.Bool(false) - if opts.skipConfirmation { - opts.yes = aws.Bool(true) + opts.skipConfirmation = aws.Bool(false) + if skipPrompt { + opts.skipConfirmation = aws.Bool(true) } } if err := opts.Validate(); err != nil { @@ -295,7 +296,7 @@ func buildSvcExecCmd() *cobra.Command { cmd.Flags().StringVarP(&vars.command, commandFlag, commandFlagShort, defaultCommand, execCommandFlagDescription) cmd.Flags().StringVar(&vars.taskID, taskIDFlag, "", taskIDFlagDescription) cmd.Flags().StringVar(&vars.containerName, containerFlag, "", containerFlagDescription) - cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, execYesFlagDescription) + cmd.Flags().BoolVar(&skipPrompt, yesFlag, false, execYesFlagDescription) cmd.SetUsageTemplate(template.Usage) return cmd diff --git a/internal/pkg/cli/svc_exec_test.go b/internal/pkg/cli/svc_exec_test.go index 0f1a9ccef9e..dd270784183 100644 --- a/internal/pkg/cli/svc_exec_test.go +++ b/internal/pkg/cli/svc_exec_test.go @@ -37,8 +37,8 @@ func TestSvcExec_Validate(t *testing.T) { ) mockErr := errors.New("some error") testCases := map[string]struct { - yes *bool - setupMocks func(mocks execSvcMocks) + skipConfirmation *bool + setupMocks func(mocks execSvcMocks) wantedError error }{ @@ -77,7 +77,7 @@ func TestSvcExec_Validate(t *testing.T) { wantedError: fmt.Errorf("some error"), }, "skip without installing/updating if yes flag is set to be false": { - yes: aws.Bool(false), + skipConfirmation: aws.Bool(false), setupMocks: func(m execSvcMocks) { gomock.InOrder( m.storeSvc.EXPECT().GetApplication("my-app").Return(&config.Application{ @@ -266,7 +266,7 @@ func TestSvcExec_Validate(t *testing.T) { wantedError: nil, }, "valid case with ssm plugin updating and skip confirming to install": { - yes: aws.Bool(true), + skipConfirmation: aws.Bool(true), setupMocks: func(m execSvcMocks) { gomock.InOrder( m.storeSvc.EXPECT().GetApplication("my-app").Return(&config.Application{ @@ -308,10 +308,10 @@ func TestSvcExec_Validate(t *testing.T) { execSvcs := &svcExecOpts{ execVars: execVars{ - name: inputSvc, - appName: inputApp, - envName: inputEnv, - yes: tc.yes, + name: inputSvc, + appName: inputApp, + envName: inputEnv, + skipConfirmation: tc.skipConfirmation, }, store: mockStoreReader, ssmPluginManager: mockSSMPluginManager, diff --git a/internal/pkg/cli/task_exec.go b/internal/pkg/cli/task_exec.go index b1ae1e10bc5..fcfc064a28d 100644 --- a/internal/pkg/cli/task_exec.go +++ b/internal/pkg/cli/task_exec.go @@ -89,7 +89,7 @@ func (o *taskExecOpts) Validate() error { return err } } - return validateSSMBinary(o.prompter, o.ssmPluginManager, o.yes) + return validateSSMBinary(o.prompter, o.ssmPluginManager, o.skipConfirmation) } // Ask asks for fields that are required but not passed in. @@ -192,6 +192,7 @@ func (o *taskExecOpts) configSession() (*session.Session, error) { // buildTaskExecCmd builds the command for execute a running container in a one-off task. func buildTaskExecCmd() *cobra.Command { + var skipPrompt bool vars := taskExecVars{} cmd := &cobra.Command{ Use: "exec", @@ -209,9 +210,9 @@ func buildTaskExecCmd() *cobra.Command { return err } if cmd.Flags().Changed(yesFlag) { - opts.yes = aws.Bool(false) - if opts.skipConfirmation { - opts.yes = aws.Bool(true) + opts.skipConfirmation = aws.Bool(false) + if skipPrompt { + opts.skipConfirmation = aws.Bool(true) } } if err := opts.Validate(); err != nil { @@ -229,7 +230,7 @@ func buildTaskExecCmd() *cobra.Command { cmd.Flags().StringVarP(&vars.command, commandFlag, commandFlagShort, defaultCommand, execCommandFlagDescription) cmd.Flags().StringVar(&vars.taskID, taskIDFlag, "", taskIDFlagDescription) cmd.Flags().BoolVar(&vars.useDefault, taskDefaultFlag, false, taskExecDefaultFlagDescription) - cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, execYesFlagDescription) + cmd.Flags().BoolVar(&skipPrompt, yesFlag, false, execYesFlagDescription) cmd.SetUsageTemplate(template.Usage) return cmd