diff --git a/.secrets.baseline b/.secrets.baseline index 203a7575..8ceebac8 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|package-lock.json|^.secrets.baseline$", "lines": null }, - "generated_at": "2025-01-24T15:40:02Z", + "generated_at": "2025-01-24T16:21:04Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -128,7 +128,7 @@ "hashed_secret": "892bd503fb45f6fcafb1c7003d88291fc0b20208", "is_secret": false, "is_verified": false, - "line_number": 284, + "line_number": 291, "type": "Secret Keyword", "verified_result": null }, @@ -136,7 +136,7 @@ "hashed_secret": "5da5a31d49370df43eff521b39c10db1466fae44", "is_secret": false, "is_verified": false, - "line_number": 287, + "line_number": 294, "type": "Secret Keyword", "verified_result": null }, @@ -144,7 +144,7 @@ "hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030", "is_secret": false, "is_verified": false, - "line_number": 488, + "line_number": 495, "type": "Secret Keyword", "verified_result": null } diff --git a/cloudinfo/mock_test.go b/cloudinfo/mock_test.go index 503354f7..e2997a5b 100644 --- a/cloudinfo/mock_test.go +++ b/cloudinfo/mock_test.go @@ -120,13 +120,25 @@ func (mock *iamPolicyServiceMock) DeletePolicy(deletePolicyOptions *iampolicyman // RESOURCE CONTROLLER SERVICE MOCK type resourceControllerServiceMock struct { mock.Mock - mockResourceList *resourcecontrollerv2.ResourceInstancesList + mockResourceList *resourcecontrollerv2.ResourceInstancesList + mockReclamationList *resourcecontrollerv2.ReclamationsList + mockReclamation *resourcecontrollerv2.Reclamation } func (mock *resourceControllerServiceMock) NewListResourceInstancesOptions() *resourcecontrollerv2.ListResourceInstancesOptions { return &resourcecontrollerv2.ListResourceInstancesOptions{} } +func (mock *resourceControllerServiceMock) NewListReclamationsOptions() *resourcecontrollerv2.ListReclamationsOptions { + + return &resourcecontrollerv2.ListReclamationsOptions{} +} + +func (mock *resourceControllerServiceMock) NewRunReclamationActionOptions(id string, action string) *resourcecontrollerv2.RunReclamationActionOptions { + + return &resourcecontrollerv2.RunReclamationActionOptions{ID: core.StringPtr(id), ActionName: core.StringPtr(action)} +} + func (mock *resourceControllerServiceMock) ListResourceInstances(options *resourcecontrollerv2.ListResourceInstancesOptions) (*resourcecontrollerv2.ResourceInstancesList, *core.DetailedResponse, error) { var retList *resourcecontrollerv2.ResourceInstancesList var mockCount int64 = 0 @@ -146,6 +158,46 @@ func (mock *resourceControllerServiceMock) ListResourceInstances(options *resour return retList, nil, nil } +func (mock *resourceControllerServiceMock) ListReclamations(options *resourcecontrollerv2.ListReclamationsOptions) (*resourcecontrollerv2.ReclamationsList, *core.DetailedResponse, error) { + + var recList *resourcecontrollerv2.ReclamationsList + var mockID string = "mock-reclamation-id" + mockReclamation := resourcecontrollerv2.Reclamation{ID: &mockID} + mockReclamationList := []resourcecontrollerv2.Reclamation{mockReclamation} + + if options.ResourceInstanceID != nil && *options.ResourceInstanceID == "ERROR" { + return nil, nil, errors.New("mock Resource is error") + } + + if mock.mockReclamationList == nil { + + recList = &resourcecontrollerv2.ReclamationsList{ + + Resources: mockReclamationList, + } + } else { + recList = mock.mockReclamationList + } + + return recList, nil, nil +} + +func (mock *resourceControllerServiceMock) RunReclamationAction(options *resourcecontrollerv2.RunReclamationActionOptions) (*resourcecontrollerv2.Reclamation, *core.DetailedResponse, error) { + + var reclamation *resourcecontrollerv2.Reclamation + var mockID string = "mock-reclamation-id" + mockReclamation := resourcecontrollerv2.Reclamation{ID: &mockID} + + if mock.mockReclamation == nil { + + reclamation = &mockReclamation + } else { + reclamation = mock.mockReclamation + } + + return reclamation, nil, nil +} + // Resource Manager mock type resourceManagerServiceMock struct { mockResourceGroupList *resourcemanagerv2.ResourceGroupList diff --git a/cloudinfo/resourcecontroller.go b/cloudinfo/resourcecontroller.go index fd87a26e..dc7fee43 100644 --- a/cloudinfo/resourcecontroller.go +++ b/cloudinfo/resourcecontroller.go @@ -2,9 +2,10 @@ package cloudinfo import ( "fmt" - bluemix_crn "github.com/IBM-Cloud/bluemix-go/crn" "strings" + bluemix_crn "github.com/IBM-Cloud/bluemix-go/crn" + "github.com/IBM/go-sdk-core/v5/core" "github.com/IBM/platform-services-go-sdk/resourcecontrollerv2" ) @@ -66,6 +67,81 @@ func (infoSvc *CloudInfoService) ListResourcesByGroupID(resourceGroupId string) return allResources, nil } +func (infoSvc *CloudInfoService) GetReclamationIdFromCRN(CRN string) (string, error) { + + parsed_crn := strings.Split(CRN, ":") + + if len(parsed_crn) < 8 { + + return "", fmt.Errorf("invalid crn, instance guid is not present") + } + resourceInstanceID := parsed_crn[7] + + listReclamationsOptions := infoSvc.resourceControllerService.NewListReclamationsOptions() + listReclamationsOptions = listReclamationsOptions.SetResourceInstanceID(resourceInstanceID) + reclamationsList, _, err := infoSvc.resourceControllerService.ListReclamations(listReclamationsOptions) + if err != nil { + + return "", err + } + + if len(reclamationsList.Resources) == 0 { + + return "", nil + + } + + reclamationID := *reclamationsList.Resources[0].ID + + fmt.Println("reclamation id is ", reclamationID) + return reclamationID, nil +} + +func (infoSvc *CloudInfoService) DeleteInstanceFromReclamationId(reclamationID string) error { + + fmt.Println("Deleting the instance using reclamation id") + + runReclamationActionOptions := infoSvc.resourceControllerService.NewRunReclamationActionOptions( + reclamationID, + "reclaim", + ) + + _, _, err := infoSvc.resourceControllerService.RunReclamationAction(runReclamationActionOptions) + if err != nil { + + return err + } + + fmt.Println("Instance reclaimed successfully") + + return nil +} + +func (infoSvc *CloudInfoService) DeleteInstanceFromReclamationByCRN(CRN string) error { + + reclamationID, err := infoSvc.GetReclamationIdFromCRN(CRN) + + if err != nil { + + return err + } + + if reclamationID == "" { + + fmt.Println("No reclamation found for the given CRN") + return nil + } + + err = infoSvc.DeleteInstanceFromReclamationId(reclamationID) + + if err != nil { + return err + } + + return nil + +} + // listResourceInstances will retrieve all resources of a given type for an account func listResourceInstances(infoSvc *CloudInfoService, options *resourcecontrollerv2.ListResourceInstancesOptions) ([]resourcecontrollerv2.ResourceInstance, error) { // this API is paginated, but there is no pager support in the library at this time. diff --git a/cloudinfo/resourcecontroller_test.go b/cloudinfo/resourcecontroller_test.go index af29e0b0..01d7facf 100644 --- a/cloudinfo/resourcecontroller_test.go +++ b/cloudinfo/resourcecontroller_test.go @@ -1,9 +1,10 @@ package cloudinfo import ( - "github.com/IBM/platform-services-go-sdk/resourcemanagerv2" "testing" + "github.com/IBM/platform-services-go-sdk/resourcemanagerv2" + "github.com/IBM/platform-services-go-sdk/resourcecontrollerv2" "github.com/stretchr/testify/assert" ) @@ -158,3 +159,37 @@ func TestListResourcesByGroupName(t *testing.T) { assert.Equal(t, len(twoTotalList), 2) }) } + +func TestGetReclamationIDFromCrn(t *testing.T) { + + infoSvc := CloudInfoService{ + resourceControllerService: &resourceControllerServiceMock{}, + } + + var CRN string = "crn:v1:bluemix:public:my-service:theregion:a/accountnum:guid::" + + _, err := infoSvc.GetReclamationIdFromCRN(CRN) + assert.Nil(t, err) + +} + +func TestDeleteInstanceFromReclamation(t *testing.T) { + + infoSvc := CloudInfoService{ + resourceControllerService: &resourceControllerServiceMock{}, + } + + err := infoSvc.DeleteInstanceFromReclamationId("abc") + assert.Nil(t, err) + +} + +func TestDeleteInstanceFromReclamationByCrn(t *testing.T) { + + infoSvc := CloudInfoService{ + resourceControllerService: &resourceControllerServiceMock{}, + } + + err := infoSvc.DeleteInstanceFromReclamationByCRN("crn:v1:bluemix:public:my-service:theregion:a/accountnum:guid::") + assert.Nil(t, err) +} diff --git a/cloudinfo/service.go b/cloudinfo/service.go index d01ecea1..5e397278 100644 --- a/cloudinfo/service.go +++ b/cloudinfo/service.go @@ -90,6 +90,9 @@ type CloudInfoServiceI interface { GetSchematicsJobFileData(jobID string, fileType string, location string) (*schematics.JobFileData, error) GetSchematicsJobPlanJson(jobID string, location string) (string, error) GetSchematicsServiceByLocation(location string) (schematicsService, error) + GetReclamationIdFromCRN(CRN string) (string, error) + DeleteInstanceFromReclamationId(reclamationID string) error + DeleteInstanceFromReclamationByCRN(CRN string) error } // CloudInfoServiceOptions structure used as input params for service constructor. @@ -144,7 +147,11 @@ type iamPolicyService interface { // resourceControllerService for external Resource Controller V2 Service API. Used for mocking. type resourceControllerService interface { NewListResourceInstancesOptions() *resourcecontrollerv2.ListResourceInstancesOptions + NewListReclamationsOptions() *resourcecontrollerv2.ListReclamationsOptions + NewRunReclamationActionOptions(string, string) *resourcecontrollerv2.RunReclamationActionOptions + ListReclamations(*resourcecontrollerv2.ListReclamationsOptions) (*resourcecontrollerv2.ReclamationsList, *core.DetailedResponse, error) ListResourceInstances(*resourcecontrollerv2.ListResourceInstancesOptions) (*resourcecontrollerv2.ResourceInstancesList, *core.DetailedResponse, error) + RunReclamationAction(*resourcecontrollerv2.RunReclamationActionOptions) (*resourcecontrollerv2.Reclamation, *core.DetailedResponse, error) } // resourceManagerService for external Resource Manager V2 Service API. Used for mocking.