From 03cc778834a02aedcf540abb00a421f119b831b1 Mon Sep 17 00:00:00 2001 From: Lee Spottiswood Date: Tue, 22 Jun 2021 15:15:54 +0100 Subject: [PATCH 1/6] add AZ commands --- cmd/ecloud/ecloud_availabilityzone.go | 91 +++++++++++++++++ cmd/ecloud/ecloud_availabilityzone_test.go | 109 +++++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- 4 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 cmd/ecloud/ecloud_availabilityzone.go create mode 100644 cmd/ecloud/ecloud_availabilityzone_test.go diff --git a/cmd/ecloud/ecloud_availabilityzone.go b/cmd/ecloud/ecloud_availabilityzone.go new file mode 100644 index 0000000..a5e4621 --- /dev/null +++ b/cmd/ecloud/ecloud_availabilityzone.go @@ -0,0 +1,91 @@ +package ecloud + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + "github.com/ukfast/cli/internal/pkg/factory" + "github.com/ukfast/cli/internal/pkg/helper" + "github.com/ukfast/cli/internal/pkg/output" + "github.com/ukfast/sdk-go/pkg/service/ecloud" +) + +func ecloudAvailabilityZoneRootCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "availabilityzone", + Short: "sub-commands relating to availability zones", + Aliases: []string{ + "az", + }, + } + + // Child commands + cmd.AddCommand(ecloudAvailabilityZoneListCmd(f)) + cmd.AddCommand(ecloudAvailabilityZoneShowCmd(f)) + + return cmd +} + +func ecloudAvailabilityZoneListCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "Lists availability zones", + Long: "This command lists availability zones", + Example: "ukfast ecloud availabilityzone list", + RunE: ecloudCobraRunEFunc(f, ecloudAvailabilityZoneList), + } + + cmd.Flags().String("policy", "", "Availability policy ID for filtering") + + return cmd +} + +func ecloudAvailabilityZoneList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + params, err := helper.GetAPIRequestParametersFromFlags(cmd, + helper.NewStringFilterFlagOption("name", "name"), + helper.NewStringFilterFlagOption("policy", "availability_policy_id"), + ) + if err != nil { + return err + } + + zones, err := service.GetAvailabilityZones(params) + if err != nil { + return fmt.Errorf("Error retrieving availability zones: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudAvailabilityZonesProvider(zones)) +} + +func ecloudAvailabilityZoneShowCmd(f factory.ClientFactory) *cobra.Command { + return &cobra.Command{ + Use: "show ...", + Short: "Shows an availability zone", + Long: "This command shows one or more availability zones", + Example: "ukfast ecloud availabilityzone show az-abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing availability zone") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudAvailabilityZoneShow), + } +} + +func ecloudAvailabilityZoneShow(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + var zones []ecloud.AvailabilityZone + for _, arg := range args { + zone, err := service.GetAvailabilityZone(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving availability zone [%s]: %s", arg, err) + continue + } + + zones = append(zones, zone) + } + + return output.CommandOutput(cmd, OutputECloudAvailabilityZonesProvider(zones)) +} diff --git a/cmd/ecloud/ecloud_availabilityzone_test.go b/cmd/ecloud/ecloud_availabilityzone_test.go new file mode 100644 index 0000000..92800bd --- /dev/null +++ b/cmd/ecloud/ecloud_availabilityzone_test.go @@ -0,0 +1,109 @@ +package ecloud + +import ( + "errors" + "testing" + + gomock "github.com/golang/mock/gomock" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/ukfast/cli/internal/pkg/clierrors" + "github.com/ukfast/cli/test/mocks" + "github.com/ukfast/cli/test/test_output" + "github.com/ukfast/sdk-go/pkg/service/ecloud" +) + +func Test_ecloudAvailabilityZoneList(t *testing.T) { + t.Run("DefaultRetrieve", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetAvailabilityZones(gomock.Any()).Return([]ecloud.AvailabilityZone{}, nil).Times(1) + + ecloudAvailabilityZoneList(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 := ecloudAvailabilityZoneList(service, cmd, []string{}) + + assert.IsType(t, &clierrors.ErrInvalidFlagValue{}, err) + }) + + t.Run("GetAvailabilityZonesError_ReturnsError", func(t *testing.T) { + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetAvailabilityZones(gomock.Any()).Return([]ecloud.AvailabilityZone{}, errors.New("test error")).Times(1) + + err := ecloudAvailabilityZoneList(service, &cobra.Command{}, []string{}) + + assert.Equal(t, "Error retrieving availability zones: test error", err.Error()) + }) +} + +func Test_ecloudAvailabilityZoneShowCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudAvailabilityZoneShowCmd(nil).Args(nil, []string{"az-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudAvailabilityZoneShowCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing availability zone", err.Error()) + }) +} + +func Test_ecloudAvailabilityZoneShow(t *testing.T) { + t.Run("SingleAvailabilityZone", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetAvailabilityZone("az-abcdef12").Return(ecloud.AvailabilityZone{}, nil).Times(1) + + ecloudAvailabilityZoneShow(service, &cobra.Command{}, []string{"az-abcdef12"}) + }) + + t.Run("MultipleAvailabilityZones", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().GetAvailabilityZone("az-abcdef12").Return(ecloud.AvailabilityZone{}, nil), + service.EXPECT().GetAvailabilityZone("az-abcdef23").Return(ecloud.AvailabilityZone{}, nil), + ) + + ecloudAvailabilityZoneShow(service, &cobra.Command{}, []string{"az-abcdef12", "az-abcdef23"}) + }) + + t.Run("GetAvailabilityZoneError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetAvailabilityZone("az-abcdef12").Return(ecloud.AvailabilityZone{}, errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error retrieving availability zone [az-abcdef12]: test error\n", func() { + ecloudAvailabilityZoneShow(service, &cobra.Command{}, []string{"az-abcdef12"}) + }) + }) +} diff --git a/go.mod b/go.mod index b7674a3..e621c11 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 github.com/stretchr/testify v1.6.1 - github.com/ukfast/sdk-go v1.4.5 + github.com/ukfast/sdk-go v1.4.6 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b // indirect gopkg.in/go-playground/assert.v1 v1.2.1 k8s.io/client-go v11.0.0+incompatible diff --git a/go.sum b/go.sum index 20e5192..5cfe590 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4U github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ukfast/go-durationstring v1.0.0 h1:kgPuA7XjLjgLDfkG8j0MpolxcZh/eMdiVoOIFD/uc5I= github.com/ukfast/go-durationstring v1.0.0/go.mod h1:Ci81n51kfxlKUIaLY9cINIKRO94VTqV+iCGbOMTb0V8= -github.com/ukfast/sdk-go v1.4.5 h1:O2VvIMulQ1P3KL3xzg7INpmwuCWtLB4NBdqDf14gn00= -github.com/ukfast/sdk-go v1.4.5/go.mod h1:tspweEP77MHhVEYgEEieKAKGITFgwkYl1q5fLh4HZAo= +github.com/ukfast/sdk-go v1.4.6 h1:1U2KeWXwNWhYiyRdYbqlJeeMvjo+/DDyxXqO+f82VBg= +github.com/ukfast/sdk-go v1.4.6/go.mod h1:tspweEP77MHhVEYgEEieKAKGITFgwkYl1q5fLh4HZAo= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= From 00cc624438835fcd343f1530ed1248ed1b0f29f7 Mon Sep 17 00:00:00 2001 From: Lee Spottiswood Date: Tue, 22 Jun 2021 15:16:22 +0100 Subject: [PATCH 2/6] update to use ecloudCobraRunEFunc --- cmd/ecloud/ecloud_dhcp.go | 18 ++------------ cmd/ecloud/ecloud_firewallrule.go | 18 ++------------ cmd/ecloud/ecloud_firewallruleport.go | 18 ++------------ cmd/ecloud/ecloud_floatingip.go | 18 ++------------ cmd/ecloud/ecloud_image.go | 18 ++------------ cmd/ecloud/ecloud_networkrule.go | 18 ++------------ cmd/ecloud/ecloud_networkruleport.go | 18 ++------------ cmd/ecloud/ecloud_nic.go | 18 ++------------ cmd/ecloud/ecloud_region.go | 18 ++------------ cmd/ecloud/ecloud_task.go | 5 ++-- cmd/ecloud/ecloud_task_test.go | 5 ++-- cmd/ecloud/ecloud_vpc.go | 36 +++------------------------ 12 files changed, 28 insertions(+), 180 deletions(-) diff --git a/cmd/ecloud/ecloud_dhcp.go b/cmd/ecloud/ecloud_dhcp.go index 8c66796..4a54423 100644 --- a/cmd/ecloud/ecloud_dhcp.go +++ b/cmd/ecloud/ecloud_dhcp.go @@ -30,14 +30,7 @@ func ecloudDHCPListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists DHCPs", Long: "This command lists DHCPs", Example: "ukfast ecloud dhcp list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudDHCPList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudDHCPList), } cmd.Flags().String("name", "", "DHCP name for filtering") @@ -76,14 +69,7 @@ func ecloudDHCPShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudDHCPShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudDHCPShow), } } diff --git a/cmd/ecloud/ecloud_firewallrule.go b/cmd/ecloud/ecloud_firewallrule.go index ba0d526..96f24f5 100644 --- a/cmd/ecloud/ecloud_firewallrule.go +++ b/cmd/ecloud/ecloud_firewallrule.go @@ -37,14 +37,7 @@ func ecloudFirewallRuleListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists firewall rules", Long: "This command lists firewall rules", Example: "ukfast ecloud firewallrule list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudFirewallRuleList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudFirewallRuleList), } cmd.Flags().String("policy", "", "Firewall policy ID for filtering") @@ -82,14 +75,7 @@ func ecloudFirewallRuleShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudFirewallRuleShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudFirewallRuleShow), } } diff --git a/cmd/ecloud/ecloud_firewallruleport.go b/cmd/ecloud/ecloud_firewallruleport.go index 9997289..585c31e 100644 --- a/cmd/ecloud/ecloud_firewallruleport.go +++ b/cmd/ecloud/ecloud_firewallruleport.go @@ -33,14 +33,7 @@ func ecloudFirewallRulePortListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists firewall rule ports", Long: "This command lists firewall rule ports", Example: "ukfast ecloud firewallruleport list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudFirewallRulePortList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudFirewallRulePortList), } cmd.Flags().String("rule", "", "Firewall rule ID for filtering") @@ -78,14 +71,7 @@ func ecloudFirewallRulePortShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudFirewallRulePortShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudFirewallRulePortShow), } } diff --git a/cmd/ecloud/ecloud_floatingip.go b/cmd/ecloud/ecloud_floatingip.go index 1bc7b43..45a22ee 100644 --- a/cmd/ecloud/ecloud_floatingip.go +++ b/cmd/ecloud/ecloud_floatingip.go @@ -35,14 +35,7 @@ func ecloudFloatingIPListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists floating IPs", Long: "This command lists floating IPs", Example: "ukfast ecloud floatingip list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudFloatingIPList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudFloatingIPList), } } @@ -73,14 +66,7 @@ func ecloudFloatingIPShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudFloatingIPShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudFloatingIPShow), } } diff --git a/cmd/ecloud/ecloud_image.go b/cmd/ecloud/ecloud_image.go index 9fbe4cd..f1b10d5 100644 --- a/cmd/ecloud/ecloud_image.go +++ b/cmd/ecloud/ecloud_image.go @@ -34,14 +34,7 @@ func ecloudImageListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists images", Long: "This command lists images", Example: "ukfast ecloud image list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudImageList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudImageList), } } @@ -72,14 +65,7 @@ func ecloudImageShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudImageShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudImageShow), } } diff --git a/cmd/ecloud/ecloud_networkrule.go b/cmd/ecloud/ecloud_networkrule.go index 3202ff4..09b865e 100644 --- a/cmd/ecloud/ecloud_networkrule.go +++ b/cmd/ecloud/ecloud_networkrule.go @@ -37,14 +37,7 @@ func ecloudNetworkRuleListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists network rules", Long: "This command lists network rules", Example: "ukfast ecloud networkrule list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudNetworkRuleList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudNetworkRuleList), } cmd.Flags().String("policy", "", "Network policy ID for filtering") @@ -82,14 +75,7 @@ func ecloudNetworkRuleShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudNetworkRuleShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudNetworkRuleShow), } } diff --git a/cmd/ecloud/ecloud_networkruleport.go b/cmd/ecloud/ecloud_networkruleport.go index fffc7a8..cb4e22a 100644 --- a/cmd/ecloud/ecloud_networkruleport.go +++ b/cmd/ecloud/ecloud_networkruleport.go @@ -33,14 +33,7 @@ func ecloudNetworkRulePortListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists network rule ports", Long: "This command lists network rule ports", Example: "ukfast ecloud networkruleport list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudNetworkRulePortList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudNetworkRulePortList), } cmd.Flags().String("rule", "", "Network rule ID for filtering") @@ -78,14 +71,7 @@ func ecloudNetworkRulePortShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudNetworkRulePortShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudNetworkRulePortShow), } } diff --git a/cmd/ecloud/ecloud_nic.go b/cmd/ecloud/ecloud_nic.go index 354de64..8210528 100644 --- a/cmd/ecloud/ecloud_nic.go +++ b/cmd/ecloud/ecloud_nic.go @@ -30,14 +30,7 @@ func ecloudNICListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists NICs", Long: "This command lists NICs", Example: "ukfast ecloud nic list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudNICList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudNICList), } cmd.Flags().String("name", "", "NIC name for filtering") @@ -72,14 +65,7 @@ func ecloudNICShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudNICShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudNICShow), } } diff --git a/cmd/ecloud/ecloud_region.go b/cmd/ecloud/ecloud_region.go index 3a140df..abfff0c 100644 --- a/cmd/ecloud/ecloud_region.go +++ b/cmd/ecloud/ecloud_region.go @@ -30,14 +30,7 @@ func ecloudRegionListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists regions", Long: "This command lists regions", Example: "ukfast ecloud region list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudRegionList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudRegionList), } cmd.Flags().String("name", "", "Region name for filtering") @@ -72,14 +65,7 @@ func ecloudRegionShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudRegionShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudRegionShow), } } diff --git a/cmd/ecloud/ecloud_task.go b/cmd/ecloud/ecloud_task.go index f28da9b..1033b33 100644 --- a/cmd/ecloud/ecloud_task.go +++ b/cmd/ecloud/ecloud_task.go @@ -20,6 +20,7 @@ func ecloudTaskRootCmd(f factory.ClientFactory) *cobra.Command { // Child commands cmd.AddCommand(ecloudTaskListCmd(f)) cmd.AddCommand(ecloudTaskShowCmd(f)) + cmd.AddCommand(ecloudTaskWaitCmd(f)) return cmd } @@ -46,12 +47,12 @@ func ecloudTaskList(service ecloud.ECloudService, cmd *cobra.Command, args []str return err } - tasks, err := service.GetTasks(params) + paginatedTasks, err := service.GetTasksPaginated(params) if err != nil { return fmt.Errorf("Error retrieving tasks: %s", err) } - return output.CommandOutput(cmd, OutputECloudTasksProvider(tasks)) + return output.CommandOutputPaginated(cmd, OutputECloudTasksProvider(paginatedTasks.Items), paginatedTasks) } func ecloudTaskShowCmd(f factory.ClientFactory) *cobra.Command { diff --git a/cmd/ecloud/ecloud_task_test.go b/cmd/ecloud/ecloud_task_test.go index 5a024cf..9202cf6 100644 --- a/cmd/ecloud/ecloud_task_test.go +++ b/cmd/ecloud/ecloud_task_test.go @@ -10,6 +10,7 @@ import ( "github.com/ukfast/cli/internal/pkg/clierrors" "github.com/ukfast/cli/test/mocks" "github.com/ukfast/cli/test/test_output" + "github.com/ukfast/sdk-go/pkg/connection" "github.com/ukfast/sdk-go/pkg/service/ecloud" ) @@ -20,7 +21,7 @@ func Test_ecloudTaskList(t *testing.T) { service := mocks.NewMockECloudService(mockCtrl) - service.EXPECT().GetTasks(gomock.Any()).Return([]ecloud.Task{}, nil).Times(1) + service.EXPECT().GetTasksPaginated(gomock.Any()).Return(&ecloud.PaginatedTask{PaginatedBase: &connection.PaginatedBase{}}, nil).Times(1) ecloudTaskList(service, &cobra.Command{}, []string{}) }) @@ -45,7 +46,7 @@ func Test_ecloudTaskList(t *testing.T) { service := mocks.NewMockECloudService(mockCtrl) - service.EXPECT().GetTasks(gomock.Any()).Return([]ecloud.Task{}, errors.New("test error")).Times(1) + service.EXPECT().GetTasksPaginated(gomock.Any()).Return(&ecloud.PaginatedTask{PaginatedBase: &connection.PaginatedBase{}}, errors.New("test error")).Times(1) err := ecloudTaskList(service, &cobra.Command{}, []string{}) diff --git a/cmd/ecloud/ecloud_vpc.go b/cmd/ecloud/ecloud_vpc.go index bf2172a..9a0cdb2 100644 --- a/cmd/ecloud/ecloud_vpc.go +++ b/cmd/ecloud/ecloud_vpc.go @@ -39,14 +39,7 @@ func ecloudVPCListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists VPCs", Long: "This command lists VPCs", Example: "ukfast ecloud vpc list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudVPCList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudVPCList), } cmd.Flags().String("name", "", "VPC name for filtering") @@ -81,14 +74,7 @@ func ecloudVPCShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudVPCShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudVPCShow), } } @@ -113,14 +99,7 @@ func ecloudVPCCreateCmd(f factory.ClientFactory) *cobra.Command { Short: "Creates a VPC", Long: "This command creates a VPC", Example: "ukfast ecloud vpc create", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudVPCCreate(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudVPCCreate), } // Setup flags @@ -176,14 +155,7 @@ func ecloudVPCUpdateCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudVPCUpdate(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudVPCUpdate), } cmd.Flags().String("name", "", "Name of VPC") From 84d0f19f23fcb4047ccb696f4fc8fab1d4a17df7 Mon Sep 17 00:00:00 2001 From: Lee Spottiswood Date: Tue, 22 Jun 2021 15:16:31 +0100 Subject: [PATCH 3/6] add host/hostgroup/hostspec commands --- cmd/ecloud/ecloud.go | 6 +- cmd/ecloud/ecloud_host.go | 234 ++++++++++++++ cmd/ecloud/ecloud_host_test.go | 473 ++++++++++++++++++++++++++++ cmd/ecloud/ecloud_hostgroup.go | 241 ++++++++++++++ cmd/ecloud/ecloud_hostgroup_test.go | 473 ++++++++++++++++++++++++++++ cmd/ecloud/ecloud_hostspec.go | 88 ++++++ cmd/ecloud/ecloud_hostspec_test.go | 109 +++++++ cmd/ecloud/ecloud_v1_host.go | 24 +- cmd/ecloud/ecloud_v1_host_test.go | 24 +- cmd/ecloud/output.go | 16 + test/mocks/mock_ecloudservice.go | 36 ++- 11 files changed, 1683 insertions(+), 41 deletions(-) create mode 100644 cmd/ecloud/ecloud_host.go create mode 100644 cmd/ecloud/ecloud_host_test.go create mode 100644 cmd/ecloud/ecloud_hostgroup.go create mode 100644 cmd/ecloud/ecloud_hostgroup_test.go create mode 100644 cmd/ecloud/ecloud_hostspec.go create mode 100644 cmd/ecloud/ecloud_hostspec_test.go diff --git a/cmd/ecloud/ecloud.go b/cmd/ecloud/ecloud.go index 72c39e2..c3bc88a 100644 --- a/cmd/ecloud/ecloud.go +++ b/cmd/ecloud/ecloud.go @@ -28,7 +28,7 @@ func ECloudRootCmd(f factory.ClientFactory, fs afero.Fs) *cobra.Command { cmd.AddCommand(ecloudVirtualMachineRootCmd(f)) cmd.AddCommand(ecloudSolutionRootCmd(f)) cmd.AddCommand(ecloudSiteRootCmd(f)) - cmd.AddCommand(ecloudHostRootCmd(f)) + cmd.AddCommand(ecloudV1HostRootCmd(f)) cmd.AddCommand(ecloudFirewallRootCmd(f)) cmd.AddCommand(ecloudPodRootCmd(f)) cmd.AddCommand(ecloudDatastoreRootCmd(f)) @@ -37,11 +37,15 @@ func ECloudRootCmd(f factory.ClientFactory, fs afero.Fs) *cobra.Command { } // -- eCloud v2 if v2envset || !v1envset { + cmd.AddCommand(ecloudAvailabilityZoneRootCmd(f)) cmd.AddCommand(ecloudDHCPRootCmd(f)) cmd.AddCommand(ecloudFirewallPolicyRootCmd(f)) cmd.AddCommand(ecloudFirewallRuleRootCmd(f)) cmd.AddCommand(ecloudFirewallRulePortRootCmd(f)) cmd.AddCommand(ecloudFloatingIPRootCmd(f)) + cmd.AddCommand(ecloudHostRootCmd(f)) + cmd.AddCommand(ecloudHostGroupRootCmd(f)) + cmd.AddCommand(ecloudHostSpecRootCmd(f)) cmd.AddCommand(ecloudImageRootCmd(f)) cmd.AddCommand(ecloudInstanceRootCmd(f)) cmd.AddCommand(ecloudNetworkRootCmd(f)) diff --git a/cmd/ecloud/ecloud_host.go b/cmd/ecloud/ecloud_host.go new file mode 100644 index 0000000..7ac3b85 --- /dev/null +++ b/cmd/ecloud/ecloud_host.go @@ -0,0 +1,234 @@ +package ecloud + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + "github.com/ukfast/cli/internal/pkg/factory" + "github.com/ukfast/cli/internal/pkg/helper" + "github.com/ukfast/cli/internal/pkg/output" + "github.com/ukfast/sdk-go/pkg/service/ecloud" +) + +func ecloudHostRootCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "host", + Short: "sub-commands relating to hosts", + } + + // Child commands + cmd.AddCommand(ecloudHostListCmd(f)) + cmd.AddCommand(ecloudHostShowCmd(f)) + cmd.AddCommand(ecloudHostCreateCmd(f)) + cmd.AddCommand(ecloudHostUpdateCmd(f)) + cmd.AddCommand(ecloudHostDeleteCmd(f)) + + return cmd +} + +func ecloudHostListCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "Lists hosts", + Long: "This command lists hosts", + Example: "ukfast ecloud host list", + RunE: ecloudCobraRunEFunc(f, ecloudHostList), + } + + cmd.Flags().String("policy", "", "Host policy ID for filtering") + + return cmd +} + +func ecloudHostList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + params, err := helper.GetAPIRequestParametersFromFlags(cmd, + helper.NewStringFilterFlagOption("name", "name"), + helper.NewStringFilterFlagOption("policy", "host_policy_id"), + ) + if err != nil { + return err + } + + groups, err := service.GetHosts(params) + if err != nil { + return fmt.Errorf("Error retrieving hosts: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudHostsProvider(groups)) +} + +func ecloudHostShowCmd(f factory.ClientFactory) *cobra.Command { + return &cobra.Command{ + Use: "show ...", + Short: "Shows an host", + Long: "This command shows one or more hosts", + Example: "ukfast ecloud host show h-abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing host") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudHostShow), + } +} + +func ecloudHostShow(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + var groups []ecloud.Host + for _, arg := range args { + group, err := service.GetHost(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving host [%s]: %s", arg, err) + continue + } + + groups = append(groups, group) + } + + return output.CommandOutput(cmd, OutputECloudHostsProvider(groups)) +} + +func ecloudHostCreateCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "create", + Short: "Creates a host", + Long: "This command creates a host", + Example: "ukfast ecloud host create --policy np-abcdef12", + RunE: ecloudCobraRunEFunc(f, ecloudHostCreate), + } + + // Setup flags + cmd.Flags().String("name", "", "Name of host") + cmd.Flags().String("host-group", "", "ID of host group") + cmd.MarkFlagRequired("host-group") + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the host has been completely created") + + return cmd +} + +func ecloudHostCreate(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + createRequest := ecloud.CreateHostRequest{} + createRequest.Name, _ = cmd.Flags().GetString("name") + createRequest.HostGroupID, _ = cmd.Flags().GetString("host-group") + + taskRef, err := service.CreateHost(createRequest) + if err != nil { + return fmt.Errorf("Error creating host: %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 host task to complete: %s", err) + } + } + + group, err := service.GetHost(taskRef.ResourceID) + if err != nil { + return fmt.Errorf("Error retrieving new host: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudHostsProvider([]ecloud.Host{group})) +} + +func ecloudHostUpdateCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "update ...", + Short: "Updates a host", + Long: "This command updates one or more hosts", + Example: "ukfast ecloud host update np-abcdef12 --name \"my group\"", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing host") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudHostUpdate), + } + + cmd.Flags().String("name", "", "Name of host") + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the host has been completely updated") + + return cmd +} + +func ecloudHostUpdate(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + patchRequest := ecloud.PatchHostRequest{} + + if cmd.Flags().Changed("name") { + patchRequest.Name, _ = cmd.Flags().GetString("name") + } + + var groups []ecloud.Host + for _, arg := range args { + task, err := service.PatchHost(arg, patchRequest) + if err != nil { + output.OutputWithErrorLevelf("Error updating host [%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 host [%s]: %s", arg, err) + continue + } + } + + group, err := service.GetHost(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving updated host [%s]: %s", arg, err) + continue + } + + groups = append(groups, group) + } + + return output.CommandOutput(cmd, OutputECloudHostsProvider(groups)) +} + +func ecloudHostDeleteCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ...", + Short: "Removes a host", + Long: "This command removes one or more hosts", + Example: "ukfast ecloud host delete h-abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing host") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudHostDelete), + } + + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the host has been completely removed") + + return cmd +} + +func ecloudHostDelete(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + for _, arg := range args { + taskID, err := service.DeleteHost(arg) + if err != nil { + output.OutputWithErrorLevelf("Error removing host [%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 host [%s]: %s", arg, err) + continue + } + } + } + return nil +} diff --git a/cmd/ecloud/ecloud_host_test.go b/cmd/ecloud/ecloud_host_test.go new file mode 100644 index 0000000..a848a56 --- /dev/null +++ b/cmd/ecloud/ecloud_host_test.go @@ -0,0 +1,473 @@ +package ecloud + +import ( + "errors" + "testing" + + gomock "github.com/golang/mock/gomock" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/ukfast/cli/internal/pkg/clierrors" + "github.com/ukfast/cli/test/mocks" + "github.com/ukfast/cli/test/test_output" + "github.com/ukfast/sdk-go/pkg/service/ecloud" +) + +func Test_ecloudHostList(t *testing.T) { + t.Run("DefaultRetrieve", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHosts(gomock.Any()).Return([]ecloud.Host{}, nil).Times(1) + + ecloudHostList(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 := ecloudHostList(service, cmd, []string{}) + + assert.IsType(t, &clierrors.ErrInvalidFlagValue{}, err) + }) + + t.Run("GetHostsError_ReturnsError", func(t *testing.T) { + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHosts(gomock.Any()).Return([]ecloud.Host{}, errors.New("test error")).Times(1) + + err := ecloudHostList(service, &cobra.Command{}, []string{}) + + assert.Equal(t, "Error retrieving hosts: test error", err.Error()) + }) +} + +func Test_ecloudHostShowCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudHostShowCmd(nil).Args(nil, []string{"h-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudHostShowCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing host", err.Error()) + }) +} + +func Test_ecloudHostShow(t *testing.T) { + t.Run("SingleHost", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, nil).Times(1) + + ecloudHostShow(service, &cobra.Command{}, []string{"h-abcdef12"}) + }) + + t.Run("MultipleHosts", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, nil), + service.EXPECT().GetHost("h-abcdef23").Return(ecloud.Host{}, nil), + ) + + ecloudHostShow(service, &cobra.Command{}, []string{"h-abcdef12", "h-abcdef23"}) + }) + + t.Run("GetHostError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error retrieving host [h-abcdef12]: test error\n", func() { + ecloudHostShow(service, &cobra.Command{}, []string{"h-abcdef12"}) + }) + }) +} + +func Test_ecloudHostCreate(t *testing.T) { + t.Run("DefaultCreate", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--host-group=hg-abcdef12"}) + + req := ecloud.CreateHostRequest{ + Name: "testgroup", + HostGroupID: "hg-abcdef12", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "h-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateHost(req).Return(resp, nil), + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, nil), + ) + + ecloudHostCreate(service, cmd, []string{}) + }) + + t.Run("CreateWithWaitFlagNoError_Succeeds", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--host-group=hg-abcdef12", "--wait"}) + + req := ecloud.CreateHostRequest{ + Name: "testgroup", + HostGroupID: "hg-abcdef12", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "h-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateHost(req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil), + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, nil), + ) + + ecloudHostCreate(service, cmd, []string{}) + }) + + t.Run("WithWaitFlag_GetTaskError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--host-group=hg-abcdef12", "--wait"}) + + req := ecloud.CreateHostRequest{ + Name: "testgroup", + HostGroupID: "hg-abcdef12", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "h-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateHost(req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, errors.New("test error")), + ) + + err := ecloudHostCreate(service, cmd, []string{}) + assert.Equal(t, "Error waiting for host task to complete: Error waiting for command: Failed to retrieve task status: test error", err.Error()) + }) + + t.Run("CreateHostError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup"}) + + service.EXPECT().CreateHost(gomock.Any()).Return(ecloud.TaskReference{}, errors.New("test error")).Times(1) + + err := ecloudHostCreate(service, cmd, []string{}) + + assert.Equal(t, "Error creating host: test error", err.Error()) + }) + + t.Run("GetHostError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup"}) + + gomock.InOrder( + service.EXPECT().CreateHost(gomock.Any()).Return(ecloud.TaskReference{ResourceID: "h-abcdef12"}, nil), + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, errors.New("test error")), + ) + + err := ecloudHostCreate(service, cmd, []string{}) + + assert.Equal(t, "Error retrieving new host: test error", err.Error()) + }) +} + +func Test_ecloudHostUpdateCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudHostUpdateCmd(nil).Args(nil, []string{"h-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudHostUpdateCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing host", err.Error()) + }) +} + +func Test_ecloudHostUpdate(t *testing.T) { + t.Run("SingleGroup", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + cmd := ecloudHostUpdateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup"}) + + req := ecloud.PatchHostRequest{ + Name: "testgroup", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "h-abcdef12", + } + + gomock.InOrder( + service.EXPECT().PatchHost("h-abcdef12", req).Return(resp, nil), + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, nil), + ) + + ecloudHostUpdate(service, cmd, []string{"h-abcdef12"}) + }) + + t.Run("MultipleHosts", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + resp1 := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "h-abcdef12", + } + + resp2 := ecloud.TaskReference{ + TaskID: "task-abcdef23", + ResourceID: "h-12abcdef", + } + + gomock.InOrder( + service.EXPECT().PatchHost("h-abcdef12", gomock.Any()).Return(resp1, nil), + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, nil), + service.EXPECT().PatchHost("h-12abcdef", gomock.Any()).Return(resp2, nil), + service.EXPECT().GetHost("h-12abcdef").Return(ecloud.Host{}, nil), + ) + + ecloudHostUpdate(service, &cobra.Command{}, []string{"h-abcdef12", "h-12abcdef"}) + }) + + t.Run("WithWaitFlag_NoError_Succeeds", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + cmd := ecloudHostUpdateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--wait"}) + + req := ecloud.PatchHostRequest{ + Name: "testgroup", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "h-abcdef12", + } + + gomock.InOrder( + service.EXPECT().PatchHost("h-abcdef12", req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil), + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, nil), + ) + + ecloudHostUpdate(service, cmd, []string{"h-abcdef12"}) + }) + + t.Run("WithWaitFlag_GetTaskError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + cmd := ecloudHostUpdateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--wait"}) + + req := ecloud.PatchHostRequest{ + Name: "testgroup", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "h-abcdef12", + } + + gomock.InOrder( + service.EXPECT().PatchHost("h-abcdef12", req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, errors.New("test error")), + ) + + test_output.AssertErrorOutput(t, "Error waiting for task to complete for host [h-abcdef12]: Error waiting for command: Failed to retrieve task status: test error\n", func() { + ecloudHostUpdate(service, cmd, []string{"h-abcdef12"}) + }) + }) + + t.Run("PatchHostError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().PatchHost("h-abcdef12", gomock.Any()).Return(ecloud.TaskReference{}, errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error updating host [h-abcdef12]: test error\n", func() { + ecloudHostUpdate(service, &cobra.Command{}, []string{"h-abcdef12"}) + }) + }) + + t.Run("GetHostError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "h-abcdef12", + } + + gomock.InOrder( + service.EXPECT().PatchHost("h-abcdef12", gomock.Any()).Return(resp, nil), + service.EXPECT().GetHost("h-abcdef12").Return(ecloud.Host{}, errors.New("test error")), + ) + + test_output.AssertErrorOutput(t, "Error retrieving updated host [h-abcdef12]: test error\n", func() { + ecloudHostUpdate(service, &cobra.Command{}, []string{"h-abcdef12"}) + }) + }) +} + +func Test_ecloudHostDeleteCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudHostDeleteCmd(nil).Args(nil, []string{"h-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudHostDeleteCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing host", err.Error()) + }) +} + +func Test_ecloudHostDelete(t *testing.T) { + t.Run("SingleGroup", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().DeleteHost("h-abcdef12").Return("task-abcdef12", nil).Times(1) + + ecloudHostDelete(service, &cobra.Command{}, []string{"h-abcdef12"}) + }) + + t.Run("MultipleHosts", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().DeleteHost("h-abcdef12").Return("task-abcdef12", nil), + service.EXPECT().DeleteHost("h-12abcdef").Return("task-abcdef23", nil), + ) + + ecloudHostDelete(service, &cobra.Command{}, []string{"h-abcdef12", "h-12abcdef"}) + }) + + t.Run("WithWaitFlag_NoError_Succeeds", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + cmd := ecloudHostDeleteCmd(nil) + cmd.ParseFlags([]string{"--wait"}) + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().DeleteHost("h-abcdef12").Return("task-abcdef12", nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil), + ) + + ecloudHostDelete(service, cmd, []string{"h-abcdef12"}) + }) + + t.Run("WithWaitFlag_GetTaskError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + cmd := ecloudHostDeleteCmd(nil) + cmd.ParseFlags([]string{"--wait"}) + + gomock.InOrder( + service.EXPECT().DeleteHost("h-abcdef12").Return("task-abcdef12", nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, errors.New("test error")), + ) + + test_output.AssertErrorOutput(t, "Error waiting for task to complete for host [h-abcdef12]: Error waiting for command: Failed to retrieve task status: test error\n", func() { + ecloudHostDelete(service, cmd, []string{"h-abcdef12"}) + }) + }) + + t.Run("DeleteHostError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().DeleteHost("h-abcdef12").Return("", errors.New("test error")).Times(1) + + test_output.AssertErrorOutput(t, "Error removing host [h-abcdef12]: test error\n", func() { + ecloudHostDelete(service, &cobra.Command{}, []string{"h-abcdef12"}) + }) + }) +} diff --git a/cmd/ecloud/ecloud_hostgroup.go b/cmd/ecloud/ecloud_hostgroup.go new file mode 100644 index 0000000..00690cc --- /dev/null +++ b/cmd/ecloud/ecloud_hostgroup.go @@ -0,0 +1,241 @@ +package ecloud + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + "github.com/ukfast/cli/internal/pkg/factory" + "github.com/ukfast/cli/internal/pkg/helper" + "github.com/ukfast/cli/internal/pkg/output" + "github.com/ukfast/sdk-go/pkg/service/ecloud" +) + +func ecloudHostGroupRootCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "hostgroup", + Short: "sub-commands relating to host groups", + } + + // Child commands + cmd.AddCommand(ecloudHostGroupListCmd(f)) + cmd.AddCommand(ecloudHostGroupShowCmd(f)) + cmd.AddCommand(ecloudHostGroupCreateCmd(f)) + cmd.AddCommand(ecloudHostGroupUpdateCmd(f)) + cmd.AddCommand(ecloudHostGroupDeleteCmd(f)) + + return cmd +} + +func ecloudHostGroupListCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "Lists host groups", + Long: "This command lists host groups", + Example: "ukfast ecloud hostgroup list", + RunE: ecloudCobraRunEFunc(f, ecloudHostGroupList), + } + + cmd.Flags().String("policy", "", "Host policy ID for filtering") + + return cmd +} + +func ecloudHostGroupList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + params, err := helper.GetAPIRequestParametersFromFlags(cmd, + helper.NewStringFilterFlagOption("name", "name"), + helper.NewStringFilterFlagOption("policy", "host_policy_id"), + ) + if err != nil { + return err + } + + groups, err := service.GetHostGroups(params) + if err != nil { + return fmt.Errorf("Error retrieving host groups: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudHostGroupsProvider(groups)) +} + +func ecloudHostGroupShowCmd(f factory.ClientFactory) *cobra.Command { + return &cobra.Command{ + Use: "show ...", + Short: "Shows an host group", + Long: "This command shows one or more host groups", + Example: "ukfast ecloud hostgroup show hg-abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing host group") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudHostGroupShow), + } +} + +func ecloudHostGroupShow(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + var groups []ecloud.HostGroup + for _, arg := range args { + group, err := service.GetHostGroup(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving host group [%s]: %s", arg, err) + continue + } + + groups = append(groups, group) + } + + return output.CommandOutput(cmd, OutputECloudHostGroupsProvider(groups)) +} + +func ecloudHostGroupCreateCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "create", + Short: "Creates a host group", + Long: "This command creates a host group", + Example: "ukfast ecloud hostgroup create --policy np-abcdef12", + RunE: ecloudCobraRunEFunc(f, ecloudHostGroupCreate), + } + + // Setup flags + cmd.Flags().String("name", "", "Name of host group") + cmd.Flags().String("vpc", "", "ID of VPC") + cmd.MarkFlagRequired("vpc") + cmd.Flags().String("availability-zone", "", "ID of availability zone") + cmd.Flags().String("host-spec", "", "ID of host specification") + cmd.MarkFlagRequired("host-spec") + cmd.Flags().Bool("windows-enabled", false, "Specifies Windows OS should be enabled for instances") + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the host group has been completely created") + + return cmd +} + +func ecloudHostGroupCreate(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + createRequest := ecloud.CreateHostGroupRequest{} + createRequest.Name, _ = cmd.Flags().GetString("name") + createRequest.VPCID, _ = cmd.Flags().GetString("vpc") + createRequest.AvailabilityZoneID, _ = cmd.Flags().GetString("availability-zone") + createRequest.HostSpecID, _ = cmd.Flags().GetString("host-spec") + createRequest.WindowsEnabled, _ = cmd.Flags().GetBool("windows-enabled") + + taskRef, err := service.CreateHostGroup(createRequest) + if err != nil { + return fmt.Errorf("Error creating host group: %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 host group task to complete: %s", err) + } + } + + group, err := service.GetHostGroup(taskRef.ResourceID) + if err != nil { + return fmt.Errorf("Error retrieving new host group: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudHostGroupsProvider([]ecloud.HostGroup{group})) +} + +func ecloudHostGroupUpdateCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "update ...", + Short: "Updates a host group", + Long: "This command updates one or more host groups", + Example: "ukfast ecloud hostgroup update np-abcdef12 --name \"my group\"", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing host group") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudHostGroupUpdate), + } + + cmd.Flags().String("name", "", "Name of host group") + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the host group has been completely updated") + + return cmd +} + +func ecloudHostGroupUpdate(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + patchRequest := ecloud.PatchHostGroupRequest{} + + if cmd.Flags().Changed("name") { + patchRequest.Name, _ = cmd.Flags().GetString("name") + } + + var groups []ecloud.HostGroup + for _, arg := range args { + task, err := service.PatchHostGroup(arg, patchRequest) + if err != nil { + output.OutputWithErrorLevelf("Error updating host group [%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 host group [%s]: %s", arg, err) + continue + } + } + + group, err := service.GetHostGroup(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving updated host group [%s]: %s", arg, err) + continue + } + + groups = append(groups, group) + } + + return output.CommandOutput(cmd, OutputECloudHostGroupsProvider(groups)) +} + +func ecloudHostGroupDeleteCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ...", + Short: "Removes a host group", + Long: "This command removes one or more host groups", + Example: "ukfast ecloud hostgroup delete hg-abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing host group") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudHostGroupDelete), + } + + cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the host group has been completely removed") + + return cmd +} + +func ecloudHostGroupDelete(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + for _, arg := range args { + taskID, err := service.DeleteHostGroup(arg) + if err != nil { + output.OutputWithErrorLevelf("Error removing host group [%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 host group [%s]: %s", arg, err) + continue + } + } + } + return nil +} diff --git a/cmd/ecloud/ecloud_hostgroup_test.go b/cmd/ecloud/ecloud_hostgroup_test.go new file mode 100644 index 0000000..5bea716 --- /dev/null +++ b/cmd/ecloud/ecloud_hostgroup_test.go @@ -0,0 +1,473 @@ +package ecloud + +import ( + "errors" + "testing" + + gomock "github.com/golang/mock/gomock" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/ukfast/cli/internal/pkg/clierrors" + "github.com/ukfast/cli/test/mocks" + "github.com/ukfast/cli/test/test_output" + "github.com/ukfast/sdk-go/pkg/service/ecloud" +) + +func Test_ecloudHostGroupList(t *testing.T) { + t.Run("DefaultRetrieve", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHostGroups(gomock.Any()).Return([]ecloud.HostGroup{}, nil).Times(1) + + ecloudHostGroupList(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 := ecloudHostGroupList(service, cmd, []string{}) + + assert.IsType(t, &clierrors.ErrInvalidFlagValue{}, err) + }) + + t.Run("GetHostGroupsError_ReturnsError", func(t *testing.T) { + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHostGroups(gomock.Any()).Return([]ecloud.HostGroup{}, errors.New("test error")).Times(1) + + err := ecloudHostGroupList(service, &cobra.Command{}, []string{}) + + assert.Equal(t, "Error retrieving host groups: test error", err.Error()) + }) +} + +func Test_ecloudHostGroupShowCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudHostGroupShowCmd(nil).Args(nil, []string{"hg-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudHostGroupShowCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing host group", err.Error()) + }) +} + +func Test_ecloudHostGroupShow(t *testing.T) { + t.Run("SingleHostGroup", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, nil).Times(1) + + ecloudHostGroupShow(service, &cobra.Command{}, []string{"hg-abcdef12"}) + }) + + t.Run("MultipleHostGroups", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, nil), + service.EXPECT().GetHostGroup("hg-abcdef23").Return(ecloud.HostGroup{}, nil), + ) + + ecloudHostGroupShow(service, &cobra.Command{}, []string{"hg-abcdef12", "hg-abcdef23"}) + }) + + t.Run("GetHostGroupError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error retrieving host group [hg-abcdef12]: test error\n", func() { + ecloudHostGroupShow(service, &cobra.Command{}, []string{"hg-abcdef12"}) + }) + }) +} + +func Test_ecloudHostGroupCreate(t *testing.T) { + t.Run("DefaultCreate", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostGroupCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--vpc=vpc-abcdef12"}) + + req := ecloud.CreateHostGroupRequest{ + Name: "testgroup", + VPCID: "vpc-abcdef12", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "hg-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateHostGroup(req).Return(resp, nil), + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, nil), + ) + + ecloudHostGroupCreate(service, cmd, []string{}) + }) + + t.Run("CreateWithWaitFlagNoError_Succeeds", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostGroupCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--vpc=vpc-abcdef12", "--wait"}) + + req := ecloud.CreateHostGroupRequest{ + Name: "testgroup", + VPCID: "vpc-abcdef12", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "hg-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateHostGroup(req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil), + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, nil), + ) + + ecloudHostGroupCreate(service, cmd, []string{}) + }) + + t.Run("WithWaitFlag_GetTaskError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostGroupCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--vpc=vpc-abcdef12", "--wait"}) + + req := ecloud.CreateHostGroupRequest{ + Name: "testgroup", + VPCID: "vpc-abcdef12", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "hg-abcdef12", + } + + gomock.InOrder( + service.EXPECT().CreateHostGroup(req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, errors.New("test error")), + ) + + err := ecloudHostGroupCreate(service, cmd, []string{}) + assert.Equal(t, "Error waiting for host group task to complete: Error waiting for command: Failed to retrieve task status: test error", err.Error()) + }) + + t.Run("CreateHostGroupError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostGroupCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--direction=IN", "--action=DROP"}) + + service.EXPECT().CreateHostGroup(gomock.Any()).Return(ecloud.TaskReference{}, errors.New("test error")).Times(1) + + err := ecloudHostGroupCreate(service, cmd, []string{}) + + assert.Equal(t, "Error creating host group: test error", err.Error()) + }) + + t.Run("GetHostGroupError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + cmd := ecloudHostGroupCreateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--direction=IN", "--action=DROP"}) + + gomock.InOrder( + service.EXPECT().CreateHostGroup(gomock.Any()).Return(ecloud.TaskReference{ResourceID: "hg-abcdef12"}, nil), + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, errors.New("test error")), + ) + + err := ecloudHostGroupCreate(service, cmd, []string{}) + + assert.Equal(t, "Error retrieving new host group: test error", err.Error()) + }) +} + +func Test_ecloudHostGroupUpdateCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudHostGroupUpdateCmd(nil).Args(nil, []string{"hg-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudHostGroupUpdateCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing host group", err.Error()) + }) +} + +func Test_ecloudHostGroupUpdate(t *testing.T) { + t.Run("SingleGroup", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + cmd := ecloudHostGroupUpdateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup"}) + + req := ecloud.PatchHostGroupRequest{ + Name: "testgroup", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "hg-abcdef12", + } + + gomock.InOrder( + service.EXPECT().PatchHostGroup("hg-abcdef12", req).Return(resp, nil), + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, nil), + ) + + ecloudHostGroupUpdate(service, cmd, []string{"hg-abcdef12"}) + }) + + t.Run("MultipleHostGroups", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + resp1 := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "hg-abcdef12", + } + + resp2 := ecloud.TaskReference{ + TaskID: "task-abcdef23", + ResourceID: "hg-12abcdef", + } + + gomock.InOrder( + service.EXPECT().PatchHostGroup("hg-abcdef12", gomock.Any()).Return(resp1, nil), + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, nil), + service.EXPECT().PatchHostGroup("hg-12abcdef", gomock.Any()).Return(resp2, nil), + service.EXPECT().GetHostGroup("hg-12abcdef").Return(ecloud.HostGroup{}, nil), + ) + + ecloudHostGroupUpdate(service, &cobra.Command{}, []string{"hg-abcdef12", "hg-12abcdef"}) + }) + + t.Run("WithWaitFlag_NoError_Succeeds", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + cmd := ecloudHostGroupUpdateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--wait"}) + + req := ecloud.PatchHostGroupRequest{ + Name: "testgroup", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "hg-abcdef12", + } + + gomock.InOrder( + service.EXPECT().PatchHostGroup("hg-abcdef12", req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil), + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, nil), + ) + + ecloudHostGroupUpdate(service, cmd, []string{"hg-abcdef12"}) + }) + + t.Run("WithWaitFlag_GetTaskError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + cmd := ecloudHostGroupUpdateCmd(nil) + cmd.ParseFlags([]string{"--name=testgroup", "--wait"}) + + req := ecloud.PatchHostGroupRequest{ + Name: "testgroup", + } + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "hg-abcdef12", + } + + gomock.InOrder( + service.EXPECT().PatchHostGroup("hg-abcdef12", req).Return(resp, nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, errors.New("test error")), + ) + + test_output.AssertErrorOutput(t, "Error waiting for task to complete for host group [hg-abcdef12]: Error waiting for command: Failed to retrieve task status: test error\n", func() { + ecloudHostGroupUpdate(service, cmd, []string{"hg-abcdef12"}) + }) + }) + + t.Run("PatchHostGroupError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().PatchHostGroup("hg-abcdef12", gomock.Any()).Return(ecloud.TaskReference{}, errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error updating host group [hg-abcdef12]: test error\n", func() { + ecloudHostGroupUpdate(service, &cobra.Command{}, []string{"hg-abcdef12"}) + }) + }) + + t.Run("GetHostGroupError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + resp := ecloud.TaskReference{ + TaskID: "task-abcdef12", + ResourceID: "hg-abcdef12", + } + + gomock.InOrder( + service.EXPECT().PatchHostGroup("hg-abcdef12", gomock.Any()).Return(resp, nil), + service.EXPECT().GetHostGroup("hg-abcdef12").Return(ecloud.HostGroup{}, errors.New("test error")), + ) + + test_output.AssertErrorOutput(t, "Error retrieving updated host group [hg-abcdef12]: test error\n", func() { + ecloudHostGroupUpdate(service, &cobra.Command{}, []string{"hg-abcdef12"}) + }) + }) +} + +func Test_ecloudHostGroupDeleteCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudHostGroupDeleteCmd(nil).Args(nil, []string{"hg-abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudHostGroupDeleteCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing host group", err.Error()) + }) +} + +func Test_ecloudHostGroupDelete(t *testing.T) { + t.Run("SingleGroup", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().DeleteHostGroup("hg-abcdef12").Return("task-abcdef12", nil).Times(1) + + ecloudHostGroupDelete(service, &cobra.Command{}, []string{"hg-abcdef12"}) + }) + + t.Run("MultipleHostGroups", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().DeleteHostGroup("hg-abcdef12").Return("task-abcdef12", nil), + service.EXPECT().DeleteHostGroup("hg-12abcdef").Return("task-abcdef23", nil), + ) + + ecloudHostGroupDelete(service, &cobra.Command{}, []string{"hg-abcdef12", "hg-12abcdef"}) + }) + + t.Run("WithWaitFlag_NoError_Succeeds", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + cmd := ecloudHostGroupDeleteCmd(nil) + cmd.ParseFlags([]string{"--wait"}) + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().DeleteHostGroup("hg-abcdef12").Return("task-abcdef12", nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil), + ) + + ecloudHostGroupDelete(service, cmd, []string{"hg-abcdef12"}) + }) + + t.Run("WithWaitFlag_GetTaskError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + cmd := ecloudHostGroupDeleteCmd(nil) + cmd.ParseFlags([]string{"--wait"}) + + gomock.InOrder( + service.EXPECT().DeleteHostGroup("hg-abcdef12").Return("task-abcdef12", nil), + service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, errors.New("test error")), + ) + + test_output.AssertErrorOutput(t, "Error waiting for task to complete for host group [hg-abcdef12]: Error waiting for command: Failed to retrieve task status: test error\n", func() { + ecloudHostGroupDelete(service, cmd, []string{"hg-abcdef12"}) + }) + }) + + t.Run("DeleteHostGroupError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().DeleteHostGroup("hg-abcdef12").Return("", errors.New("test error")).Times(1) + + test_output.AssertErrorOutput(t, "Error removing host group [hg-abcdef12]: test error\n", func() { + ecloudHostGroupDelete(service, &cobra.Command{}, []string{"hg-abcdef12"}) + }) + }) +} diff --git a/cmd/ecloud/ecloud_hostspec.go b/cmd/ecloud/ecloud_hostspec.go new file mode 100644 index 0000000..badd16b --- /dev/null +++ b/cmd/ecloud/ecloud_hostspec.go @@ -0,0 +1,88 @@ +package ecloud + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + "github.com/ukfast/cli/internal/pkg/factory" + "github.com/ukfast/cli/internal/pkg/helper" + "github.com/ukfast/cli/internal/pkg/output" + "github.com/ukfast/sdk-go/pkg/service/ecloud" +) + +func ecloudHostSpecRootCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "hostspec", + Short: "sub-commands relating to host specs", + } + + // Child commands + cmd.AddCommand(ecloudHostSpecListCmd(f)) + cmd.AddCommand(ecloudHostSpecShowCmd(f)) + + return cmd +} + +func ecloudHostSpecListCmd(f factory.ClientFactory) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "Lists host specs", + Long: "This command lists host specs", + Example: "ukfast ecloud hostspec list", + RunE: ecloudCobraRunEFunc(f, ecloudHostSpecList), + } + + cmd.Flags().String("policy", "", "Host policy ID for filtering") + + return cmd +} + +func ecloudHostSpecList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + params, err := helper.GetAPIRequestParametersFromFlags(cmd, + helper.NewStringFilterFlagOption("name", "name"), + helper.NewStringFilterFlagOption("policy", "host_policy_id"), + ) + if err != nil { + return err + } + + specs, err := service.GetHostSpecs(params) + if err != nil { + return fmt.Errorf("Error retrieving host specs: %s", err) + } + + return output.CommandOutput(cmd, OutputECloudHostSpecsProvider(specs)) +} + +func ecloudHostSpecShowCmd(f factory.ClientFactory) *cobra.Command { + return &cobra.Command{ + Use: "show ...", + Short: "Shows an host spec", + Long: "This command shows one or more host specs", + Example: "ukfast ecloud hostspec show hs--abcdef12", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Missing host spec") + } + + return nil + }, + RunE: ecloudCobraRunEFunc(f, ecloudHostSpecShow), + } +} + +func ecloudHostSpecShow(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { + var specs []ecloud.HostSpec + for _, arg := range args { + spec, err := service.GetHostSpec(arg) + if err != nil { + output.OutputWithErrorLevelf("Error retrieving host spec [%s]: %s", arg, err) + continue + } + + specs = append(specs, spec) + } + + return output.CommandOutput(cmd, OutputECloudHostSpecsProvider(specs)) +} diff --git a/cmd/ecloud/ecloud_hostspec_test.go b/cmd/ecloud/ecloud_hostspec_test.go new file mode 100644 index 0000000..a1c6856 --- /dev/null +++ b/cmd/ecloud/ecloud_hostspec_test.go @@ -0,0 +1,109 @@ +package ecloud + +import ( + "errors" + "testing" + + gomock "github.com/golang/mock/gomock" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/ukfast/cli/internal/pkg/clierrors" + "github.com/ukfast/cli/test/mocks" + "github.com/ukfast/cli/test/test_output" + "github.com/ukfast/sdk-go/pkg/service/ecloud" +) + +func Test_ecloudHostSpecList(t *testing.T) { + t.Run("DefaultRetrieve", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHostSpecs(gomock.Any()).Return([]ecloud.HostSpec{}, nil).Times(1) + + ecloudHostSpecList(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 := ecloudHostSpecList(service, cmd, []string{}) + + assert.IsType(t, &clierrors.ErrInvalidFlagValue{}, err) + }) + + t.Run("GetHostSpecsError_ReturnsError", func(t *testing.T) { + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHostSpecs(gomock.Any()).Return([]ecloud.HostSpec{}, errors.New("test error")).Times(1) + + err := ecloudHostSpecList(service, &cobra.Command{}, []string{}) + + assert.Equal(t, "Error retrieving host specs: test error", err.Error()) + }) +} + +func Test_ecloudHostSpecShowCmd_Args(t *testing.T) { + t.Run("ValidArgs_NoError", func(t *testing.T) { + err := ecloudHostSpecShowCmd(nil).Args(nil, []string{"hs--abcdef12"}) + + assert.Nil(t, err) + }) + + t.Run("InvalidArgs_Error", func(t *testing.T) { + err := ecloudHostSpecShowCmd(nil).Args(nil, []string{}) + + assert.NotNil(t, err) + assert.Equal(t, "Missing host spec", err.Error()) + }) +} + +func Test_ecloudHostSpecShow(t *testing.T) { + t.Run("SingleHostSpec", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHostSpec("hs--abcdef12").Return(ecloud.HostSpec{}, nil).Times(1) + + ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs--abcdef12"}) + }) + + t.Run("MultipleHostSpecs", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + gomock.InOrder( + service.EXPECT().GetHostSpec("hs--abcdef12").Return(ecloud.HostSpec{}, nil), + service.EXPECT().GetHostSpec("hs--abcdef23").Return(ecloud.HostSpec{}, nil), + ) + + ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs--abcdef12", "hs--abcdef23"}) + }) + + t.Run("GetHostSpecError_OutputsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + service := mocks.NewMockECloudService(mockCtrl) + + service.EXPECT().GetHostSpec("hs--abcdef12").Return(ecloud.HostSpec{}, errors.New("test error")) + + test_output.AssertErrorOutput(t, "Error retrieving host spec [hs--abcdef12]: test error\n", func() { + ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs--abcdef12"}) + }) + }) +} diff --git a/cmd/ecloud/ecloud_v1_host.go b/cmd/ecloud/ecloud_v1_host.go index 6e4f9f1..6d273d7 100644 --- a/cmd/ecloud/ecloud_v1_host.go +++ b/cmd/ecloud/ecloud_v1_host.go @@ -12,37 +12,37 @@ import ( "github.com/ukfast/sdk-go/pkg/service/ecloud" ) -func ecloudHostRootCmd(f factory.ClientFactory) *cobra.Command { +func ecloudV1HostRootCmd(f factory.ClientFactory) *cobra.Command { cmd := &cobra.Command{ - Use: "host", + Use: "v1host", Short: "sub-commands relating to hosts", } // Child commands - cmd.AddCommand(ecloudHostListCmd(f)) - cmd.AddCommand(ecloudHostShowCmd(f)) + cmd.AddCommand(ecloudV1HostListCmd(f)) + cmd.AddCommand(ecloudV1HostShowCmd(f)) return cmd } -func ecloudHostListCmd(f factory.ClientFactory) *cobra.Command { +func ecloudV1HostListCmd(f factory.ClientFactory) *cobra.Command { return &cobra.Command{ Use: "list", Short: "Lists hosts", Long: "This command lists hosts", - Example: "ukfast ecloud host list", + Example: "ukfast ecloud v1host list", RunE: func(cmd *cobra.Command, args []string) error { c, err := f.NewClient() if err != nil { return err } - return ecloudHostList(c.ECloudService(), cmd, args) + return ecloudV1HostList(c.ECloudService(), cmd, args) }, } } -func ecloudHostList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { +func ecloudV1HostList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { params, err := helper.GetAPIRequestParametersFromFlags(cmd) if err != nil { return err @@ -56,12 +56,12 @@ func ecloudHostList(service ecloud.ECloudService, cmd *cobra.Command, args []str return output.CommandOutput(cmd, OutputECloudV1HostsProvider(hosts)) } -func ecloudHostShowCmd(f factory.ClientFactory) *cobra.Command { +func ecloudV1HostShowCmd(f factory.ClientFactory) *cobra.Command { return &cobra.Command{ Use: "show ...", Short: "Shows a host", Long: "This command shows one or more hosts", - Example: "ukfast ecloud vm host 123", + Example: "ukfast ecloud v1host 123", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errors.New("Missing host") @@ -75,12 +75,12 @@ func ecloudHostShowCmd(f factory.ClientFactory) *cobra.Command { return err } - return ecloudHostShow(c.ECloudService(), cmd, args) + return ecloudV1HostShow(c.ECloudService(), cmd, args) }, } } -func ecloudHostShow(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { +func ecloudV1HostShow(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { var hosts []ecloud.V1Host for _, arg := range args { hostID, err := strconv.Atoi(arg) diff --git a/cmd/ecloud/ecloud_v1_host_test.go b/cmd/ecloud/ecloud_v1_host_test.go index 60c60c0..99c01cb 100644 --- a/cmd/ecloud/ecloud_v1_host_test.go +++ b/cmd/ecloud/ecloud_v1_host_test.go @@ -13,7 +13,7 @@ import ( "github.com/ukfast/sdk-go/pkg/service/ecloud" ) -func Test_ecloudHostList(t *testing.T) { +func Test_ecloudV1HostList(t *testing.T) { t.Run("DefaultRetrieve", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() @@ -22,7 +22,7 @@ func Test_ecloudHostList(t *testing.T) { service.EXPECT().GetV1Hosts(gomock.Any()).Return([]ecloud.V1Host{}, nil).Times(1) - ecloudHostList(service, &cobra.Command{}, []string{}) + ecloudV1HostList(service, &cobra.Command{}, []string{}) }) t.Run("MalformedFlag_ReturnsError", func(t *testing.T) { @@ -33,7 +33,7 @@ func Test_ecloudHostList(t *testing.T) { cmd := &cobra.Command{} cmd.Flags().StringArray("filter", []string{"invalidfilter"}, "") - err := ecloudHostList(service, cmd, []string{}) + err := ecloudV1HostList(service, cmd, []string{}) assert.IsType(t, &clierrors.ErrInvalidFlagValue{}, err) }) @@ -47,28 +47,28 @@ func Test_ecloudHostList(t *testing.T) { service.EXPECT().GetV1Hosts(gomock.Any()).Return([]ecloud.V1Host{}, errors.New("test error")).Times(1) - err := ecloudHostList(service, &cobra.Command{}, []string{}) + err := ecloudV1HostList(service, &cobra.Command{}, []string{}) assert.Equal(t, "Error retrieving hosts: test error", err.Error()) }) } -func Test_ecloudHostShowCmd_Args(t *testing.T) { +func Test_ecloudV1HostShowCmd_Args(t *testing.T) { t.Run("ValidArgs_NoError", func(t *testing.T) { - err := ecloudHostShowCmd(nil).Args(nil, []string{"123"}) + err := ecloudV1HostShowCmd(nil).Args(nil, []string{"123"}) assert.Nil(t, err) }) t.Run("InvalidArgs_Error", func(t *testing.T) { - err := ecloudHostShowCmd(nil).Args(nil, []string{}) + err := ecloudV1HostShowCmd(nil).Args(nil, []string{}) assert.NotNil(t, err) assert.Equal(t, "Missing host", err.Error()) }) } -func Test_ecloudHostShow(t *testing.T) { +func Test_ecloudV1HostShow(t *testing.T) { t.Run("SingleHost", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() @@ -77,7 +77,7 @@ func Test_ecloudHostShow(t *testing.T) { service.EXPECT().GetV1Host(123).Return(ecloud.V1Host{}, nil).Times(1) - ecloudHostShow(service, &cobra.Command{}, []string{"123"}) + ecloudV1HostShow(service, &cobra.Command{}, []string{"123"}) }) t.Run("MultipleHosts", func(t *testing.T) { @@ -91,7 +91,7 @@ func Test_ecloudHostShow(t *testing.T) { service.EXPECT().GetV1Host(456).Return(ecloud.V1Host{}, nil), ) - ecloudHostShow(service, &cobra.Command{}, []string{"123", "456"}) + ecloudV1HostShow(service, &cobra.Command{}, []string{"123", "456"}) }) t.Run("GetV1HostID_OutputsError", func(t *testing.T) { @@ -101,7 +101,7 @@ func Test_ecloudHostShow(t *testing.T) { service := mocks.NewMockECloudService(mockCtrl) test_output.AssertErrorOutput(t, "Invalid host ID [abc]\n", func() { - ecloudHostShow(service, &cobra.Command{}, []string{"abc"}) + ecloudV1HostShow(service, &cobra.Command{}, []string{"abc"}) }) }) @@ -114,7 +114,7 @@ func Test_ecloudHostShow(t *testing.T) { service.EXPECT().GetV1Host(123).Return(ecloud.V1Host{}, errors.New("test error")) test_output.AssertErrorOutput(t, "Error retrieving host [123]: test error\n", func() { - ecloudHostShow(service, &cobra.Command{}, []string{"123"}) + ecloudV1HostShow(service, &cobra.Command{}, []string{"123"}) }) }) } diff --git a/cmd/ecloud/output.go b/cmd/ecloud/output.go index ea54718..fc973b7 100644 --- a/cmd/ecloud/output.go +++ b/cmd/ecloud/output.go @@ -407,3 +407,19 @@ func OutputECloudNetworkRulesProvider(rules []ecloud.NetworkRule) output.OutputH func OutputECloudNetworkRulePortsProvider(rules []ecloud.NetworkRulePort) output.OutputHandlerDataProvider { return output.NewSerializedOutputHandlerDataProvider(rules).WithDefaultFields([]string{"id", "name", "network_rule_id", "protocol", "source", "destination"}) } + +func OutputECloudHostGroupsProvider(groups []ecloud.HostGroup) output.OutputHandlerDataProvider { + return output.NewSerializedOutputHandlerDataProvider(groups).WithDefaultFields([]string{"id", "name", "vpc_id", "sync_status"}) +} + +func OutputECloudHostsProvider(hosts []ecloud.Host) output.OutputHandlerDataProvider { + return output.NewSerializedOutputHandlerDataProvider(hosts).WithDefaultFields([]string{"id", "name", "host_group_id", "sync_status"}) +} + +func OutputECloudHostSpecsProvider(specs []ecloud.HostSpec) output.OutputHandlerDataProvider { + return output.NewSerializedOutputHandlerDataProvider(specs).WithDefaultFields([]string{"id", "name", "cpu_sockets", "cpu_cores", "cpu_type", "cpu_clock_speed", "ram_capacity"}) +} + +func OutputECloudAvailabilityZonesProvider(azs []ecloud.AvailabilityZone) output.OutputHandlerDataProvider { + return output.NewSerializedOutputHandlerDataProvider(azs).WithDefaultFields([]string{"id", "name", "region_id"}) +} diff --git a/test/mocks/mock_ecloudservice.go b/test/mocks/mock_ecloudservice.go index decc806..3233eaf 100644 --- a/test/mocks/mock_ecloudservice.go +++ b/test/mocks/mock_ecloudservice.go @@ -140,10 +140,10 @@ func (mr *MockECloudServiceMockRecorder) CreateFloatingIP(arg0 interface{}) *gom } // CreateHost mocks base method -func (m *MockECloudService) CreateHost(arg0 ecloud.CreateHostRequest) (string, error) { +func (m *MockECloudService) CreateHost(arg0 ecloud.CreateHostRequest) (ecloud.TaskReference, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateHost", arg0) - ret0, _ := ret[0].(string) + ret0, _ := ret[0].(ecloud.TaskReference) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -155,10 +155,10 @@ func (mr *MockECloudServiceMockRecorder) CreateHost(arg0 interface{}) *gomock.Ca } // CreateHostGroup mocks base method -func (m *MockECloudService) CreateHostGroup(arg0 ecloud.CreateHostGroupRequest) (string, error) { +func (m *MockECloudService) CreateHostGroup(arg0 ecloud.CreateHostGroupRequest) (ecloud.TaskReference, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateHostGroup", arg0) - ret0, _ := ret[0].(string) + ret0, _ := ret[0].(ecloud.TaskReference) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -466,11 +466,12 @@ func (mr *MockECloudServiceMockRecorder) DeleteFloatingIP(arg0 interface{}) *gom } // DeleteHost mocks base method -func (m *MockECloudService) DeleteHost(arg0 string) error { +func (m *MockECloudService) DeleteHost(arg0 string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteHost", arg0) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 } // DeleteHost indicates an expected call of DeleteHost @@ -480,11 +481,12 @@ func (mr *MockECloudServiceMockRecorder) DeleteHost(arg0 interface{}) *gomock.Ca } // DeleteHostGroup mocks base method -func (m *MockECloudService) DeleteHostGroup(arg0 string) error { +func (m *MockECloudService) DeleteHostGroup(arg0 string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteHostGroup", arg0) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 } // DeleteHostGroup indicates an expected call of DeleteHostGroup @@ -3644,11 +3646,12 @@ func (mr *MockECloudServiceMockRecorder) PatchFloatingIP(arg0, arg1 interface{}) } // PatchHost mocks base method -func (m *MockECloudService) PatchHost(arg0 string, arg1 ecloud.PatchHostRequest) error { +func (m *MockECloudService) PatchHost(arg0 string, arg1 ecloud.PatchHostRequest) (ecloud.TaskReference, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PatchHost", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(ecloud.TaskReference) + ret1, _ := ret[1].(error) + return ret0, ret1 } // PatchHost indicates an expected call of PatchHost @@ -3658,11 +3661,12 @@ func (mr *MockECloudServiceMockRecorder) PatchHost(arg0, arg1 interface{}) *gomo } // PatchHostGroup mocks base method -func (m *MockECloudService) PatchHostGroup(arg0 string, arg1 ecloud.PatchHostGroupRequest) error { +func (m *MockECloudService) PatchHostGroup(arg0 string, arg1 ecloud.PatchHostGroupRequest) (ecloud.TaskReference, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PatchHostGroup", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(ecloud.TaskReference) + ret1, _ := ret[1].(error) + return ret0, ret1 } // PatchHostGroup indicates an expected call of PatchHostGroup From 6dca909b11f18b1ee727de1f05eaa47b6518d41b Mon Sep 17 00:00:00 2001 From: Lee Spottiswood Date: Tue, 22 Jun 2021 15:24:13 +0100 Subject: [PATCH 4/6] update filtering --- cmd/ecloud/ecloud_availabilityzone.go | 5 +++-- cmd/ecloud/ecloud_host.go | 3 +-- cmd/ecloud/ecloud_hostgroup.go | 3 +-- cmd/ecloud/ecloud_hostspec.go | 3 +-- go.mod | 2 +- go.sum | 4 ++-- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/cmd/ecloud/ecloud_availabilityzone.go b/cmd/ecloud/ecloud_availabilityzone.go index a5e4621..fd72064 100644 --- a/cmd/ecloud/ecloud_availabilityzone.go +++ b/cmd/ecloud/ecloud_availabilityzone.go @@ -36,7 +36,8 @@ func ecloudAvailabilityZoneListCmd(f factory.ClientFactory) *cobra.Command { RunE: ecloudCobraRunEFunc(f, ecloudAvailabilityZoneList), } - cmd.Flags().String("policy", "", "Availability policy ID for filtering") + cmd.Flags().String("name", "", "Availability zone name for filtering") + cmd.Flags().String("region", "", "Region ID for filtering") return cmd } @@ -44,7 +45,7 @@ func ecloudAvailabilityZoneListCmd(f factory.ClientFactory) *cobra.Command { func ecloudAvailabilityZoneList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { params, err := helper.GetAPIRequestParametersFromFlags(cmd, helper.NewStringFilterFlagOption("name", "name"), - helper.NewStringFilterFlagOption("policy", "availability_policy_id"), + helper.NewStringFilterFlagOption("region", "region_id"), ) if err != nil { return err diff --git a/cmd/ecloud/ecloud_host.go b/cmd/ecloud/ecloud_host.go index 7ac3b85..e10a51c 100644 --- a/cmd/ecloud/ecloud_host.go +++ b/cmd/ecloud/ecloud_host.go @@ -36,7 +36,7 @@ func ecloudHostListCmd(f factory.ClientFactory) *cobra.Command { RunE: ecloudCobraRunEFunc(f, ecloudHostList), } - cmd.Flags().String("policy", "", "Host policy ID for filtering") + cmd.Flags().String("name", "", "Host name for filtering") return cmd } @@ -44,7 +44,6 @@ func ecloudHostListCmd(f factory.ClientFactory) *cobra.Command { func ecloudHostList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { params, err := helper.GetAPIRequestParametersFromFlags(cmd, helper.NewStringFilterFlagOption("name", "name"), - helper.NewStringFilterFlagOption("policy", "host_policy_id"), ) if err != nil { return err diff --git a/cmd/ecloud/ecloud_hostgroup.go b/cmd/ecloud/ecloud_hostgroup.go index 00690cc..b90bfc2 100644 --- a/cmd/ecloud/ecloud_hostgroup.go +++ b/cmd/ecloud/ecloud_hostgroup.go @@ -36,7 +36,7 @@ func ecloudHostGroupListCmd(f factory.ClientFactory) *cobra.Command { RunE: ecloudCobraRunEFunc(f, ecloudHostGroupList), } - cmd.Flags().String("policy", "", "Host policy ID for filtering") + cmd.Flags().String("name", "", "Host group name for filtering") return cmd } @@ -44,7 +44,6 @@ func ecloudHostGroupListCmd(f factory.ClientFactory) *cobra.Command { func ecloudHostGroupList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { params, err := helper.GetAPIRequestParametersFromFlags(cmd, helper.NewStringFilterFlagOption("name", "name"), - helper.NewStringFilterFlagOption("policy", "host_policy_id"), ) if err != nil { return err diff --git a/cmd/ecloud/ecloud_hostspec.go b/cmd/ecloud/ecloud_hostspec.go index badd16b..8727103 100644 --- a/cmd/ecloud/ecloud_hostspec.go +++ b/cmd/ecloud/ecloud_hostspec.go @@ -33,7 +33,7 @@ func ecloudHostSpecListCmd(f factory.ClientFactory) *cobra.Command { RunE: ecloudCobraRunEFunc(f, ecloudHostSpecList), } - cmd.Flags().String("policy", "", "Host policy ID for filtering") + cmd.Flags().String("name", "", "Host spec name for filtering") return cmd } @@ -41,7 +41,6 @@ func ecloudHostSpecListCmd(f factory.ClientFactory) *cobra.Command { func ecloudHostSpecList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error { params, err := helper.GetAPIRequestParametersFromFlags(cmd, helper.NewStringFilterFlagOption("name", "name"), - helper.NewStringFilterFlagOption("policy", "host_policy_id"), ) if err != nil { return err diff --git a/go.mod b/go.mod index e621c11..7be5bf1 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 github.com/stretchr/testify v1.6.1 - github.com/ukfast/sdk-go v1.4.6 + github.com/ukfast/sdk-go v1.4.7 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b // indirect gopkg.in/go-playground/assert.v1 v1.2.1 k8s.io/client-go v11.0.0+incompatible diff --git a/go.sum b/go.sum index 5cfe590..83973ae 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4U github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ukfast/go-durationstring v1.0.0 h1:kgPuA7XjLjgLDfkG8j0MpolxcZh/eMdiVoOIFD/uc5I= github.com/ukfast/go-durationstring v1.0.0/go.mod h1:Ci81n51kfxlKUIaLY9cINIKRO94VTqV+iCGbOMTb0V8= -github.com/ukfast/sdk-go v1.4.6 h1:1U2KeWXwNWhYiyRdYbqlJeeMvjo+/DDyxXqO+f82VBg= -github.com/ukfast/sdk-go v1.4.6/go.mod h1:tspweEP77MHhVEYgEEieKAKGITFgwkYl1q5fLh4HZAo= +github.com/ukfast/sdk-go v1.4.7 h1:lMGbFToegL0Z0m3Z51O7zbe6hMHRT8sWenYx26bCEJA= +github.com/ukfast/sdk-go v1.4.7/go.mod h1:tspweEP77MHhVEYgEEieKAKGITFgwkYl1q5fLh4HZAo= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= From 75393be1f52581bf08375509bfe29f2f3ab8fd0f Mon Sep 17 00:00:00 2001 From: Lee Spottiswood Date: Tue, 22 Jun 2021 16:32:16 +0100 Subject: [PATCH 5/6] use ecloudCobraRunEFunc --- cmd/ecloud/ecloud_image_metadata.go | 9 +-------- cmd/ecloud/ecloud_image_parameter.go | 9 +-------- cmd/ecloud/ecloud_instance_consolesession.go | 9 +-------- cmd/ecloud/ecloud_routerthroughput.go | 18 ++---------------- 4 files changed, 5 insertions(+), 40 deletions(-) diff --git a/cmd/ecloud/ecloud_image_metadata.go b/cmd/ecloud/ecloud_image_metadata.go index ca2f570..702a643 100644 --- a/cmd/ecloud/ecloud_image_metadata.go +++ b/cmd/ecloud/ecloud_image_metadata.go @@ -36,14 +36,7 @@ func ecloudImageMetadataListCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudImageMetadataList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudImageMetadataList), } } diff --git a/cmd/ecloud/ecloud_image_parameter.go b/cmd/ecloud/ecloud_image_parameter.go index ab0f93a..6a34359 100644 --- a/cmd/ecloud/ecloud_image_parameter.go +++ b/cmd/ecloud/ecloud_image_parameter.go @@ -36,14 +36,7 @@ func ecloudImageParameterListCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudImageParameterList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudImageParameterList), } } diff --git a/cmd/ecloud/ecloud_instance_consolesession.go b/cmd/ecloud/ecloud_instance_consolesession.go index 896bc00..75ad8c0 100644 --- a/cmd/ecloud/ecloud_instance_consolesession.go +++ b/cmd/ecloud/ecloud_instance_consolesession.go @@ -36,14 +36,7 @@ func ecloudInstanceConsoleSessionCreateCmd(f factory.ClientFactory) *cobra.Comma return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudInstanceConsoleSessionCreate(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudInstanceConsoleSessionCreate), } cmd.Flags().Bool("browser", false, "Indicates session should be opened in default browser") diff --git a/cmd/ecloud/ecloud_routerthroughput.go b/cmd/ecloud/ecloud_routerthroughput.go index 0d78ec0..eec0f12 100644 --- a/cmd/ecloud/ecloud_routerthroughput.go +++ b/cmd/ecloud/ecloud_routerthroughput.go @@ -30,14 +30,7 @@ func ecloudRouterThroughputListCmd(f factory.ClientFactory) *cobra.Command { Short: "Lists router throughputs", Long: "This command lists router throughputs", Example: "ukfast ecloud routerthroughput list", - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudRouterThroughputList(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudRouterThroughputList), } cmd.Flags().String("name", "", "Router throughput name for filtering") @@ -76,14 +69,7 @@ func ecloudRouterThroughputShowCmd(f factory.ClientFactory) *cobra.Command { return nil }, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := f.NewClient() - if err != nil { - return err - } - - return ecloudRouterThroughputShow(c.ECloudService(), cmd, args) - }, + RunE: ecloudCobraRunEFunc(f, ecloudRouterThroughputShow), } } From e535e7a1a6130b335177545cf114914ef4e7d593 Mon Sep 17 00:00:00 2001 From: Lee Spottiswood Date: Wed, 23 Jun 2021 09:19:12 +0100 Subject: [PATCH 6/6] update examples --- cmd/ecloud/ecloud_hostgroup.go | 4 ++-- cmd/ecloud/ecloud_hostspec.go | 2 +- cmd/ecloud/ecloud_hostspec_test.go | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/ecloud/ecloud_hostgroup.go b/cmd/ecloud/ecloud_hostgroup.go index b90bfc2..7450e1a 100644 --- a/cmd/ecloud/ecloud_hostgroup.go +++ b/cmd/ecloud/ecloud_hostgroup.go @@ -94,7 +94,7 @@ func ecloudHostGroupCreateCmd(f factory.ClientFactory) *cobra.Command { Use: "create", Short: "Creates a host group", Long: "This command creates a host group", - Example: "ukfast ecloud hostgroup create --policy np-abcdef12", + Example: "ukfast ecloud hostgroup create --policy hg-abcdef12", RunE: ecloudCobraRunEFunc(f, ecloudHostGroupCreate), } @@ -145,7 +145,7 @@ func ecloudHostGroupUpdateCmd(f factory.ClientFactory) *cobra.Command { Use: "update ...", Short: "Updates a host group", Long: "This command updates one or more host groups", - Example: "ukfast ecloud hostgroup update np-abcdef12 --name \"my group\"", + Example: "ukfast ecloud hostgroup update hg-abcdef12 --name \"my group\"", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errors.New("Missing host group") diff --git a/cmd/ecloud/ecloud_hostspec.go b/cmd/ecloud/ecloud_hostspec.go index 8727103..6a758d0 100644 --- a/cmd/ecloud/ecloud_hostspec.go +++ b/cmd/ecloud/ecloud_hostspec.go @@ -59,7 +59,7 @@ func ecloudHostSpecShowCmd(f factory.ClientFactory) *cobra.Command { Use: "show ...", Short: "Shows an host spec", Long: "This command shows one or more host specs", - Example: "ukfast ecloud hostspec show hs--abcdef12", + Example: "ukfast ecloud hostspec show hs-abcdef12", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errors.New("Missing host spec") diff --git a/cmd/ecloud/ecloud_hostspec_test.go b/cmd/ecloud/ecloud_hostspec_test.go index a1c6856..f4dfbcd 100644 --- a/cmd/ecloud/ecloud_hostspec_test.go +++ b/cmd/ecloud/ecloud_hostspec_test.go @@ -55,7 +55,7 @@ func Test_ecloudHostSpecList(t *testing.T) { func Test_ecloudHostSpecShowCmd_Args(t *testing.T) { t.Run("ValidArgs_NoError", func(t *testing.T) { - err := ecloudHostSpecShowCmd(nil).Args(nil, []string{"hs--abcdef12"}) + err := ecloudHostSpecShowCmd(nil).Args(nil, []string{"hs-abcdef12"}) assert.Nil(t, err) }) @@ -75,9 +75,9 @@ func Test_ecloudHostSpecShow(t *testing.T) { service := mocks.NewMockECloudService(mockCtrl) - service.EXPECT().GetHostSpec("hs--abcdef12").Return(ecloud.HostSpec{}, nil).Times(1) + service.EXPECT().GetHostSpec("hs-abcdef12").Return(ecloud.HostSpec{}, nil).Times(1) - ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs--abcdef12"}) + ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs-abcdef12"}) }) t.Run("MultipleHostSpecs", func(t *testing.T) { @@ -87,11 +87,11 @@ func Test_ecloudHostSpecShow(t *testing.T) { service := mocks.NewMockECloudService(mockCtrl) gomock.InOrder( - service.EXPECT().GetHostSpec("hs--abcdef12").Return(ecloud.HostSpec{}, nil), - service.EXPECT().GetHostSpec("hs--abcdef23").Return(ecloud.HostSpec{}, nil), + service.EXPECT().GetHostSpec("hs-abcdef12").Return(ecloud.HostSpec{}, nil), + service.EXPECT().GetHostSpec("hs-abcdef23").Return(ecloud.HostSpec{}, nil), ) - ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs--abcdef12", "hs--abcdef23"}) + ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs-abcdef12", "hs-abcdef23"}) }) t.Run("GetHostSpecError_OutputsError", func(t *testing.T) { @@ -100,10 +100,10 @@ func Test_ecloudHostSpecShow(t *testing.T) { service := mocks.NewMockECloudService(mockCtrl) - service.EXPECT().GetHostSpec("hs--abcdef12").Return(ecloud.HostSpec{}, errors.New("test error")) + service.EXPECT().GetHostSpec("hs-abcdef12").Return(ecloud.HostSpec{}, errors.New("test error")) - test_output.AssertErrorOutput(t, "Error retrieving host spec [hs--abcdef12]: test error\n", func() { - ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs--abcdef12"}) + test_output.AssertErrorOutput(t, "Error retrieving host spec [hs-abcdef12]: test error\n", func() { + ecloudHostSpecShow(service, &cobra.Command{}, []string{"hs-abcdef12"}) }) }) }