From c4a71859a0f42715669fdfbcab157e297e1d7ca5 Mon Sep 17 00:00:00 2001 From: Lee Spottiswood Date: Mon, 18 Oct 2021 14:57:44 +0100 Subject: [PATCH] add method for retrieving floating IPs indirectly associated with instances (#99) --- pkg/service/ecloud/service.go | 2 + pkg/service/ecloud/service_instance.go | 47 ++++++++++++ pkg/service/ecloud/service_instance_test.go | 83 +++++++++++++++++++++ 3 files changed, 132 insertions(+) diff --git a/pkg/service/ecloud/service.go b/pkg/service/ecloud/service.go index 513bb04..50e329a 100644 --- a/pkg/service/ecloud/service.go +++ b/pkg/service/ecloud/service.go @@ -173,6 +173,8 @@ type ECloudService interface { GetInstanceTasksPaginated(instanceID string, parameters connection.APIRequestParameters) (*PaginatedTask, error) AttachInstanceVolume(instanceID string, req AttachDetachInstanceVolumeRequest) (string, error) DetachInstanceVolume(instanceID string, req AttachDetachInstanceVolumeRequest) (string, error) + GetInstanceFloatingIPs(instanceID string, parameters connection.APIRequestParameters) ([]FloatingIP, error) + GetInstanceFloatingIPsPaginated(instanceID string, parameters connection.APIRequestParameters) (*PaginatedFloatingIP, error) // Floating IP GetFloatingIPs(parameters connection.APIRequestParameters) ([]FloatingIP, error) diff --git a/pkg/service/ecloud/service_instance.go b/pkg/service/ecloud/service_instance.go index ad84b0a..ba7c4a6 100644 --- a/pkg/service/ecloud/service_instance.go +++ b/pkg/service/ecloud/service_instance.go @@ -640,3 +640,50 @@ func (s *Service) detachInstanceVolumeResponseBody(instanceID string, req Attach return nil }) } + +// GetInstanceFloatingIPs retrieves a list of instance fips +func (s *Service) GetInstanceFloatingIPs(instanceID string, parameters connection.APIRequestParameters) ([]FloatingIP, error) { + var fips []FloatingIP + + return fips, connection.InvokeRequestAll( + func(p connection.APIRequestParameters) (connection.Paginated, error) { + return s.GetInstanceFloatingIPsPaginated(instanceID, p) + }, + func(response connection.Paginated) { + for _, fip := range response.(*PaginatedFloatingIP).Items { + fips = append(fips, fip) + } + }, + parameters, + ) +} + +// GetInstanceFloatingIPsPaginated retrieves a paginated list of instance floating IPs +func (s *Service) GetInstanceFloatingIPsPaginated(instanceID string, parameters connection.APIRequestParameters) (*PaginatedFloatingIP, error) { + body, err := s.getInstanceFloatingIPsPaginatedResponseBody(instanceID, parameters) + + return NewPaginatedFloatingIP(func(p connection.APIRequestParameters) (connection.Paginated, error) { + return s.GetInstanceFloatingIPsPaginated(instanceID, p) + }, parameters, body.Metadata.Pagination, body.Data), err +} + +func (s *Service) getInstanceFloatingIPsPaginatedResponseBody(instanceID string, parameters connection.APIRequestParameters) (*GetFloatingIPSliceResponseBody, error) { + body := &GetFloatingIPSliceResponseBody{} + + if instanceID == "" { + return body, fmt.Errorf("invalid instance id") + } + + response, err := s.connection.Get(fmt.Sprintf("/ecloud/v2/instances/%s/floating-ips", instanceID), parameters) + if err != nil { + return body, err + } + + return body, response.HandleResponse(body, func(resp *connection.APIResponse) error { + if response.StatusCode == 404 { + return &InstanceNotFoundError{ID: instanceID} + } + + return nil + }) +} diff --git a/pkg/service/ecloud/service_instance_test.go b/pkg/service/ecloud/service_instance_test.go index 1b82bfe..2845e2b 100644 --- a/pkg/service/ecloud/service_instance_test.go +++ b/pkg/service/ecloud/service_instance_test.go @@ -1599,3 +1599,86 @@ func TestDetachInstanceVolume(t *testing.T) { assert.IsType(t, &InstanceNotFoundError{}, err) }) } + +func TestGetInstanceFloatingIPs(t *testing.T) { + t.Run("Single", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + c := mocks.NewMockConnection(mockCtrl) + + s := Service{ + connection: c, + } + + c.EXPECT().Get("/ecloud/v2/instances/i-abcdef12/floating-ips", gomock.Any()).Return(&connection.APIResponse{ + Response: &http.Response{ + Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"data\":[{\"id\":\"vol-abcdef12\"}],\"meta\":{\"pagination\":{\"total_pages\":1}}}"))), + StatusCode: 200, + }, + }, nil) + + instances, err := s.GetInstanceFloatingIPs("i-abcdef12", connection.APIRequestParameters{}) + + assert.Nil(t, err) + assert.Len(t, instances, 1) + assert.Equal(t, "vol-abcdef12", instances[0].ID) + }) + + t.Run("ConnectionError_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + c := mocks.NewMockConnection(mockCtrl) + + s := Service{ + connection: c, + } + + c.EXPECT().Get("/ecloud/v2/instances/i-abcdef12/floating-ips", gomock.Any()).Return(&connection.APIResponse{}, errors.New("test error 1")) + + _, err := s.GetInstanceFloatingIPs("i-abcdef12", connection.APIRequestParameters{}) + + assert.NotNil(t, err) + assert.Equal(t, "test error 1", err.Error()) + }) + + t.Run("InvalidInstanceID_ReturnsError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + c := mocks.NewMockConnection(mockCtrl) + + s := Service{ + connection: c, + } + + _, err := s.GetInstanceFloatingIPs("", connection.APIRequestParameters{}) + + assert.NotNil(t, err) + assert.Equal(t, "invalid instance id", err.Error()) + }) + + t.Run("404_ReturnsInstanceNotFoundError", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + c := mocks.NewMockConnection(mockCtrl) + + s := Service{ + connection: c, + } + + c.EXPECT().Get("/ecloud/v2/instances/i-abcdef12/floating-ips", gomock.Any()).Return(&connection.APIResponse{ + Response: &http.Response{ + Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + StatusCode: 404, + }, + }, nil) + + _, err := s.GetInstanceFloatingIPs("i-abcdef12", connection.APIRequestParameters{}) + + assert.NotNil(t, err) + assert.IsType(t, &InstanceNotFoundError{}, err) + }) +}