diff --git a/cmd/cloudFoundryDeleteService.go b/cmd/cloudFoundryDeleteService.go index 36ff763c1..a35204f7b 100644 --- a/cmd/cloudFoundryDeleteService.go +++ b/cmd/cloudFoundryDeleteService.go @@ -2,6 +2,7 @@ package cmd import ( "bytes" + "encoding/json" "fmt" "strings" @@ -70,33 +71,29 @@ func cloudFoundryDeleteServiceKeys(options *cloudFoundryDeleteServiceOptions, c log.Entry().Info("Deleting inherent Service Keys") - var cfFindServiceKeysScript = []string{"service-keys", options.CfServiceInstance} - - var serviceKeyBytes bytes.Buffer - c.Stdout(&serviceKeyBytes) - - err := c.RunExecutable("cf", cfFindServiceKeysScript...) - + ServiceGUID, err := cloudFoundryFindServiceGUID(options, c) if err != nil { - return fmt.Errorf("Failed to Delete Service Key, most likely your service doesn't exist: %w", err) + return fmt.Errorf("Failed to determine Service Instance GUID: %w", err) } - if len(serviceKeyBytes.String()) == 0 { - log.Entry().Info("No service key could be retrieved for your requested Service") + log.Entry().WithField("Service Instance GUID :", ServiceGUID).Info("ServiceGUID") + + ServiceKeyNames, err := cloudFoundryFindServiceKeyNames(ServiceGUID, c) + if err != nil { + return fmt.Errorf("Failed to determine Service Key names: %w", err) + } + if len(ServiceKeyNames) == 0 { + log.Entry().Info("No service key names could be retrieved for your requested Service") return nil } - var lines []string = strings.Split(serviceKeyBytes.String(), "\n") - if len(lines) <= 4 { - log.Entry().Info("No Service Keys active to be deleted") - return nil - } - var numberOfLines = len(lines) - log.Entry().WithField("Number of service keys :", numberOfLines-4).Info("ServiceKey") + log.Entry().WithField("Number of service keys :", len(ServiceKeyNames)).Info("ServiceKey") //Deleting all matched Service Keys for Service - for i := 3; i <= numberOfLines-2; i++ { - log.Entry().WithField("Deleting Service Key", lines[i]).Info("ServiceKeyDeletion") - var cfDeleteServiceKeyScript = []string{"delete-service-key", options.CfServiceInstance, lines[i], "-f"} + for _, serviceKey := range ServiceKeyNames { + log.Entry().WithField("Service key :", serviceKey).Info("ServiceKey") + log.Entry().WithField("Deleting Service Key", serviceKey).Info("ServiceKeyDeletion") + + var cfDeleteServiceKeyScript = []string{"delete-service-key", options.CfServiceInstance, serviceKey, "-f"} if !options.CfAsync { cfDeleteServiceKeyScript = append(cfDeleteServiceKeyScript, "--wait") } @@ -105,10 +102,75 @@ func cloudFoundryDeleteServiceKeys(options *cloudFoundryDeleteServiceOptions, c return fmt.Errorf("Failed to Delete Service Key: %w", err) } } + log.Entry().Info("ServiceKeys have been deleted!") return nil } +func cloudFoundryFindServiceKeyNames(ServiceGUID string, c command.ExecRunner) (ServiceKeyNames []string, err error) { + + type Resource struct { + Name string `json:"name"` + Type string `json:"type"` + } + type ServiceKeysResponse struct { + Resources []Resource `json:"resources"` + } + + // Retrieve list of Service Keys bound to Service Instance + var cfFindServiceKeysScript = []string{"curl", "/v3/service_credential_bindings?service_instance_guids=" + ServiceGUID} + var serviceKeyBytes bytes.Buffer + + c.Stdout(&serviceKeyBytes) + err = c.RunExecutable("cf", cfFindServiceKeysScript...) + + if err != nil { + return ServiceKeyNames, fmt.Errorf("Failed to find any related Service Keys for the Service Instance, most likely your service doesn't exist: %w", err) + } + + if len(serviceKeyBytes.String()) == 0 { + log.Entry().Info("No service key could be retrieved for your requested Service") + return ServiceKeyNames, nil + } + var serviceKeys = serviceKeyBytes.String() + var response ServiceKeysResponse + + err = json.Unmarshal([]byte(serviceKeys), &response) + if err != nil { + fmt.Println("Error parsing JSON:", err) + return + } + if len(response.Resources) == 0 { + log.Entry().Info("No service key could be retrieved for your requested Service") + return ServiceKeyNames, nil + } + for _, resource := range response.Resources { + if resource.Type == "key" { + ServiceKeyNames = append(ServiceKeyNames, resource.Name) + } + } + return ServiceKeyNames, err +} + +func cloudFoundryFindServiceGUID(options *cloudFoundryDeleteServiceOptions, c command.ExecRunner) (GUID string, err error) { + + // Read GUID of Cloud Foundry Instance + var cfFindServiceGUIDScript = []string{"service", options.CfServiceInstance, "--guid"} + var serviceGUIDBytes bytes.Buffer + c.Stdout(&serviceGUIDBytes) + err = c.RunExecutable("cf", cfFindServiceGUIDScript...) + if err != nil { + return GUID, fmt.Errorf("Failed to Find Service Instance GUID, most likely your service doesn't exist: %w", err) + } + if len(serviceGUIDBytes.String()) == 0 { + log.Entry().Info("No Service Instance GUID could be retrieved for your requested Service") + return GUID, nil + } + unformattedGUID := serviceGUIDBytes.String() + GUID = strings.Replace(unformattedGUID, "\n", "", 1) + return GUID, nil +} + func cloudFoundryDeleteServiceFunction(options *cloudFoundryDeleteServiceOptions, c command.ExecRunner) error { var cfdeleteServiceScript = []string{"delete-service", options.CfServiceInstance, "-f"} diff --git a/cmd/cloudFoundryDeleteService_test.go b/cmd/cloudFoundryDeleteService_test.go index 0444045bc..977f4860e 100644 --- a/cmd/cloudFoundryDeleteService_test.go +++ b/cmd/cloudFoundryDeleteService_test.go @@ -13,6 +13,56 @@ import ( func TestCloudFoundryDeleteService(t *testing.T) { + t.Run("CF Delete Service without service keys: success case", func(t *testing.T) { + config := cloudFoundryDeleteServiceOptions{ + CfAPIEndpoint: "https://api.endpoint.com", + CfOrg: "testOrg", + CfSpace: "testSpace", + Username: "testUser", + Password: "testPassword", + CfServiceInstance: "testInstance", + CfDeleteServiceKeys: false, + CfAsync: false, + } + m := make(map[string]string) + execRunner := mock.ExecMockRunner{ + StdoutReturn: m, + } + cfUtils := cloudfoundry.CfUtilsMock{} + + err := runCloudFoundryDeleteService(&config, &execRunner, &cfUtils) + if assert.NoError(t, err) { + assert.Equal(t, "cf", execRunner.Calls[0].Exec) + assert.Equal(t, []string{"delete-service", "testInstance", "-f", "--wait"}, execRunner.Calls[0].Params) + + } + }) + + t.Run("CF Delete Service without service keys async: success case", func(t *testing.T) { + config := cloudFoundryDeleteServiceOptions{ + CfAPIEndpoint: "https://api.endpoint.com", + CfOrg: "testOrg", + CfSpace: "testSpace", + Username: "testUser", + Password: "testPassword", + CfServiceInstance: "testInstance", + CfDeleteServiceKeys: false, + CfAsync: true, + } + m := make(map[string]string) + execRunner := mock.ExecMockRunner{ + StdoutReturn: m, + } + cfUtils := cloudfoundry.CfUtilsMock{} + + err := runCloudFoundryDeleteService(&config, &execRunner, &cfUtils) + if assert.NoError(t, err) { + assert.Equal(t, "cf", execRunner.Calls[0].Exec) + assert.Equal(t, []string{"delete-service", "testInstance", "-f"}, execRunner.Calls[0].Params) + + } + }) + t.Run("CF Delete Service : success case", func(t *testing.T) { config := cloudFoundryDeleteServiceOptions{ CfAPIEndpoint: "https://api.endpoint.com", @@ -25,12 +75,15 @@ func TestCloudFoundryDeleteService(t *testing.T) { CfAsync: false, } m := make(map[string]string) - m["cf service-keys testInstance"] = `line1 -line2 -line3 -myServiceKey1 -myServiceKey2 -` + m["cf service testInstance --guid"] = `instance-guid` + m["cf curl /v3/service_credential_bindings?service_instance_guids=instance-guid"] = `{ +"resources": [ + { "name": "ExampleServiceKey1", "type": "key" }, + { "name": "ExampleServiceKey2", "type": "application" }, + { "name": "ExampleServiceKey3", "type": "key" } +] +}` + execRunner := mock.ExecMockRunner{ StdoutReturn: m, } @@ -42,10 +95,50 @@ myServiceKey2 assert.Equal(t, "cf", execRunner.Calls[1].Exec) assert.Equal(t, "cf", execRunner.Calls[2].Exec) assert.Equal(t, "cf", execRunner.Calls[3].Exec) - assert.Equal(t, []string{"service-keys", "testInstance"}, execRunner.Calls[0].Params) - assert.Equal(t, []string{"delete-service-key", "testInstance", "myServiceKey1", "-f", "--wait"}, execRunner.Calls[1].Params) - assert.Equal(t, []string{"delete-service-key", "testInstance", "myServiceKey2", "-f", "--wait"}, execRunner.Calls[2].Params) - assert.Equal(t, []string{"delete-service", "testInstance", "-f", "--wait"}, execRunner.Calls[3].Params) + assert.Equal(t, []string{"service", "testInstance", "--guid"}, execRunner.Calls[0].Params) + assert.Equal(t, []string{"curl", "/v3/service_credential_bindings?service_instance_guids=instance-guid"}, execRunner.Calls[1].Params) + assert.Equal(t, []string{"delete-service-key", "testInstance", "ExampleServiceKey1", "-f", "--wait"}, execRunner.Calls[2].Params) + assert.Equal(t, []string{"delete-service-key", "testInstance", "ExampleServiceKey3", "-f", "--wait"}, execRunner.Calls[3].Params) + assert.Equal(t, []string{"delete-service", "testInstance", "-f", "--wait"}, execRunner.Calls[4].Params) + } + }) + t.Run("CF Delete Service async : success case", func(t *testing.T) { + config := cloudFoundryDeleteServiceOptions{ + CfAPIEndpoint: "https://api.endpoint.com", + CfOrg: "testOrg", + CfSpace: "testSpace", + Username: "testUser", + Password: "testPassword", + CfServiceInstance: "testInstance", + CfDeleteServiceKeys: true, + CfAsync: true, + } + m := make(map[string]string) + m["cf service testInstance --guid"] = `instance-guid` + m["cf curl /v3/service_credential_bindings?service_instance_guids=instance-guid"] = `{ +"resources": [ + { "name": "ExampleServiceKey1", "type": "key" }, + { "name": "ExampleServiceKey2", "type": "application" }, + { "name": "ExampleServiceKey3", "type": "key" } +] +}` + + execRunner := mock.ExecMockRunner{ + StdoutReturn: m, + } + cfUtils := cloudfoundry.CfUtilsMock{} + + err := runCloudFoundryDeleteService(&config, &execRunner, &cfUtils) + if assert.NoError(t, err) { + assert.Equal(t, "cf", execRunner.Calls[0].Exec) + assert.Equal(t, "cf", execRunner.Calls[1].Exec) + assert.Equal(t, "cf", execRunner.Calls[2].Exec) + assert.Equal(t, "cf", execRunner.Calls[3].Exec) + assert.Equal(t, []string{"service", "testInstance", "--guid"}, execRunner.Calls[0].Params) + assert.Equal(t, []string{"curl", "/v3/service_credential_bindings?service_instance_guids=instance-guid"}, execRunner.Calls[1].Params) + assert.Equal(t, []string{"delete-service-key", "testInstance", "ExampleServiceKey1", "-f"}, execRunner.Calls[2].Params) + assert.Equal(t, []string{"delete-service-key", "testInstance", "ExampleServiceKey3", "-f"}, execRunner.Calls[3].Params) + assert.Equal(t, []string{"delete-service", "testInstance", "-f"}, execRunner.Calls[4].Params) } }) }