From e054dfb07ad1dc9cada5e972c5f95ca6a58514a2 Mon Sep 17 00:00:00 2001 From: Dane Elwell Date: Tue, 12 Nov 2024 17:01:29 +0000 Subject: [PATCH] Add support for backup gateways and agent-level backups --- cmd/ecloud/ecloud.go | 1 + cmd/ecloud/ecloud_backupgateway.go | 236 ++++++++++++++ cmd/ecloud/ecloud_backupgateway_specs.go | 85 +++++ cmd/ecloud/ecloud_backupgateway_specs_test.go | 121 +++++++ cmd/ecloud/ecloud_backupgateway_test.go | 303 ++++++++++++++++++ cmd/ecloud/ecloud_instance.go | 10 + cmd/ecloud/ecloud_instance_test.go | 46 +++ cmd/ecloud/ecloud_vpngateway_user_test.go | 3 +- cmd/ecloud/output.go | 8 + go.mod | 2 +- test/mocks/mock_ecloudservice.go | 137 +++++++- 11 files changed, 948 insertions(+), 4 deletions(-) create mode 100644 cmd/ecloud/ecloud_backupgateway.go create mode 100644 cmd/ecloud/ecloud_backupgateway_specs.go create mode 100644 cmd/ecloud/ecloud_backupgateway_specs_test.go create mode 100644 cmd/ecloud/ecloud_backupgateway_test.go diff --git a/cmd/ecloud/ecloud.go b/cmd/ecloud/ecloud.go index 7f03810..5970803 100644 --- a/cmd/ecloud/ecloud.go +++ b/cmd/ecloud/ecloud.go @@ -74,6 +74,7 @@ func ECloudRootCmd(f factory.ClientFactory, fs afero.Fs) *cobra.Command { cmd.AddCommand(ecloudAffinityRuleRootCmd(f)) cmd.AddCommand(ecloudAffinityRuleMemberRootCmd(f)) cmd.AddCommand(ecloudResourceTierRootCmd(f)) + cmd.AddCommand(ecloudBackupGatewayRootCmd(f)) } return cmd diff --git a/cmd/ecloud/ecloud_backupgateway.go b/cmd/ecloud/ecloud_backupgateway.go new file mode 100644 index 0000000..59069f4 --- /dev/null +++ b/cmd/ecloud/ecloud_backupgateway.go @@ -0,0 +1,236 @@ +package ecloud + +import ( + "errors" + "fmt" + + "github.com/ans-group/cli/internal/pkg/factory" + "github.com/ans-group/cli/internal/pkg/helper" + "github.com/ans-group/cli/internal/pkg/output" + "github.com/ans-group/sdk-go/pkg/service/ecloud" + "github.com/spf13/cobra" +) + +func ecloudBackupGatewayRootCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "backupgateway", + Short: "sub-commands relating to backup gateways", + } + + // Child commands + cmd.AddCommand(ecloudBackupGatewaySpecificationRootCmd(f)) + cmd.AddCommand(ecloudBackupGatewayListCmd(f)) + cmd.AddCommand(ecloudBackupGatewayShowCmd(f)) + cmd.AddCommand(ecloudBackupGatewayCreateCmd(f)) + cmd.AddCommand(ecloudBackupGatewayUpdateCmd(f)) + cmd.AddCommand(ecloudBackupGatewayDeleteCmd(f)) + + return cmd +} + +func ecloudBackupGatewayListCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "Lists backup gateways", + Example: "ans ecloud backupgateway list", + RunE: ecloudCobraRunEFunc(f, ecloudBackupGatewayList), + } + + cmd.Flags().String("name", "", "Backup gateway name for filtering") + + return cmd +} + +func ecloudBackupGatewayList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + params, err := helper.GetAPIRequestParametersFromFlags(cmd, + helper.NewStringFilterFlagOption("name", "name"), + ) + if err != nil { + return err + } + + gateways, err := service.GetBackupGateways(params) + if err != nil { + return fmt.Errorf("Error retrieving backup gateways: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudBackupGatewaysProvider(gateways)) +} + +func ecloudBackupGatewayShowCmd(f factory.ClientFactory) *cobra.Command { + return &cobra.Command{ + Use: "show ...", + Short: "Show details of a backup gateway", + Example: "ans ecloud backupgateway show bgw-abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing backup gateway ID") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudBackupGatewayShow), + } +} + +func ecloudBackupGatewayShow(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + var backupGateways []ecloud.BackupGateway + for _, arg := range args { + backupGateway, err := service.GetBackupGateway(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving backup gateway [%s]: %s", arg, err) + continue + } + + backupGateways = append(backupGateways, backupGateway) + } + + return output.CommandOutput(cmd, OutputECloudBackupGatewaysProvider(backupGateways)) +} + +func ecloudBackupGatewayCreateCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "create", + Short: "Creates a backup gateway", + Example: "ans ecloud backupgateway create --router rtr-abcdef12 --vpc vpc-abcd1234 --specification bgws-abcdef12", + RunE: ecloudCobraRunEFunc(f, ecloudBackupGatewayCreate), + } + + // Setup flags + cmd.Flags().String("name", "", "Name of gateway") + cmd.Flags().String("vpc", "", "ID of VPC") + cmd.MarkFlagRequired("vpc") + cmd.Flags().String("router", "", "ID of router") + cmd.MarkFlagRequired("router") + cmd.Flags().String("specification", "", "ID of backup gateway specification") + cmd.MarkFlagRequired("specification") + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the backup gateway has been completely created") + + return cmd +} + +func ecloudBackupGatewayCreate(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + createRequest := ecloud.CreateBackupGatewayRequest{} + createRequest.Name, _ = cmd.Flags().GetString("name") + createRequest.VPCID, _ = cmd.Flags().GetString("vpc") + createRequest.RouterID, _ = cmd.Flags().GetString("router") + createRequest.GatewaySpecID, _ = cmd.Flags().GetString("specification") + + taskRef, err := service.CreateBackupGateway(createRequest) + if err != nil { + return fmt.Errorf("Error creating backup gateway: %s", err) + } + + waitFlag, _ := cmd.Flags().GetBool("wait") + if waitFlag { + err := helper.WaitForCommand(TaskStatusWaitFunc(service, taskRef.TaskID, ecloud.TaskStatusComplete)) + if err != nil { + return fmt.Errorf("Error waiting for backup gateway task to complete: %s", err) + } + } + + backupGateway, err := service.GetBackupGateway(taskRef.ResourceID) + if err != nil { + return fmt.Errorf("Error retrieving new backup gateway: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudBackupGatewaysProvider([]ecloud.BackupGateway{backupGateway})) +} + +func ecloudBackupGatewayUpdateCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "update ...", + Short: "Updates a backup gateway", + Long: "Update the name of a backup gateway", + Example: "ans ecloud backupgateway update bgw-abcdef12 --name \"my gateway\"", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing backup gateway") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudBackupGatewayUpdate), + } + + cmd.Flags().String("name", "", "Name of gateway") + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the backup gateway has been completely updated") + + return cmd +} + +func ecloudBackupGatewayUpdate(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + patchRequest := ecloud.PatchBackupGatewayRequest{} + + if cmd.Flags().Changed("name") { + patchRequest.Name, _ = cmd.Flags().GetString("name") + } + + var backupGateways []ecloud.BackupGateway + for _, arg := range args { + task, err := service.PatchBackupGateway(arg, patchRequest) + if err != nil { + output.OutputWithErrorLevelf("Error updating backup gateway [%s]: %s", arg, err) + continue + } + + waitFlag, _ := cmd.Flags().GetBool("wait") + if waitFlag { + err := helper.WaitForCommand(TaskStatusWaitFunc(service, task.TaskID, ecloud.TaskStatusComplete)) + if err != nil { + output.OutputWithErrorLevelf("Error waiting for task to complete for backup gateway [%s]: %s", arg, err) + continue + } + } + + backupGateway, err := service.GetBackupGateway(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving updated backup gateway [%s]: %s", arg, err) + continue + } + + backupGateways = append(backupGateways, backupGateway) + } + + return output.CommandOutput(cmd, OutputECloudBackupGatewaysProvider(backupGateways)) +} + +func ecloudBackupGatewayDeleteCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ...", + Short: "Removes a backup gateway", + Example: "ans ecloud backupgateway delete bgw-abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing backup gateway") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudBackupGatewayDelete), + } + + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the backup gateway has been completely removed") + + return cmd +} + +func ecloudBackupGatewayDelete(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + for _, arg := range args { + taskID, err := service.DeleteBackupGateway(arg) + if err != nil { + output.OutputWithErrorLevelf("Error removing backup gateway [%s]: %s", arg, err) + continue + } + + waitFlag, _ := cmd.Flags().GetBool("wait") + if waitFlag { + err := helper.WaitForCommand(TaskStatusWaitFunc(service, taskID, ecloud.TaskStatusComplete)) + if err != nil { + output.OutputWithErrorLevelf("Error waiting for task to complete for backup gateway [%s]: %s", arg, err) + continue + } + } + } + return nil +} diff --git a/cmd/ecloud/ecloud_backupgateway_specs.go b/cmd/ecloud/ecloud_backupgateway_specs.go new file mode 100644 index 0000000..d3e6e6d --- /dev/null +++ b/cmd/ecloud/ecloud_backupgateway_specs.go @@ -0,0 +1,85 @@ +package ecloud + +import ( + "errors" + "fmt" + + "github.com/ans-group/cli/internal/pkg/factory" + "github.com/ans-group/cli/internal/pkg/helper" + "github.com/ans-group/cli/internal/pkg/output" + "github.com/ans-group/sdk-go/pkg/service/ecloud" + "github.com/spf13/cobra" +) + +func ecloudBackupGatewaySpecificationRootCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "spec", + Short: "sub-commands relating to backup gateway specifications", + } + + // Child commands + cmd.AddCommand(ecloudBackupGatewaySpecificationListCmd(f)) + cmd.AddCommand(ecloudBackupGatewaySpecificationShowCmd(f)) + + return cmd +} + +func ecloudBackupGatewaySpecificationListCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "Lists Backup gateway specifications", + Example: "ans ecloud backupgateway spec list", + RunE: ecloudCobraRunEFunc(f, ecloudBackupGatewaySpecificationList), + } + + cmd.Flags().String("name", "", "Backup gateway specification name for filtering") + + return cmd +} + +func ecloudBackupGatewaySpecificationList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + params, err := helper.GetAPIRequestParametersFromFlags(cmd, + helper.NewStringFilterFlagOption("name", "name"), + ) + if err != nil { + return err + } + + specs, err := service.GetBackupGatewaySpecifications(params) + if err != nil { + return fmt.Errorf("Error retrieving backup gateway specifications: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudBackupGatewaySpecificationsProvider(specs)) +} + +func ecloudBackupGatewaySpecificationShowCmd(f factory.ClientFactory) *cobra.Command { + return &cobra.Command{ + Use: "show ...", + Short: "Show details of a backup gateway specification", + Example: "ans ecloud backupgateway spec show bgws-abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing backup gateway specification") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudBackupGatewaySpecificationShow), + } +} + +func ecloudBackupGatewaySpecificationShow(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + var specs []ecloud.BackupGatewaySpecification + for _, arg := range args { + spec, err := service.GetBackupGatewaySpecification(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving backup gateway specification [%s]: %s", arg, err) + continue + } + + specs = append(specs, spec) + } + + return output.CommandOutput(cmd, OutputECloudBackupGatewaySpecificationsProvider(specs)) +} diff --git a/cmd/ecloud/ecloud_backupgateway_specs_test.go b/cmd/ecloud/ecloud_backupgateway_specs_test.go new file mode 100644 index 0000000..f04e070 --- /dev/null +++ b/cmd/ecloud/ecloud_backupgateway_specs_test.go @@ -0,0 +1,121 @@ +package ecloud + +import ( + "errors" + "testing" + + "github.com/ans-group/cli/internal/pkg/clierrors" + "github.com/ans-group/cli/test/mocks" + "github.com/ans-group/cli/test/test_output" + "github.com/ans-group/sdk-go/pkg/service/ecloud" + "github.com/golang/mock/gomock" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +func Test_ecloudBackupGatewaySpecificationList(t *testing.T) { + t.Run("DefaultRetrieve", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGatewaySpecifications(gomock.Any()).Return([]ecloud.BackupGatewaySpecification{}, nil).Times(1) + + ecloudBackupGatewaySpecificationList(service, &cobra.Command{}, []string{}) + }) + + t.Run("MalformedFlag_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := &cobra.Command{} + cmd.Flags().StringArray("filter", []string{"invalidfilter"}, "") + + err := ecloudBackupGatewaySpecificationList(service, cmd, []string{}) + + assert.IsType(t, &clierrors.ErrInvalidFlagValue{}, err) + }) + + t.Run("GetBackupGatewaySpecificationsError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGatewaySpecifications(gomock.Any()).Return([]ecloud.BackupGatewaySpecification{}, errors.New("test error")).Times(1) + + err := ecloudBackupGatewaySpecificationList(service, &cobra.Command{}, []string{}) + + assert.Equal(t, "Error retrieving backup gateway specifications: test error", err.Error()) + }) +} + +func Test_ecloudBackupGatewaySpecificationShowCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudBackupGatewaySpecificationShowCmd(nil).Args(nil, []string{"bgws-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudBackupGatewaySpecificationShowCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing backup gateway specification", err.Error()) + }) +} + +func Test_ecloudBackupGatewaySpecificationShow(t *testing.T) { + t.Run("SingleSpec", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGatewaySpecification("bgws-abcdef12").Return(ecloud.BackupGatewaySpecification{}, nil).Times(1) + + ecloudBackupGatewaySpecificationShow(service, &cobra.Command{}, []string{"bgws-abcdef12"}) + }) + + t.Run("MultipleSpecs", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().GetBackupGatewaySpecification("bgws-abcdef12").Return(ecloud.BackupGatewaySpecification{}, nil), + service.EXPECT().GetBackupGatewaySpecification("bgws-abcdef23").Return(ecloud.BackupGatewaySpecification{}, nil), + ) + + ecloudBackupGatewaySpecificationShow(service, &cobra.Command{}, []string{"bgws-abcdef12", "bgws-abcdef23"}) + }) + + t.Run("GetBackupGatewaySpecificationError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGatewaySpecification("bgws-abcdef12").Return(ecloud.BackupGatewaySpecification{}, errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error retrieving backup gateway specification [bgws-abcdef12]: test error\n", func() { + ecloudBackupGatewaySpecificationShow(service, &cobra.Command{}, []string{"bgws-abcdef12"}) + }) + }) + + t.Run("NotFound_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGatewaySpecification("bgws-abcdef12").Return(ecloud.BackupGatewaySpecification{}, &ecloud.BackupGatewaySpecificationNotFoundError{ID: "bgws-abcdef12"}) + + test_output.AssertErrorOutput(t, "Error retrieving backup gateway specification [bgws-abcdef12]: Backup gateway specification not found with ID [bgws-abcdef12]\n", func() { + ecloudBackupGatewaySpecificationShow(service, &cobra.Command{}, []string{"bgws-abcdef12"}) + }) + }) +} diff --git a/cmd/ecloud/ecloud_backupgateway_test.go b/cmd/ecloud/ecloud_backupgateway_test.go new file mode 100644 index 0000000..e08b297 --- /dev/null +++ b/cmd/ecloud/ecloud_backupgateway_test.go @@ -0,0 +1,303 @@ +package ecloud + +import ( + "errors" + "testing" + + "github.com/ans-group/cli/internal/pkg/clierrors" + "github.com/ans-group/cli/test/mocks" + "github.com/ans-group/cli/test/test_output" + "github.com/ans-group/sdk-go/pkg/service/ecloud" + "github.com/golang/mock/gomock" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +func Test_ecloudBackupGatewayList(t *testing.T) { + t.Run("DefaultRetrieve", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGateways(gomock.Any()).Return([]ecloud.BackupGateway{}, nil).Times(1) + + ecloudBackupGatewayList(service, &cobra.Command{}, []string{}) + }) + + t.Run("MalformedFlag_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := &cobra.Command{} + cmd.Flags().StringArray("filter", []string{"invalidfilter"}, "") + + err := ecloudBackupGatewayList(service, cmd, []string{}) + + assert.IsType(t, &clierrors.ErrInvalidFlagValue{}, err) + }) + + t.Run("GetBackupGatewaysError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGateways(gomock.Any()).Return([]ecloud.BackupGateway{}, errors.New("test error")).Times(1) + + err := ecloudBackupGatewayList(service, &cobra.Command{}, []string{}) + + assert.Equal(t, "Error retrieving backup gateways: test error", err.Error()) + }) +} + +func Test_ecloudBackupGatewayShowCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudBackupGatewayShowCmd(nil).Args(nil, []string{"bgw-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudBackupGatewayShowCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing backup gateway ID", err.Error()) + }) +} + +func Test_ecloudBackupGatewayShow(t *testing.T) { + t.Run("SingleBackupGateway", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGateway("bgw-abcdef12").Return(ecloud.BackupGateway{}, nil).Times(1) + + ecloudBackupGatewayShow(service, &cobra.Command{}, []string{"bgw-abcdef12"}) + }) + + t.Run("MultipleBackupGateways", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().GetBackupGateway("bgw-abcdef12").Return(ecloud.BackupGateway{}, nil), + service.EXPECT().GetBackupGateway("bgw-abcdef23").Return(ecloud.BackupGateway{}, nil), + ) + + ecloudBackupGatewayShow(service, &cobra.Command{}, []string{"bgw-abcdef12", "bgw-abcdef23"}) + }) + + t.Run("GetBackupGatewayError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetBackupGateway("bgw-abcdef12").Return(ecloud.BackupGateway{}, errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error retrieving backup gateway [bgw-abcdef12]: test error\n", func() { + ecloudBackupGatewayShow(service, &cobra.Command{}, []string{"bgw-abcdef12"}) + }) + }) +} + +func Test_ecloudBackupGatewayCreate(t *testing.T) { + t.Run("DefaultCreate", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudBackupGatewayCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgateway", "--vpc=vpc-abcdef12", "--router=rtr-abcdef12", "--specification=bgws-abcdef12"}) + + req := ecloud.CreateBackupGatewayRequest{ + Name: "testgateway", + VPCID: "vpc-abcdef12", + RouterID: "rtr-abcdef12", + GatewaySpecID: "bgws-abcdef12", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "bgw-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateBackupGateway(req).Return(resp, nil), + service.EXPECT().GetBackupGateway("bgw-abcdef12").Return(ecloud.BackupGateway{}, nil), + ) + + ecloudBackupGatewayCreate(service, cmd, []string{}) + }) + + t.Run("CreateWithWaitFlag_NoError_Succeeds", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudBackupGatewayCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgateway", "--vpc=vpc-abcdef12", "--router=rtr-abcdef12", "--specification=bgws-abcdef12", "--wait"}) + + req := ecloud.CreateBackupGatewayRequest{ + Name: "testgateway", + VPCID: "vpc-abcdef12", + RouterID: "rtr-abcdef12", + GatewaySpecID: "bgws-abcdef12", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "bgw-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateBackupGateway(req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil), + service.EXPECT().GetBackupGateway("bgw-abcdef12").Return(ecloud.BackupGateway{}, nil), + ) + + ecloudBackupGatewayCreate(service, cmd, []string{}) + }) + + t.Run("WithWaitFlag_TaskError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudBackupGatewayCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgateway", "--vpc=vpc-abcdef12", "--router=rtr-abcdef12", "--specification=bgws-abcdef12", "--wait"}) + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "bgw-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateBackupGateway(gomock.Any()).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{}, errors.New("test error")), + ) + + err := ecloudBackupGatewayCreate(service, cmd, []string{}) + + assert.Equal(t, "Error waiting for backup gateway task to complete: Error waiting for command: Failed to retrieve task status: test error", err.Error()) + }) + + t.Run("CreateError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudBackupGatewayCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgateway"}) + + service.EXPECT().CreateBackupGateway(gomock.Any()).Return(ecloud.TaskReference{}, errors.New("test error")) + + err := ecloudBackupGatewayCreate(service, cmd, []string{}) + + assert.Equal(t, "Error creating backup gateway: test error", err.Error()) + }) + + t.Run("GetBackupGatewayError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudBackupGatewayCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgateway", "--vpc=vpc-abcdef12", "--router=rtr-abcdef12", "--specification=bgws-abcdef12"}) + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "bgw-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateBackupGateway(gomock.Any()).Return(resp, nil), + service.EXPECT().GetBackupGateway("bgw-abcdef12").Return(ecloud.BackupGateway{}, errors.New("test error")), + ) + + err := ecloudBackupGatewayCreate(service, cmd, []string{}) + + assert.Equal(t, "Error retrieving new backup gateway: test error", err.Error()) + }) +} + +func Test_ecloudBackupGatewayUpdateCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudBackupGatewayUpdateCmd(nil).Args(nil, []string{"bgw-abcdef12"}) + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudBackupGatewayUpdateCmd(nil).Args(nil, []string{}) + assert.NotNil(t, err) + assert.Equal(t, "Missing backup gateway", err.Error()) + }) +} + +func Test_ecloudBackupGatewayDelete(t *testing.T) { + t.Run("SingleBackupGateway", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().DeleteBackupGateway("bgw-abcdef12").Return("task-abcdef12", nil) + + ecloudBackupGatewayDelete(service, &cobra.Command{}, []string{"bgw-abcdef12"}) + }) + + t.Run("WithWaitFlag_NoError_Succeeds", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + cmd := ecloudBackupGatewayDeleteCmd(nil) + cmd.ParseFlags([]string{"--wait"}) + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().DeleteBackupGateway("bgw-abcdef12").Return("task-abcdef12", nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil), + ) + + ecloudBackupGatewayDelete(service, cmd, []string{"bgw-abcdef12"}) + }) + + t.Run("DeleteError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().DeleteBackupGateway("bgw-abcdef12").Return("", errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error removing backup gateway [bgw-abcdef12]: test error\n", func() { + ecloudBackupGatewayDelete(service, &cobra.Command{}, []string{"bgw-abcdef12"}) + }) + }) + + t.Run("WaitFlag_GetTaskError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + cmd := ecloudBackupGatewayDeleteCmd(nil) + cmd.ParseFlags([]string{"--wait"}) + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().DeleteBackupGateway("bgw-abcdef12").Return("task-abcdef12", nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{}, errors.New("test error")), + ) + + test_output.AssertErrorOutput(t, "Error waiting for task to complete for backup gateway [bgw-abcdef12]: Error waiting for command: Failed to retrieve task status: test error\n", func() { + ecloudBackupGatewayDelete(service, cmd, []string{"bgw-abcdef12"}) + }) + }) +} diff --git a/cmd/ecloud/ecloud_instance.go b/cmd/ecloud/ecloud_instance.go index 7dd20b9..60242ed 100644 --- a/cmd/ecloud/ecloud_instance.go +++ b/cmd/ecloud/ecloud_instance.go @@ -140,6 +140,9 @@ func ecloudInstanceCreateCmd(f factory.ClientFactory) *cobra.Command { cmd.Flags().String("host-group", "", "ID of host group to deploy to") cmd.Flags().String("resource-tier", "", "ID of resource tier to deploy to. A default tier is chosen if not specified") cmd.Flags().String("ip-address", "", "IP address to allocate for DHCP") + cmd.Flags().Bool("enable-vm-backups", false, "Enable VM-level backups") + cmd.Flags().Bool("enable-agent-backups", false, "Enable agent-level backups, requires a backup gateway") + cmd.Flags().String("backup-gateway-id", "", "Backup gateway ID, for use with agent level backups") cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the instance has been completely created") return cmd @@ -153,6 +156,9 @@ func ecloudInstanceCreate(service ecloud.ECloudService, cmd *cobra.Command, args createRequest.NetworkID, _ = cmd.Flags().GetString("network") createRequest.HostGroupID, _ = cmd.Flags().GetString("host-group") createRequest.ResourceTierID, _ = cmd.Flags().GetString("resource-tier") + createRequest.BackupEnabled, _ = cmd.Flags().GetBool("enable-vm-backups") + createRequest.BackupAgentEnabled, _ = cmd.Flags().GetBool("enable-agent-backups") + createRequest.BackupGatewayID, _ = cmd.Flags().GetString("backup-gateway-id") createRequest.Name, _ = cmd.Flags().GetString("name") if cmd.Flags().Changed("vcpu") { @@ -176,6 +182,10 @@ func ecloudInstanceCreate(service ecloud.ECloudService, cmd *cobra.Command, args imageFlag, _ := cmd.Flags().GetString("image") + if createRequest.BackupAgentEnabled && createRequest.BackupGatewayID == "" { + return fmt.Errorf("A backup gateway is required to use agent-level backups, please specify a backup gateway ID") + } + if strings.HasPrefix(imageFlag, "img-") { createRequest.ImageID = imageFlag } else { diff --git a/cmd/ecloud/ecloud_instance_test.go b/cmd/ecloud/ecloud_instance_test.go index fa33cab..78ca695 100644 --- a/cmd/ecloud/ecloud_instance_test.go +++ b/cmd/ecloud/ecloud_instance_test.go @@ -291,6 +291,52 @@ func Test_ecloudInstanceCreate(t *testing.T) { assert.Equal(t, "Error retrieving new instance: test error", err.Error()) }) + + t.Run("AgentBackupsWithoutGateway_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudInstanceCreateCmd(nil) + cmd.ParseFlags([]string{ + "--name=testinstance", + "--image=img-abcdef12", + "--enable-agent-backups", + }) + + err := ecloudInstanceCreate(service, cmd, []string{}) + assert.NotNil(t, err) + assert.Equal(t, "A backup gateway is required to use agent-level backups, please specify a backup gateway ID", err.Error()) + }) + + t.Run("AgentBackupsWithGateway_NoError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudInstanceCreateCmd(nil) + cmd.ParseFlags([]string{ + "--name=testinstance", + "--image=img-abcdef12", + "--enable-agent-backups", + "--backup-gateway-id=bgw-abcdef12", + }) + + req := ecloud.CreateInstanceRequest{ + Name: "testinstance", + ImageID: "img-abcdef12", + VCPUSockets: 1, + VCPUCoresPerSocket: 1, + BackupAgentEnabled: true, + BackupGatewayID: "bgw-abcdef12", + } + + service.EXPECT().CreateInstance(req).Return("i-abcdef12", nil) + service.EXPECT().GetInstance("i-abcdef12").Return(ecloud.Instance{}, nil) + + err := ecloudInstanceCreate(service, cmd, []string{}) + assert.Nil(t, err) + }) } func Test_ecloudInstanceUpdateCmd_Args(t *testing.T) { diff --git a/cmd/ecloud/ecloud_vpngateway_user_test.go b/cmd/ecloud/ecloud_vpngateway_user_test.go index 7bf91e3..3cfa92c 100644 --- a/cmd/ecloud/ecloud_vpngateway_user_test.go +++ b/cmd/ecloud/ecloud_vpngateway_user_test.go @@ -254,8 +254,7 @@ func Test_ecloudVPNGatewayUserUpdate(t *testing.T) { cmd.ParseFlags([]string{"--name=testuser", "--username=user1"}) req := ecloud.PatchVPNGatewayUserRequest{ - Name: "testuser", - Username: "user1", + Name: "testuser", } resp := ecloud.TaskReference{ diff --git a/cmd/ecloud/output.go b/cmd/ecloud/output.go index beeb81d..0e42b2a 100644 --- a/cmd/ecloud/output.go +++ b/cmd/ecloud/output.go @@ -495,3 +495,11 @@ func OutputECloudVPNGatewaySpecificationsProvider(specs []ecloud.VPNGatewaySpeci func OutputECloudVPNGatewayUsersProvider(users []ecloud.VPNGatewayUser) output.OutputHandlerDataProvider { return output.NewSerializedOutputHandlerDataProvider(users).WithDefaultFields([]string{"id", "name", "username", "vpn_gateway_id", "sync_status"}) } + +func OutputECloudBackupGatewaySpecificationsProvider(specs []ecloud.BackupGatewaySpecification) output.OutputHandlerDataProvider { + return output.NewSerializedOutputHandlerDataProvider(specs).WithDefaultFields([]string{"id", "name", "description"}) +} + +func OutputECloudBackupGatewaysProvider(gateways []ecloud.BackupGateway) output.OutputHandlerDataProvider { + return output.NewSerializedOutputHandlerDataProvider(gateways).WithDefaultFields([]string{"id", "name", "vpc_id", "availability_zone", "gateway_spec_id", "sync_status"}) +} diff --git a/go.mod b/go.mod index ee5494f..d60d15a 100644 --- a/go.mod +++ b/go.mod @@ -59,4 +59,4 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect ) -// replace github.com/ans-group/sdk-go => ../sdk-go +replace github.com/ans-group/sdk-go => ../sdk-go diff --git a/test/mocks/mock_ecloudservice.go b/test/mocks/mock_ecloudservice.go index b7ea92e..6063ba4 100644 --- a/test/mocks/mock_ecloudservice.go +++ b/test/mocks/mock_ecloudservice.go @@ -5,11 +5,11 @@ package mocks import ( - connection "github.com/ans-group/sdk-go/pkg/connection" account "github.com/ans-group/sdk-go/pkg/service/account" ecloud "github.com/ans-group/sdk-go/pkg/service/ecloud" gomock "github.com/golang/mock/gomock" reflect "reflect" + connection "github.com/ans-group/sdk-go/pkg/connection" ) // MockECloudService is a mock of ECloudService interface. @@ -125,6 +125,21 @@ func (mr *MockECloudServiceMockRecorder) CreateAffinityRuleMember(arg0 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAffinityRuleMember", reflect.TypeOf((*MockECloudService)(nil).CreateAffinityRuleMember), arg0) } +// CreateBackupGateway mocks base method. +func (m *MockECloudService) CreateBackupGateway(arg0 ecloud.CreateBackupGatewayRequest) (ecloud.TaskReference, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateBackupGateway", arg0) + ret0, _ := ret[0].(ecloud.TaskReference) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateBackupGateway indicates an expected call of CreateBackupGateway. +func (mr *MockECloudServiceMockRecorder) CreateBackupGateway(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBackupGateway", reflect.TypeOf((*MockECloudService)(nil).CreateBackupGateway), arg0) +} + // CreateFirewallPolicy mocks base method. func (m *MockECloudService) CreateFirewallPolicy(arg0 ecloud.CreateFirewallPolicyRequest) (ecloud.TaskReference, error) { m.ctrl.T.Helper() @@ -647,6 +662,21 @@ func (mr *MockECloudServiceMockRecorder) DeleteAffinityRuleMember(arg0 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAffinityRuleMember", reflect.TypeOf((*MockECloudService)(nil).DeleteAffinityRuleMember), arg0) } +// DeleteBackupGateway mocks base method. +func (m *MockECloudService) DeleteBackupGateway(arg0 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteBackupGateway", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteBackupGateway indicates an expected call of DeleteBackupGateway. +func (mr *MockECloudServiceMockRecorder) DeleteBackupGateway(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBackupGateway", reflect.TypeOf((*MockECloudService)(nil).DeleteBackupGateway), arg0) +} + // DeleteFirewallPolicy mocks base method. func (m *MockECloudService) DeleteFirewallPolicy(arg0 string) (string, error) { m.ctrl.T.Helper() @@ -1460,6 +1490,96 @@ func (mr *MockECloudServiceMockRecorder) GetAvailabilityZonesPaginated(arg0 inte return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAvailabilityZonesPaginated", reflect.TypeOf((*MockECloudService)(nil).GetAvailabilityZonesPaginated), arg0) } +// GetBackupGateway mocks base method. +func (m *MockECloudService) GetBackupGateway(arg0 string) (ecloud.BackupGateway, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBackupGateway", arg0) + ret0, _ := ret[0].(ecloud.BackupGateway) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBackupGateway indicates an expected call of GetBackupGateway. +func (mr *MockECloudServiceMockRecorder) GetBackupGateway(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBackupGateway", reflect.TypeOf((*MockECloudService)(nil).GetBackupGateway), arg0) +} + +// GetBackupGatewaySpecification mocks base method. +func (m *MockECloudService) GetBackupGatewaySpecification(arg0 string) (ecloud.BackupGatewaySpecification, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBackupGatewaySpecification", arg0) + ret0, _ := ret[0].(ecloud.BackupGatewaySpecification) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBackupGatewaySpecification indicates an expected call of GetBackupGatewaySpecification. +func (mr *MockECloudServiceMockRecorder) GetBackupGatewaySpecification(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBackupGatewaySpecification", reflect.TypeOf((*MockECloudService)(nil).GetBackupGatewaySpecification), arg0) +} + +// GetBackupGatewaySpecifications mocks base method. +func (m *MockECloudService) GetBackupGatewaySpecifications(arg0 connection.APIRequestParameters) ([]ecloud.BackupGatewaySpecification, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBackupGatewaySpecifications", arg0) + ret0, _ := ret[0].([]ecloud.BackupGatewaySpecification) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBackupGatewaySpecifications indicates an expected call of GetBackupGatewaySpecifications. +func (mr *MockECloudServiceMockRecorder) GetBackupGatewaySpecifications(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBackupGatewaySpecifications", reflect.TypeOf((*MockECloudService)(nil).GetBackupGatewaySpecifications), arg0) +} + +// GetBackupGatewaySpecificationsPaginated mocks base method. +func (m *MockECloudService) GetBackupGatewaySpecificationsPaginated(arg0 connection.APIRequestParameters) (*connection.Paginated[ecloud.BackupGatewaySpecification], error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBackupGatewaySpecificationsPaginated", arg0) + ret0, _ := ret[0].(*connection.Paginated[ecloud.BackupGatewaySpecification]) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBackupGatewaySpecificationsPaginated indicates an expected call of GetBackupGatewaySpecificationsPaginated. +func (mr *MockECloudServiceMockRecorder) GetBackupGatewaySpecificationsPaginated(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBackupGatewaySpecificationsPaginated", reflect.TypeOf((*MockECloudService)(nil).GetBackupGatewaySpecificationsPaginated), arg0) +} + +// GetBackupGateways mocks base method. +func (m *MockECloudService) GetBackupGateways(arg0 connection.APIRequestParameters) ([]ecloud.BackupGateway, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBackupGateways", arg0) + ret0, _ := ret[0].([]ecloud.BackupGateway) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBackupGateways indicates an expected call of GetBackupGateways. +func (mr *MockECloudServiceMockRecorder) GetBackupGateways(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBackupGateways", reflect.TypeOf((*MockECloudService)(nil).GetBackupGateways), arg0) +} + +// GetBackupGatewaysPaginated mocks base method. +func (m *MockECloudService) GetBackupGatewaysPaginated(arg0 connection.APIRequestParameters) (*connection.Paginated[ecloud.BackupGateway], error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBackupGatewaysPaginated", arg0) + ret0, _ := ret[0].(*connection.Paginated[ecloud.BackupGateway]) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBackupGatewaysPaginated indicates an expected call of GetBackupGatewaysPaginated. +func (mr *MockECloudServiceMockRecorder) GetBackupGatewaysPaginated(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBackupGatewaysPaginated", reflect.TypeOf((*MockECloudService)(nil).GetBackupGatewaysPaginated), arg0) +} + // GetBillingMetric mocks base method. func (m *MockECloudService) GetBillingMetric(arg0 string) (ecloud.BillingMetric, error) { m.ctrl.T.Helper() @@ -4954,6 +5074,21 @@ func (mr *MockECloudServiceMockRecorder) PatchAffinityRule(arg0, arg1 interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchAffinityRule", reflect.TypeOf((*MockECloudService)(nil).PatchAffinityRule), arg0, arg1) } +// PatchBackupGateway mocks base method. +func (m *MockECloudService) PatchBackupGateway(arg0 string, arg1 ecloud.PatchBackupGatewayRequest) (ecloud.TaskReference, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PatchBackupGateway", arg0, arg1) + ret0, _ := ret[0].(ecloud.TaskReference) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PatchBackupGateway indicates an expected call of PatchBackupGateway. +func (mr *MockECloudServiceMockRecorder) PatchBackupGateway(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchBackupGateway", reflect.TypeOf((*MockECloudService)(nil).PatchBackupGateway), arg0, arg1) +} + // PatchFirewallPolicy mocks base method. func (m *MockECloudService) PatchFirewallPolicy(arg0 string, arg1 ecloud.PatchFirewallPolicyRequest) (ecloud.TaskReference, error) { m.ctrl.T.Helper()