diff --git a/cmd/link/ocmrole/cmd.go b/cmd/link/ocmrole/cmd.go index c5c0bc1123..1adba70f3c 100644 --- a/cmd/link/ocmrole/cmd.go +++ b/cmd/link/ocmrole/cmd.go @@ -64,23 +64,28 @@ func init() { } func run(cmd *cobra.Command, argv []string) { - r := rosa.NewRuntime().WithAWS().WithOCM() - defer r.Cleanup() - if len(argv) > 0 { args.roleArn = argv[0] } - orgAccount, _, err := r.OCMClient.GetCurrentOrganization() + r := rosa.NewRuntime().WithAWS().WithOCM() + defer r.Cleanup() + err := runWithRuntime(r, cmd) if err != nil { - r.Reporter.Errorf("Error getting organization account: %v", err) + r.Reporter.Errorf("%s", err) os.Exit(1) } +} + +func runWithRuntime(r *rosa.Runtime, cmd *cobra.Command) error { + orgAccount, _, err := r.OCMClient.GetCurrentOrganization() + if err != nil { + return fmt.Errorf("error getting organization account: %v", err) + } if args.organizationID != "" && orgAccount != args.organizationID { - r.Reporter.Errorf("Invalid organization ID '%s'. "+ - "It doesn't match with the user session '%s'.", args.organizationID, orgAccount) - os.Exit(1) + return fmt.Errorf("invalid organization ID '%s', "+ + "it doesn't match with the user session '%s'", args.organizationID, orgAccount) } if r.Reporter.IsTerminal() { @@ -105,31 +110,27 @@ func run(cmd *cobra.Command, argv []string) { }, }) if err != nil { - r.Reporter.Errorf("Expected a valid ocm role ARN to link to a current organization: %s", err) - os.Exit(1) + return fmt.Errorf("expected a valid ocm role ARN to link to a current organization: %s", err) } } if roleArn != "" { err = aws.ARNValidator(roleArn) if err != nil { - r.Reporter.Errorf("Expected a valid ocm role ARN to link to a current organization: %s", err) - os.Exit(1) + return fmt.Errorf("expected a valid ocm role ARN to link to a current organization: %s", err) } } role, err := r.AWSClient.GetRoleByARN(roleArn) if err != nil { - r.Reporter.Errorf("There was a problem checking if role '%s' exists: %v", roleArn, err) - os.Exit(1) + return fmt.Errorf("there was a problem checking if role '%s' exists: %v", roleArn, err) } if *role.Arn != roleArn { - r.Reporter.Errorf("The role with '%s' cannot be found", roleArn) - os.Exit(1) + return fmt.Errorf("the role with '%s' cannot be found", roleArn) } if !confirm.Prompt(true, "Link the '%s' role with organization '%s'?", roleArn, orgAccount) { - os.Exit(0) + return nil } linked, err := r.OCMClient.LinkOrgToRole(orgAccount, roleArn) @@ -144,19 +145,18 @@ func run(cmd *cobra.Command, argv []string) { "Your Red Hat Account '%s' has no permission for this command.\n", ocmAccount.Username()) } - r.Reporter.Errorf("%s"+ + return fmt.Errorf("%s"+ "Only organization member can run this command. "+ "Please ask someone with the organization member role to run the following command \n\n"+ "\t rosa link ocm-role --role-arn %s --organization-id %s", errMessage, roleArn, orgAccount) - os.Exit(1) } - r.Reporter.Errorf("Unable to link role arn '%s' with the organization id : '%s' : %v", + return fmt.Errorf("unable to link role arn '%s' with the organization id : '%s' : %v", roleArn, orgAccount, err) - os.Exit(1) } if !linked { r.Reporter.Infof("Role-arn '%s' is already linked with the organization account '%s'", roleArn, orgAccount) - os.Exit(0) + return nil } r.Reporter.Infof("Successfully linked role-arn '%s' with organization account '%s'", roleArn, orgAccount) + return nil } diff --git a/cmd/link/ocmrole/cmd_suite_test.go b/cmd/link/ocmrole/cmd_suite_test.go new file mode 100644 index 0000000000..9f8f0d7e66 --- /dev/null +++ b/cmd/link/ocmrole/cmd_suite_test.go @@ -0,0 +1,13 @@ +package ocmrole + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestLinkOcmRole(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Link OCM role suite") +} diff --git a/cmd/link/ocmrole/cmd_test.go b/cmd/link/ocmrole/cmd_test.go new file mode 100644 index 0000000000..ec77c3ac6c --- /dev/null +++ b/cmd/link/ocmrole/cmd_test.go @@ -0,0 +1,110 @@ +package ocmrole + +import ( + "fmt" + "net/http" + + awssdk "github.com/aws/aws-sdk-go-v2/aws" + iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + . "github.com/openshift-online/ocm-sdk-go/testing" + + "github.com/openshift/rosa/pkg/aws" + "github.com/openshift/rosa/pkg/interactive" + "github.com/openshift/rosa/pkg/test" +) + +const ( + testOrgID = "org-123" + testRoleARN = "arn:aws:iam::123456789012:role/ManagedOpenshift-OCM-Role" +) + +var currentAccountResponse = `{ + "kind": "Account", + "id": "acct-123", + "username": "testuser", + "organization": { + "id": "org-123", + "kind": "Organization" + } +}` + +func mockIAMRole(roleARN string) iamtypes.Role { + return iamtypes.Role{ + Arn: awssdk.String(roleARN), + } +} + +var _ = Describe("link ocm-role", func() { + var t *test.TestingRuntime + + BeforeEach(func() { + t = test.NewTestRuntime() + args = struct { + roleArn string + organizationID string + }{} + interactive.SetEnabled(false) + Expect(Cmd.Flag("yes").Value.Set("false")).To(Succeed()) + }) + + Context("runWithRuntime", func() { + It("returns error when role ARN is empty", func() { + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, currentAccountResponse), + ) + + err := runWithRuntime(t.RosaRuntime, Cmd) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("expected a valid ocm role ARN to link to a current organization")) + }) + + It("returns error when role ARN format is invalid", func() { + args.roleArn = "invalid-arn" + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, currentAccountResponse), + ) + + err := runWithRuntime(t.RosaRuntime, Cmd) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("expected a valid ocm role ARN to link to a current organization")) + }) + + It("returns error when role does not exist in AWS", func() { + args.roleArn = testRoleARN + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, currentAccountResponse), + ) + + mockClient := t.RosaRuntime.AWSClient.(*aws.MockClient) + mockClient.EXPECT().GetRoleByARN(testRoleARN).Return(iamtypes.Role{}, fmt.Errorf("role not found")) + + err := runWithRuntime(t.RosaRuntime, Cmd) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("there was a problem checking if role")) + Expect(err.Error()).To(ContainSubstring("role not found")) + }) + + It("successfully links role when OCM API call succeeds", func() { + args.roleArn = testRoleARN + Expect(Cmd.Flag("yes").Value.Set("true")).To(Succeed()) + + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, currentAccountResponse), + RespondWithJSON(http.StatusOK, `{"key":"sts_ocm_role","value":""}`), + RespondWithJSON(http.StatusCreated, fmt.Sprintf(`{"key":"sts_ocm_role","value":"%s"}`, testRoleARN)), + ) + + mockClient := t.RosaRuntime.AWSClient.(*aws.MockClient) + mockClient.EXPECT().GetRoleByARN(testRoleARN).Return(mockIAMRole(testRoleARN), nil) + + stdout, stderr, err := test.RunWithOutputCapture(runWithRuntime, t.RosaRuntime, Cmd) + Expect(err).NotTo(HaveOccurred()) + Expect(stderr).To(BeEmpty()) + Expect(stdout).To(ContainSubstring("Successfully linked role-arn")) + Expect(stdout).To(ContainSubstring(testRoleARN)) + Expect(stdout).To(ContainSubstring(testOrgID)) + }) + }) +}) diff --git a/cmd/link/userrole/cmd.go b/cmd/link/userrole/cmd.go index 51924b0d78..c0dfa8a28e 100644 --- a/cmd/link/userrole/cmd.go +++ b/cmd/link/userrole/cmd.go @@ -17,6 +17,7 @@ limitations under the License. package userrole import ( + "fmt" "os" "strings" @@ -66,19 +67,27 @@ func init() { } func run(cmd *cobra.Command, argv []string) { - var err error - r := rosa.NewRuntime().WithAWS().WithOCM() - defer r.Cleanup() - if len(argv) > 0 { args.roleArn = argv[0] } + r := rosa.NewRuntime().WithAWS().WithOCM() + defer r.Cleanup() + err := runWithRuntime(r, cmd) + if err != nil { + r.Reporter.Errorf("%s", err) + os.Exit(1) + } +} + +func runWithRuntime(r *rosa.Runtime, cmd *cobra.Command) error { + var err error + accountID := args.accountID if accountID == "" { - currentAccount, err := r.OCMClient.GetCurrentAccount() - if err != nil { - r.Reporter.Errorf("Error getting current account: %v", err) + currentAccount, getAccountErr := r.OCMClient.GetCurrentAccount() + if getAccountErr != nil { + r.Reporter.Errorf("Error getting current account: %v", getAccountErr) } accountID = currentAccount.ID() } @@ -105,44 +114,39 @@ func run(cmd *cobra.Command, argv []string) { }, }) if err != nil { - r.Reporter.Errorf("Expected a valid user role ARN to link to a current account: %s", err) - os.Exit(1) + return fmt.Errorf("expected a valid user role ARN to link to a current account: %s", err) } } if roleArn != "" { err = aws.ARNValidator(roleArn) if err != nil { - r.Reporter.Errorf("Expected a valid user role ARN to link to a current account: %s", err) - os.Exit(1) + return fmt.Errorf("expected a valid user role ARN to link to a current account: %s", err) } } role, err := r.AWSClient.GetRoleByARN(roleArn) if err != nil { - r.Reporter.Errorf("There was a problem checking if role '%s' exists: %v", roleArn, err) - os.Exit(1) + return fmt.Errorf("there was a problem checking if role '%s' exists: %v", roleArn, err) } if *role.Arn != roleArn { - r.Reporter.Errorf("The role with '%s' cannot be found", roleArn) - os.Exit(1) + return fmt.Errorf("the role with '%s' cannot be found", roleArn) } if !confirm.Prompt(true, "Link the '%s' role with account '%s'?", roleArn, accountID) { - os.Exit(0) + return nil } err = r.OCMClient.LinkAccountRole(accountID, roleArn) if err != nil { if errors.GetType(err) == errors.Forbidden || strings.Contains(err.Error(), "ACCT-MGMT-11") { - r.Reporter.Errorf("Only organization admin or the user that owns this account '%s' can run this command. "+ - "Please ask someone with adequate permissions to run the following command \n\n"+ - "\t rosa link user-role --role-arn %s --account-id %s", accountID, roleArn, accountID) - os.Exit(1) + return fmt.Errorf("only organization admin or the user that owns this account '%s' can run this command, "+ + "please ask someone with adequate permissions to run the following command: "+ + "rosa link user-role --role-arn %s --account-id %s", accountID, roleArn, accountID) } - r.Reporter.Errorf("Unable to link role ARN '%s' with the account id : '%s' : %v", + return fmt.Errorf("unable to link role ARN '%s' with the account id : '%s' : %v", args.roleArn, accountID, err) - os.Exit(1) } r.Reporter.Infof("Successfully linked role ARN '%s' with account '%s'", roleArn, accountID) + return nil } diff --git a/cmd/link/userrole/cmd_suite_test.go b/cmd/link/userrole/cmd_suite_test.go new file mode 100644 index 0000000000..c99b86c60a --- /dev/null +++ b/cmd/link/userrole/cmd_suite_test.go @@ -0,0 +1,13 @@ +package userrole + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestLinkUserRole(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Link user role suite") +} diff --git a/cmd/link/userrole/cmd_test.go b/cmd/link/userrole/cmd_test.go new file mode 100644 index 0000000000..c4b2feab1a --- /dev/null +++ b/cmd/link/userrole/cmd_test.go @@ -0,0 +1,93 @@ +package userrole + +import ( + "fmt" + "net/http" + + awssdk "github.com/aws/aws-sdk-go-v2/aws" + iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + . "github.com/openshift-online/ocm-sdk-go/testing" + + "github.com/openshift/rosa/pkg/aws" + "github.com/openshift/rosa/pkg/interactive" + "github.com/openshift/rosa/pkg/test" +) + +const ( + testAccountID = "acct-123" + testRoleARN = "arn:aws:iam::123456789012:role/ManagedOpenshift-User-Role" +) + +var currentAccountResponse = `{ + "kind": "Account", + "id": "acct-123", + "username": "testuser", + "organization": { + "id": "org-123", + "kind": "Organization" + } +}` + +func mockIAMRole(roleARN string) iamtypes.Role { + return iamtypes.Role{ + Arn: awssdk.String(roleARN), + } +} + +var _ = Describe("link user-role", func() { + var t *test.TestingRuntime + + BeforeEach(func() { + t = test.NewTestRuntime() + args = struct { + roleArn string + accountID string + }{} + interactive.SetEnabled(false) + Expect(Cmd.Flag("yes").Value.Set("false")).To(Succeed()) + }) + + Context("runWithRuntime", func() { + It("returns error when role ARN is empty", func() { + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, currentAccountResponse), + ) + + err := runWithRuntime(t.RosaRuntime, Cmd) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("expected a valid user role ARN to link to a current account")) + }) + + It("returns error when role ARN format is invalid", func() { + args.roleArn = "invalid-arn" + args.accountID = testAccountID + + err := runWithRuntime(t.RosaRuntime, Cmd) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("expected a valid user role ARN to link to a current account")) + }) + + It("successfully links role when OCM API call succeeds", func() { + args.roleArn = testRoleARN + args.accountID = testAccountID + Expect(Cmd.Flag("yes").Value.Set("true")).To(Succeed()) + + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusNotFound, `{}`), + RespondWithJSON(http.StatusCreated, fmt.Sprintf(`{"key":"sts_user_role","value":"%s"}`, testRoleARN)), + ) + + mockClient := t.RosaRuntime.AWSClient.(*aws.MockClient) + mockClient.EXPECT().GetRoleByARN(testRoleARN).Return(mockIAMRole(testRoleARN), nil) + + stdout, stderr, err := test.RunWithOutputCapture(runWithRuntime, t.RosaRuntime, Cmd) + Expect(err).NotTo(HaveOccurred()) + Expect(stderr).To(BeEmpty()) + Expect(stdout).To(ContainSubstring("Successfully linked role ARN")) + Expect(stdout).To(ContainSubstring(testRoleARN)) + Expect(stdout).To(ContainSubstring(testAccountID)) + }) + }) +}) diff --git a/cmd/unlink/ocmrole/cmd.go b/cmd/unlink/ocmrole/cmd.go index 22d3060dec..391f9772de 100644 --- a/cmd/unlink/ocmrole/cmd.go +++ b/cmd/unlink/ocmrole/cmd.go @@ -14,6 +14,7 @@ limitations under the License. package ocmrole import ( + "fmt" "os" "strings" @@ -63,22 +64,27 @@ func init() { } func run(cmd *cobra.Command, argv []string) { - r := rosa.NewRuntime().WithOCM() - defer r.Cleanup() - if len(argv) > 0 { args.roleArn = argv[0] } - orgID, _, err := r.OCMClient.GetCurrentOrganization() + r := rosa.NewRuntime().WithOCM() + defer r.Cleanup() + err := runWithRuntime(r, cmd) if err != nil { - r.Reporter.Errorf("Error getting organization account: %v", err) + r.Reporter.Errorf("%s", err) os.Exit(1) } +} + +func runWithRuntime(r *rosa.Runtime, cmd *cobra.Command) error { + orgID, _, err := r.OCMClient.GetCurrentOrganization() + if err != nil { + return fmt.Errorf("error getting organization account: %v", err) + } if args.organizationID != "" && orgID != args.organizationID { - r.Reporter.Errorf("Invalid organization ID '%s'. "+ - "It doesn't match with the user session '%s'.", args.organizationID, orgID) - os.Exit(1) + return fmt.Errorf("invalid organization ID '%s', "+ + "it doesn't match with the user session '%s'", args.organizationID, orgID) } if r.Reporter.IsTerminal() { @@ -103,32 +109,29 @@ func run(cmd *cobra.Command, argv []string) { }, }) if err != nil { - r.Reporter.Errorf("Expected a valid ocm role ARN to unlink from the current organization: %s", err) - os.Exit(1) + return fmt.Errorf("expected a valid ocm role ARN to unlink from the current organization: %s", err) } } if roleArn != "" { err = aws.ARNValidator(roleArn) if err != nil { - r.Reporter.Errorf("Expected a valid ocm role ARN to unlink from the current organization: %s", err) - os.Exit(1) + return fmt.Errorf("expected a valid ocm role ARN to unlink from the current organization: %s", err) } } if !confirm.Prompt(true, "Unlink the '%s' role from organization '%s'?", roleArn, orgID) { - os.Exit(0) + return nil } err = r.OCMClient.UnlinkOCMRoleFromOrg(orgID, roleArn) if err != nil { if errors.GetType(err) == errors.Forbidden || strings.Contains(err.Error(), "ACCT-MGMT-11") { - r.Reporter.Errorf("Only organization admin can run this command. "+ - "Please ask someone with the organization admin role to run the following command \n\n"+ - "\t rosa unlink ocm-role --role-arn %s --organization-id %s", roleArn, orgID) - os.Exit(1) + return fmt.Errorf("only organization admin can run this command, "+ + "please ask someone with the organization admin role to run the following command: "+ + "rosa unlink ocm-role --role-arn %s --organization-id %s", roleArn, orgID) } - r.Reporter.Errorf("Unable to unlink role arn '%s' from the organization id : '%s' : %v", + return fmt.Errorf("unable to unlink role arn '%s' from the organization id : '%s' : %v", roleArn, orgID, err) - os.Exit(1) } r.Reporter.Infof("Successfully unlinked role-arn '%s' from organization account '%s'", roleArn, orgID) + return nil } diff --git a/cmd/unlink/ocmrole/cmd_suite_test.go b/cmd/unlink/ocmrole/cmd_suite_test.go new file mode 100644 index 0000000000..af7a14da23 --- /dev/null +++ b/cmd/unlink/ocmrole/cmd_suite_test.go @@ -0,0 +1,13 @@ +package ocmrole + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestUnlinkOcmRole(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Unlink OCM role suite") +} diff --git a/cmd/unlink/ocmrole/cmd_test.go b/cmd/unlink/ocmrole/cmd_test.go new file mode 100644 index 0000000000..d22545346b --- /dev/null +++ b/cmd/unlink/ocmrole/cmd_test.go @@ -0,0 +1,72 @@ +package ocmrole + +import ( + "fmt" + "net/http" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + . "github.com/openshift-online/ocm-sdk-go/testing" + + "github.com/openshift/rosa/pkg/interactive" + "github.com/openshift/rosa/pkg/test" +) + +const ( + testOrgID = "org-123" + testRoleARN = "arn:aws:iam::123456789012:role/ManagedOpenshift-OCM-Role" +) + +var currentAccountResponse = `{ + "kind": "Account", + "id": "acct-123", + "username": "testuser", + "organization": { + "id": "org-123", + "kind": "Organization" + } +}` + +var _ = Describe("unlink ocm-role", func() { + var t *test.TestingRuntime + + BeforeEach(func() { + t = test.NewTestRuntime() + args = struct { + roleArn string + organizationID string + }{} + interactive.SetEnabled(false) + Expect(Cmd.Flag("yes").Value.Set("false")).To(Succeed()) + }) + + Context("runWithRuntime", func() { + It("returns error when role ARN is empty", func() { + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, currentAccountResponse), + ) + + err := runWithRuntime(t.RosaRuntime, Cmd) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("expected a valid ocm role ARN to unlink from the current organization")) + }) + + It("successfully unlinks role when OCM API call succeeds", func() { + args.roleArn = testRoleARN + Expect(Cmd.Flag("yes").Value.Set("true")).To(Succeed()) + + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, currentAccountResponse), + RespondWithJSON(http.StatusOK, fmt.Sprintf(`{"key":"sts_ocm_role","value":"%s"}`, testRoleARN)), + RespondWithJSON(http.StatusOK, `{}`), + ) + + stdout, stderr, err := test.RunWithOutputCapture(runWithRuntime, t.RosaRuntime, Cmd) + Expect(err).NotTo(HaveOccurred()) + Expect(stderr).To(BeEmpty()) + Expect(stdout).To(ContainSubstring("Successfully unlinked role-arn")) + Expect(stdout).To(ContainSubstring(testRoleARN)) + Expect(stdout).To(ContainSubstring(testOrgID)) + }) + }) +}) diff --git a/cmd/unlink/userrole/cmd.go b/cmd/unlink/userrole/cmd.go index dc4baed110..c9bd293b4a 100644 --- a/cmd/unlink/userrole/cmd.go +++ b/cmd/unlink/userrole/cmd.go @@ -14,6 +14,7 @@ limitations under the License. package userrole import ( + "fmt" "os" "strings" @@ -63,20 +64,27 @@ func init() { } func run(cmd *cobra.Command, argv []string) { - var err error - r := rosa.NewRuntime().WithOCM() - defer r.Cleanup() - if len(argv) > 0 { args.roleArn = argv[0] } + r := rosa.NewRuntime().WithOCM() + defer r.Cleanup() + err := runWithRuntime(r, cmd) + if err != nil { + r.Reporter.Errorf("%s", err) + os.Exit(1) + } +} + +func runWithRuntime(r *rosa.Runtime, cmd *cobra.Command) error { + var err error + accountID := args.accountID if accountID == "" { currentAccount, err := r.OCMClient.GetCurrentAccount() if err != nil { - r.Reporter.Errorf("Error getting current account: %v", err) - os.Exit(1) + return fmt.Errorf("error getting current account: %v", err) } accountID = currentAccount.ID() } @@ -103,32 +111,29 @@ func run(cmd *cobra.Command, argv []string) { }, }) if err != nil { - r.Reporter.Errorf("Expected a valid user role ARN to unlink from the current account: %s", err) - os.Exit(1) + return fmt.Errorf("expected a valid user role ARN to unlink from the current account: %s", err) } } if roleArn != "" { err = aws.ARNValidator(roleArn) if err != nil { - r.Reporter.Errorf("Expected a valid user role ARN to unlink from the current account: %s", err) - os.Exit(1) + return fmt.Errorf("expected a valid user role ARN to unlink from the current account: %s", err) } } if !confirm.Prompt(true, "Unlink the '%s' role from the current account '%s'?", roleArn, accountID) { - os.Exit(0) + return nil } err = r.OCMClient.UnlinkUserRoleFromAccount(accountID, roleArn) if err != nil { if errors.GetType(err) == errors.Forbidden || strings.Contains(err.Error(), "ACCT-MGMT-11") { - r.Reporter.Errorf("Only organization admin or the user that owns this account '%s' can run this command. "+ - "Please ask someone with adequate permissions to run the following command \n\n"+ - "\t rosa unlink user-role --role-arn %s --account-id %s", accountID, roleArn, accountID) - os.Exit(1) + return fmt.Errorf("only organization admin or the user that owns this account '%s' can run this command, "+ + "please ask someone with adequate permissions to run the following command: "+ + "rosa unlink user-role --role-arn %s --account-id %s", accountID, roleArn, accountID) } - r.Reporter.Errorf("Unable to unlink role ARN '%s' from the account id : '%s' : %v", + return fmt.Errorf("unable to unlink role ARN '%s' from the account id : '%s' : %v", roleArn, accountID, err) - os.Exit(1) } r.Reporter.Infof("Successfully unlinked role ARN '%s' from account '%s'", roleArn, accountID) + return nil } diff --git a/cmd/unlink/userrole/cmd_suite_test.go b/cmd/unlink/userrole/cmd_suite_test.go new file mode 100644 index 0000000000..c74948b2f9 --- /dev/null +++ b/cmd/unlink/userrole/cmd_suite_test.go @@ -0,0 +1,13 @@ +package userrole + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestUnlinkUserRole(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Unlink user role suite") +} diff --git a/cmd/unlink/userrole/cmd_test.go b/cmd/unlink/userrole/cmd_test.go new file mode 100644 index 0000000000..fdc2d1f117 --- /dev/null +++ b/cmd/unlink/userrole/cmd_test.go @@ -0,0 +1,72 @@ +package userrole + +import ( + "fmt" + "net/http" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + . "github.com/openshift-online/ocm-sdk-go/testing" + + "github.com/openshift/rosa/pkg/interactive" + "github.com/openshift/rosa/pkg/test" +) + +const ( + testAccountID = "acct-123" + testRoleARN = "arn:aws:iam::123456789012:role/ManagedOpenshift-User-Role" +) + +var currentAccountResponse = `{ + "kind": "Account", + "id": "acct-123", + "username": "testuser", + "organization": { + "id": "org-123", + "kind": "Organization" + } +}` + +var _ = Describe("unlink user-role", func() { + var t *test.TestingRuntime + + BeforeEach(func() { + t = test.NewTestRuntime() + args = struct { + roleArn string + accountID string + }{} + interactive.SetEnabled(false) + Expect(Cmd.Flag("yes").Value.Set("false")).To(Succeed()) + }) + + Context("runWithRuntime", func() { + It("returns error when role ARN is empty", func() { + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, currentAccountResponse), + ) + + err := runWithRuntime(t.RosaRuntime, Cmd) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("expected a valid user role ARN to unlink from the current account")) + }) + + It("successfully unlinks role when OCM API call succeeds", func() { + args.roleArn = testRoleARN + args.accountID = testAccountID + Expect(Cmd.Flag("yes").Value.Set("true")).To(Succeed()) + + t.ApiServer.AppendHandlers( + RespondWithJSON(http.StatusOK, fmt.Sprintf(`{"key":"sts_user_role","value":"%s"}`, testRoleARN)), + RespondWithJSON(http.StatusOK, `{}`), + ) + + stdout, stderr, err := test.RunWithOutputCapture(runWithRuntime, t.RosaRuntime, Cmd) + Expect(err).NotTo(HaveOccurred()) + Expect(stderr).To(BeEmpty()) + Expect(stdout).To(ContainSubstring("Successfully unlinked role ARN")) + Expect(stdout).To(ContainSubstring(testRoleARN)) + Expect(stdout).To(ContainSubstring(testAccountID)) + }) + }) +})