Skip to content

Commit 4bb370e

Browse files
committed
OCM-12806 | feat: Delete operator roles auto mode (and changes to account roles)
1 parent 37f81bb commit 4bb370e

File tree

7 files changed

+156
-27
lines changed

7 files changed

+156
-27
lines changed

cmd/dlt/accountroles/cmd.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/openshift/rosa/pkg/interactive"
3131
"github.com/openshift/rosa/pkg/interactive/confirm"
3232
"github.com/openshift/rosa/pkg/ocm"
33+
"github.com/openshift/rosa/pkg/roles"
3334
"github.com/openshift/rosa/pkg/rosa"
3435
)
3536

@@ -204,12 +205,16 @@ func deleteAccountRoles(r *rosa.Runtime, env string, prefix string, clusters []*
204205
r.Reporter.Infof(fmt.Sprintf("Deleting %saccount roles", roleTypeString))
205206

206207
r.OCMClient.LogEvent("ROSADeleteAccountRoleModeAuto", nil)
208+
deleteHcpSharedVpcPolicies := false
209+
if roles.CheckIfRolesAreHcpSharedVpc(r, finalRoleList) {
210+
deleteHcpSharedVpcPolicies = confirm.Prompt(true, "Attempt to delete Hosted CP shared VPC policies?")
211+
}
207212
for _, role := range finalRoleList {
208213
if !confirm.Prompt(true, "Delete the account role '%s'?", role) {
209214
continue
210215
}
211216
r.Reporter.Infof("Deleting account role '%s'", role)
212-
err := r.AWSClient.DeleteAccountRole(role, prefix, managedPolicies)
217+
err := r.AWSClient.DeleteAccountRole(role, prefix, managedPolicies, deleteHcpSharedVpcPolicies)
213218
if err != nil {
214219
r.Reporter.Warnf("There was an error deleting the account roles or policies: %s", err)
215220
continue

cmd/dlt/operatorrole/cmd.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/openshift/rosa/pkg/interactive"
3232
"github.com/openshift/rosa/pkg/interactive/confirm"
3333
"github.com/openshift/rosa/pkg/ocm"
34+
"github.com/openshift/rosa/pkg/roles"
3435
"github.com/openshift/rosa/pkg/rosa"
3536
)
3637

@@ -220,6 +221,13 @@ func run(cmd *cobra.Command, _ []string) {
220221
switch mode {
221222
case interactive.ModeAuto:
222223
r.OCMClient.LogEvent("ROSADeleteOperatorroleModeAuto", nil)
224+
225+
// Only ask user if they want to delete policies if they are deleting HcpSharedVpc roles
226+
deleteHcpSharedVpcPolicies := false
227+
if roles.CheckIfRolesAreHcpSharedVpc(r, foundOperatorRoles) {
228+
deleteHcpSharedVpcPolicies = confirm.Prompt(true, "Attempt to delete Hosted CP shared VPC policies?")
229+
}
230+
allSharedVpcPoliciesNotDeleted := make(map[string]bool)
223231
for _, role := range foundOperatorRoles {
224232
if !confirm.Prompt(true, "Delete the operator role '%s'?", role) {
225233
continue
@@ -228,7 +236,11 @@ func run(cmd *cobra.Command, _ []string) {
228236
if spin != nil {
229237
spin.Start()
230238
}
231-
err := r.AWSClient.DeleteOperatorRole(role, managedPolicies)
239+
sharedVpcPoliciesNotDeleted, err := r.AWSClient.DeleteOperatorRole(role, managedPolicies,
240+
deleteHcpSharedVpcPolicies)
241+
for key, value := range sharedVpcPoliciesNotDeleted {
242+
allSharedVpcPoliciesNotDeleted[key] = value
243+
}
232244

233245
if err != nil {
234246
if spin != nil {
@@ -242,6 +254,12 @@ func run(cmd *cobra.Command, _ []string) {
242254
spin.Stop()
243255
}
244256
}
257+
for policyOutput, notDeleted := range allSharedVpcPoliciesNotDeleted {
258+
if notDeleted {
259+
r.Logger.Warnf("Unable to delete policy %s: Policy still attached to other resources",
260+
policyOutput)
261+
}
262+
}
245263
if !errOccured {
246264
r.Reporter.Infof("Successfully deleted the operator roles")
247265
}

pkg/aws/client.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ type Client interface {
145145
ListOidcProviders(targetClusterId string, config *cmv1.OidcConfig) ([]OidcProviderOutput, error)
146146
GetRoleByARN(roleARN string) (iamtypes.Role, error)
147147
GetRoleByName(roleName string) (iamtypes.Role, error)
148-
DeleteOperatorRole(roles string, managedPolicies bool) error
148+
DeleteOperatorRole(roles string, managedPolicies bool, deleteHcpSharedVpcPolicies bool) (map[string]bool, error)
149149
GetOperatorRolesFromAccountByClusterID(
150150
clusterID string,
151151
credRequests map[string]*cmv1.STSOperator,
@@ -156,11 +156,12 @@ type Client interface {
156156
GetAccountRoleForCurrentEnv(env string, roleName string) (Role, error)
157157
GetAccountRoleForCurrentEnvWithPrefix(env string, rolePrefix string,
158158
accountRolesMap map[string]AccountRole) ([]Role, error)
159-
DeleteAccountRole(roleName string, prefix string, managedPolicies bool) error
159+
DeleteAccountRole(roleName string, prefix string, managedPolicies bool, deleteHcpSharedVpcPolicies bool) error
160160
DeleteOCMRole(roleARN string, managedPolicies bool) error
161161
DeleteUserRole(roleName string) error
162162
GetAccountRolePolicies(roles []string, prefix string) (map[string][]PolicyDetail, map[string][]PolicyDetail, error)
163163
GetAttachedPolicy(role *string) ([]PolicyDetail, error)
164+
GetPolicyDetailsFromRole(role *string) ([]*iam.GetPolicyOutput, error)
164165
HasPermissionsBoundary(roleName string) (bool, error)
165166
GetOpenIDConnectProviderByClusterIdTag(clusterID string) (string, error)
166167
GetOpenIDConnectProviderByOidcEndpointUrl(oidcEndpointUrl string) (string, error)

pkg/aws/client_mock.go

Lines changed: 26 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/aws/policies.go

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,15 +1065,37 @@ func checkIfROSAOperatorRole(role iamtypes.Role, credRequest map[string]*cmv1.ST
10651065
return false
10661066
}
10671067

1068-
func (c *awsClient) DeleteOperatorRole(roleName string, managedPolicies bool) error {
1068+
func (c *awsClient) GetPolicyDetailsFromRole(role *string) ([]*iam.GetPolicyOutput, error) {
1069+
policies, err := c.iamClient.ListAttachedRolePolicies(context.Background(), &iam.ListAttachedRolePoliciesInput{
1070+
RoleName: role,
1071+
})
1072+
if err != nil {
1073+
return nil, err
1074+
}
1075+
finalOutput := make([]*iam.GetPolicyOutput, 0)
1076+
for _, attachedPolicy := range policies.AttachedPolicies {
1077+
policy, err := c.iamClient.GetPolicy(context.Background(), &iam.GetPolicyInput{
1078+
PolicyArn: attachedPolicy.PolicyArn,
1079+
})
1080+
if err != nil {
1081+
return nil, err
1082+
}
1083+
finalOutput = append(finalOutput, policy)
1084+
}
1085+
return finalOutput, nil
1086+
}
1087+
1088+
func (c *awsClient) DeleteOperatorRole(roleName string, managedPolicies bool,
1089+
deleteHcpSharedVpcPolicies bool) (map[string]bool, error) {
10691090
role := aws.String(roleName)
1091+
sharedVpcPoliciesNotDeleted := make(map[string]bool)
10701092
tagFilter, err := getOperatorRolePolicyTags(c.iamClient, roleName)
10711093
if err != nil {
1072-
return err
1094+
return sharedVpcPoliciesNotDeleted, err
10731095
}
10741096
policies, excludedPolicies, err := getAttachedPolicies(c.iamClient, roleName, tagFilter)
10751097
if err != nil {
1076-
return err
1098+
return sharedVpcPoliciesNotDeleted, err
10771099
}
10781100
err = c.detachOperatorRolePolicies(role)
10791101
if err != nil {
@@ -1086,25 +1108,55 @@ func (c *awsClient) DeleteOperatorRole(roleName string, managedPolicies bool) er
10861108
err = nil
10871109
}
10881110
if err != nil {
1089-
return err
1111+
return sharedVpcPoliciesNotDeleted, err
10901112
}
10911113
}
10921114
err = c.DeleteRole(*role)
10931115
if err != nil {
1094-
return err
1116+
return sharedVpcPoliciesNotDeleted, err
10951117
}
10961118
if !managedPolicies {
10971119
_, err = c.deletePolicies(policies)
1098-
} else {
1120+
} else if deleteHcpSharedVpcPolicies {
10991121
var sharedVpcHcpPolicies []string
11001122
for _, policy := range excludedPolicies {
1101-
if strings.Contains(policy, SharedVpcAssumeRolePrefix) {
1102-
sharedVpcHcpPolicies = append(sharedVpcHcpPolicies, policy)
1123+
policyOutput, err := c.iamClient.GetPolicy(context.Background(), &iam.GetPolicyInput{
1124+
PolicyArn: aws.String(policy),
1125+
})
1126+
if err != nil || policyOutput == nil {
1127+
return sharedVpcPoliciesNotDeleted, err
1128+
}
1129+
1130+
containsManagedTag := false
1131+
containsHcpSharedVpcTag := false
1132+
for _, policyTag := range policyOutput.Policy.Tags {
1133+
switch strings.Trim(*policyTag.Key, " ") {
1134+
case tags.RedHatManaged:
1135+
containsManagedTag = true
1136+
case tags.HcpSharedVpc:
1137+
containsHcpSharedVpcTag = true
1138+
}
1139+
}
1140+
// Delete if no attachments and correct tags showing it's a hcp sharedvpc policy
1141+
if containsManagedTag && containsHcpSharedVpcTag {
1142+
if *policyOutput.Policy.AttachmentCount == 0 {
1143+
// If the policy is deleted, remove from the warning outputs
1144+
sharedVpcPoliciesNotDeleted[*policyOutput.Policy.Arn] = false
1145+
1146+
// Add to list of sharedVpc policies to be actually deleted
1147+
c.logger.Infof("Deleting policy '%s'", policy)
1148+
sharedVpcHcpPolicies = append(sharedVpcHcpPolicies, policy)
1149+
} else {
1150+
// Print warning message after all roles are checked (will result in duplicated warnings without
1151+
// adding to this map and displaying at the end of role deletion due to multiple roles having
1152+
// one of the policies)
1153+
sharedVpcPoliciesNotDeleted[*policyOutput.Policy.Arn] = true
1154+
}
11031155
}
11041156
}
11051157
_, err = c.deletePolicies(sharedVpcHcpPolicies)
11061158
}
1107-
return err
1159+
return sharedVpcPoliciesNotDeleted, err
11081160
}
11091161

11101162
func (c *awsClient) DeleteRole(role string) error {
@@ -1140,7 +1192,8 @@ func (c *awsClient) GetInstanceProfilesForRole(r string) ([]string, error) {
11401192
return instanceProfiles, nil
11411193
}
11421194

1143-
func (c *awsClient) DeleteAccountRole(roleName string, prefix string, managedPolicies bool) error {
1195+
func (c *awsClient) DeleteAccountRole(roleName string, prefix string, managedPolicies bool,
1196+
deleteHcpSharedVpcPolicies bool) error {
11441197
role := aws.String(roleName)
11451198
err := c.DeleteInlineRolePolicies(aws.ToString(role))
11461199
if err != nil {
@@ -1173,7 +1226,7 @@ func (c *awsClient) DeleteAccountRole(roleName string, prefix string, managedPol
11731226
}
11741227
if !managedPolicies {
11751228
_, err = c.deletePolicies(policyMap)
1176-
} else {
1229+
} else if deleteHcpSharedVpcPolicies {
11771230
var sharedVpcHcpPolicies []string
11781231
for _, policy := range excludedPolicyMap {
11791232
policyOutput, err := c.iamClient.GetPolicy(context.Background(), &iam.GetPolicyInput{
@@ -1195,6 +1248,7 @@ func (c *awsClient) DeleteAccountRole(roleName string, prefix string, managedPol
11951248
}
11961249
if containsManagedTag && containsHcpSharedVpcTag {
11971250
if *policyOutput.Policy.AttachmentCount == 0 {
1251+
c.logger.Infof("Deleting policy '%s'", policy)
11981252
sharedVpcHcpPolicies = append(sharedVpcHcpPolicies, policy)
11991253
} else {
12001254
c.logger.Warnf("Unable to delete policy %s: Policy still attached to %v other resource(s)",

pkg/aws/policies_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,15 @@ var _ = Describe("Cluster Roles/Policies", func() {
531531
Expect(policies).To(HaveLen(1))
532532
Expect(policies[0]).To(Equal(operatorRolePolicyArn))
533533
})
534+
It("Test GetPolicyDetailsFromRole", func() {
535+
mockIamAPI.EXPECT().ListAttachedRolePolicies(gomock.Any(), &iam.ListAttachedRolePoliciesInput{
536+
RoleName: aws.String(accountRole),
537+
}).Return(accountRoleAttachedPolicies, nil).Times(1)
538+
mockIamAPI.EXPECT().GetPolicy(gomock.Any(), gomock.Any()).Times(2)
539+
output, err := client.GetPolicyDetailsFromRole(&accountRole)
540+
Expect(err).NotTo(HaveOccurred())
541+
Expect(output).To(HaveLen(2))
542+
})
534543
It("Test DeleteAccountRole", func() {
535544
mockIamAPI.EXPECT().ListRolePolicies(gomock.Any(), gomock.Any()).Return(&iam.ListRolePoliciesOutput{}, nil)
536545
mockIamAPI.EXPECT().ListAttachedRolePolicies(gomock.Any(), &iam.ListAttachedRolePoliciesInput{
@@ -567,7 +576,7 @@ var _ = Describe("Cluster Roles/Policies", func() {
567576
mockIamAPI.EXPECT().DeletePolicy(gomock.Any(), &iam.DeletePolicyInput{
568577
PolicyArn: aws.String(accountRolePolicyArn),
569578
}).Return(&iam.DeletePolicyOutput{}, nil)
570-
err := client.DeleteAccountRole(accountRole, rolePrefix, false)
579+
err := client.DeleteAccountRole(accountRole, rolePrefix, false, false)
571580
Expect(err).NotTo(HaveOccurred())
572581
})
573582
It("Test DeleteOperatorRole", func() {
@@ -602,7 +611,7 @@ var _ = Describe("Cluster Roles/Policies", func() {
602611
AttachmentCount: &attachCount,
603612
},
604613
}, nil)
605-
err := client.DeleteOperatorRole(operatorRole, false)
614+
_, err := client.DeleteOperatorRole(operatorRole, false, false)
606615
Expect(err).NotTo(HaveOccurred())
607616
})
608617
})

pkg/roles/utils.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,29 @@ func GetHcpSharedVpcPolicyDetails(r *rosa.Runtime, roleArn string) (bool, string
5858

5959
return existsQuery != nil, createPolicy, policyName, nil
6060
}
61+
62+
func CheckIfRolesAreHcpSharedVpc(r *rosa.Runtime, roles []string) bool {
63+
isHcpSharedVpc := false
64+
for _, roleName := range roles {
65+
ptrRoleName := roleName
66+
attachedPolicies, err := r.AWSClient.GetPolicyDetailsFromRole(&ptrRoleName)
67+
if err != nil {
68+
r.Reporter.Errorf("Failed to get policy details for role '%s': %v", roleName, err)
69+
}
70+
for _, attachedPolicy := range attachedPolicies {
71+
rhManaged := false
72+
hcpSharedVpc := false
73+
for _, tag := range attachedPolicy.Policy.Tags {
74+
if *tag.Key == tags.RedHatManaged {
75+
rhManaged = true
76+
} else if *tag.Key == tags.HcpSharedVpc {
77+
hcpSharedVpc = true
78+
}
79+
}
80+
if rhManaged && hcpSharedVpc {
81+
isHcpSharedVpc = true
82+
}
83+
}
84+
}
85+
return isHcpSharedVpc
86+
}

0 commit comments

Comments
 (0)