Skip to content

Commit 3747c0a

Browse files
committed
Enhancements to the code
1 parent 41017cd commit 3747c0a

File tree

4 files changed

+168
-190
lines changed

4 files changed

+168
-190
lines changed

Diff for: pkg/controller/iamrole/aws.go

+159-83
Original file line numberDiff line numberDiff line change
@@ -11,98 +11,153 @@ import (
1111
iamv1beta1 "github.com/ihoegen/iam-role-manager/pkg/apis/iam/v1beta1"
1212
)
1313

14+
// IAMClient provides an interface with interacting with AWS
15+
type IAMClient struct {
16+
Client *iam.IAM
17+
Role *iamv1beta1.IAMRole
18+
}
19+
20+
// NewIAMClient returns a new client for interacting with AWS IAM
21+
func NewIAMClient(client *iam.IAM, role *iamv1beta1.IAMRole) IAMClient {
22+
return IAMClient{
23+
Client: client,
24+
Role: role,
25+
}
26+
}
27+
1428
//CreateIAMRole creates an IAM role in AWS, based on a spec
15-
func CreateIAMRole(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error {
16-
roleName := iamRole.ObjectMeta.GetName()
17-
createRoleOutput, err := iamClient.CreateRole(&iam.CreateRoleInput{
18-
AssumeRolePolicyDocument: &iamRole.Spec.TrustRelationship,
19-
Description: &iamRole.Spec.Description,
20-
Path: &iamRole.Spec.Path,
29+
func (i *IAMClient) CreateIAMRole() error {
30+
roleName := i.Role.ObjectMeta.GetName()
31+
createRoleOutput, err := i.Client.CreateRole(&iam.CreateRoleInput{
32+
AssumeRolePolicyDocument: &i.Role.Spec.TrustRelationship,
33+
Description: &i.Role.Spec.Description,
34+
Path: &i.Role.Spec.Path,
2135
RoleName: &roleName,
22-
MaxSessionDuration: &iamRole.Spec.MaxSessionDuration,
36+
MaxSessionDuration: &i.Role.Spec.MaxSessionDuration,
2337
})
2438
if err != nil {
2539
return err
2640
}
27-
iamRole.Status.ARN = *createRoleOutput.Role.Arn
28-
iamRole.Status.RoleID = *createRoleOutput.Role.RoleId
29-
err = createInlinePolicies(iamClient, iamRole)
41+
i.Role.Status.ARN = *createRoleOutput.Role.Arn
42+
i.Role.Status.RoleID = *createRoleOutput.Role.RoleId
43+
err = i.createInlinePolicies()
3044
if err != nil {
3145
return err
3246
}
33-
err = attachPolicies(iamClient, iamRole)
47+
err = i.attachPolicies()
3448
if err != nil {
3549
return err
3650
}
3751
return nil
3852
}
3953

40-
//DeleteIAMRole deletes an IAM role with a matching name
41-
func DeleteIAMRole(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error {
42-
var errors []error
43-
roleName := iamRole.ObjectMeta.GetName()
44-
err := removeInlinePolicies(iamClient, iamRole)
45-
attachedPolicies, err := iamClient.ListAttachedRolePolicies(&iam.ListAttachedRolePoliciesInput{
46-
RoleName: &roleName,
47-
})
54+
//DeleteIAMRole deletes an IAM role
55+
func (i *IAMClient) DeleteIAMRole() error {
56+
roleName := i.Role.ObjectMeta.GetName()
57+
currentPolicies, err := i.listInlinePolicies(roleName)
4858
if err != nil {
4959
return err
5060
}
51-
for _, policy := range attachedPolicies.AttachedPolicies {
52-
_, err = iamClient.DetachRolePolicy(&iam.DetachRolePolicyInput{
61+
for _, policy := range currentPolicies {
62+
_, err = i.Client.DeleteRolePolicy(&iam.DeleteRolePolicyInput{
63+
PolicyName: &policy,
64+
RoleName: &roleName,
65+
})
66+
if err != nil {
67+
return err
68+
}
69+
}
70+
attachedPolicies, err := i.listAttachedPolicies(roleName)
71+
if err != nil {
72+
return err
73+
}
74+
for _, policy := range attachedPolicies {
75+
_, err = i.Client.DetachRolePolicy(&iam.DetachRolePolicyInput{
5376
PolicyArn: policy.PolicyArn,
5477
RoleName: &roleName,
5578
})
5679
if err != nil {
57-
errors = append(errors, err)
80+
return err
5881
}
5982
}
60-
_, err = iamClient.DeleteRole(&iam.DeleteRoleInput{
83+
_, err = i.Client.DeleteRole(&iam.DeleteRoleInput{
6184
RoleName: &roleName,
6285
})
63-
if len(errors) > 0 {
64-
return fmt.Errorf("Errors occurred while detaching policies: %v", errors)
65-
}
6686
return err
6787
}
6888

6989
//SyncIAMRole synchronizes an AWS IAM Role to a spec
70-
func SyncIAMRole(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error {
90+
func (i *IAMClient) SyncIAMRole() error {
7191
var errors []error
72-
roleName := iamRole.ObjectMeta.GetName()
73-
_, err := iamClient.UpdateRole(&iam.UpdateRoleInput{
74-
Description: &iamRole.Spec.Description,
75-
MaxSessionDuration: &iamRole.Spec.MaxSessionDuration,
76-
RoleName: &roleName,
92+
roleName := i.Role.ObjectMeta.GetName()
93+
getRoleOutput, err := i.Client.GetRole(&iam.GetRoleInput{
94+
RoleName: &roleName,
7795
})
7896
if err != nil {
7997
return err
8098
}
81-
_, err = iamClient.UpdateAssumeRolePolicy(&iam.UpdateAssumeRolePolicyInput{
82-
RoleName: &roleName,
83-
PolicyDocument: &iamRole.Spec.TrustRelationship,
84-
})
85-
err = removeInlinePolicies(iamClient, iamRole)
99+
awsRole := *getRoleOutput.Role
100+
if *awsRole.Description != i.Role.Spec.Description {
101+
_, err = i.Client.UpdateRoleDescription(&iam.UpdateRoleDescriptionInput{
102+
Description: &i.Role.Spec.Description,
103+
RoleName: &roleName,
104+
})
105+
if err != nil {
106+
return err
107+
}
108+
}
109+
if *awsRole.MaxSessionDuration != i.Role.Spec.MaxSessionDuration {
110+
_, err = i.Client.UpdateRole(&iam.UpdateRoleInput{
111+
RoleName: &roleName,
112+
MaxSessionDuration: &i.Role.Spec.MaxSessionDuration,
113+
})
114+
if err != nil {
115+
return err
116+
}
117+
}
118+
if *awsRole.AssumeRolePolicyDocument != i.Role.Spec.TrustRelationship {
119+
_, err = i.Client.UpdateAssumeRolePolicy(&iam.UpdateAssumeRolePolicyInput{
120+
RoleName: &roleName,
121+
PolicyDocument: &i.Role.Spec.TrustRelationship,
122+
})
123+
if err != nil {
124+
return err
125+
}
126+
}
127+
err = i.createInlinePolicies()
86128
if err != nil {
87129
return err
88130
}
89-
err = createInlinePolicies(iamClient, iamRole)
131+
inlinePolicies, err := i.listInlinePolicies(roleName)
90132
if err != nil {
91133
return err
92134
}
93-
err = attachPolicies(iamClient, iamRole)
135+
var requestedInlinePolicies []string
136+
for _, p := range i.Role.Spec.InlinePolicy {
137+
requestedInlinePolicies = append(requestedInlinePolicies, p.Name)
138+
}
139+
for _, policy := range inlinePolicies {
140+
if !in(requestedInlinePolicies, policy) {
141+
_, err = i.Client.DeleteRolePolicy(&iam.DeleteRolePolicyInput{
142+
PolicyName: &policy,
143+
RoleName: &roleName,
144+
})
145+
if err != nil {
146+
errors = append(errors, err)
147+
}
148+
}
149+
}
150+
err = i.attachPolicies()
94151
if err != nil {
95152
errors = append(errors, err)
96153
}
97-
attachedPolicies, err := iamClient.ListAttachedRolePolicies(&iam.ListAttachedRolePoliciesInput{
98-
RoleName: &roleName,
99-
})
154+
attachedPolicies, err := i.listAttachedPolicies(roleName)
100155
if err != nil {
101156
return err
102157
}
103-
for _, policy := range attachedPolicies.AttachedPolicies {
104-
if !in(iamRole.Spec.Policies, *policy.PolicyArn) && !in(iamRole.Spec.Policies, *policy.PolicyName) {
105-
_, err = iamClient.DetachRolePolicy(&iam.DetachRolePolicyInput{
158+
for _, policy := range attachedPolicies {
159+
if !in(i.Role.Spec.Policies, *policy.PolicyArn) && !in(i.Role.Spec.Policies, *policy.PolicyName) {
160+
_, err = i.Client.DetachRolePolicy(&iam.DetachRolePolicyInput{
106161
PolicyArn: policy.PolicyArn,
107162
RoleName: &roleName,
108163
})
@@ -117,25 +172,24 @@ func SyncIAMRole(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error {
117172
return nil
118173
}
119174

120-
// Checks to see if a named IAM Role exists in AWS
121-
// TODO: Enhance the logic
122-
func iamRoleExists(iamClient *iam.IAM, roleName string) bool {
123-
_, err := iamClient.GetRole(&iam.GetRoleInput{
175+
// IAMRoleExists Checks to see if a named IAM Role exists in AWS
176+
func (i *IAMClient) IAMRoleExists(roleName string) bool {
177+
_, err := i.Client.GetRole(&iam.GetRoleInput{
124178
RoleName: &roleName,
125179
})
126180
return err == nil
127181
}
128182

129183
// Attaches policies found in the spec to a named IAM role
130-
func attachPolicies(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error {
131-
roleName := iamRole.ObjectMeta.GetName()
184+
func (i *IAMClient) attachPolicies() error {
185+
roleName := i.Role.ObjectMeta.GetName()
132186
var errors []error
133-
for _, policy := range iamRole.Spec.Policies {
187+
for _, policy := range i.Role.Spec.Policies {
134188
policyArn, err := getArn(policy)
135189
if err != nil {
136190
return err
137191
}
138-
_, err = iamClient.AttachRolePolicy(&iam.AttachRolePolicyInput{
192+
_, err = i.Client.AttachRolePolicy(&iam.AttachRolePolicyInput{
139193
PolicyArn: &policyArn,
140194
RoleName: &roleName,
141195
})
@@ -150,11 +204,11 @@ func attachPolicies(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error {
150204
}
151205

152206
// Creates inline polices defined in a spec and attaches it to a role
153-
func createInlinePolicies(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error {
207+
func (i *IAMClient) createInlinePolicies() error {
154208
var errors []error
155-
roleName := iamRole.ObjectMeta.GetName()
156-
for _, inlinePolicy := range iamRole.Spec.InlinePolicy {
157-
_, err := iamClient.PutRolePolicy(&iam.PutRolePolicyInput{
209+
roleName := i.Role.ObjectMeta.GetName()
210+
for _, inlinePolicy := range i.Role.Spec.InlinePolicy {
211+
_, err := i.Client.PutRolePolicy(&iam.PutRolePolicyInput{
158212
PolicyDocument: &inlinePolicy.Value,
159213
RoleName: &roleName,
160214
PolicyName: &inlinePolicy.Name,
@@ -169,31 +223,6 @@ func createInlinePolicies(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error
169223
return nil
170224
}
171225

172-
// Removes inline policies from a role
173-
func removeInlinePolicies(iamClient *iam.IAM, iamRole *iamv1beta1.IAMRole) error {
174-
var errors []error
175-
roleName := iamRole.ObjectMeta.GetName()
176-
currentPolicies, err := iamClient.ListRolePolicies(&iam.ListRolePoliciesInput{
177-
RoleName: &roleName,
178-
})
179-
if err != nil {
180-
return err
181-
}
182-
for _, policy := range currentPolicies.PolicyNames {
183-
_, err = iamClient.DeleteRolePolicy(&iam.DeleteRolePolicyInput{
184-
PolicyName: policy,
185-
RoleName: &roleName,
186-
})
187-
if err != nil {
188-
errors = append(errors, err)
189-
}
190-
}
191-
if len(errors) > 0 {
192-
return fmt.Errorf("Errors occurred while attaching policies: %v", errors)
193-
}
194-
return nil
195-
}
196-
197226
// Returns the ARN of a policy; allows for simply naming policies
198227
func getArn(policyName string) (string, error) {
199228
if isArn(policyName) {
@@ -208,7 +237,54 @@ func getArn(policyName string) (string, error) {
208237
}
209238

210239
// Returns if a policy string is an ARN
211-
// TODO: Enhance the logic
212240
func isArn(policy string) bool {
213241
return strings.Contains(policy, "arn:aws:iam")
214242
}
243+
244+
// Paginate over inline policies
245+
func (i *IAMClient) listInlinePolicies(roleName string) ([]string, error) {
246+
var policyNamesPointers []*string
247+
isTruncated := true
248+
marker := "1"
249+
for isTruncated {
250+
currentPolicies, err := i.Client.ListRolePolicies(&iam.ListRolePoliciesInput{
251+
RoleName: &roleName,
252+
Marker: &marker,
253+
})
254+
if err != nil {
255+
return nil, err
256+
}
257+
policyNamesPointers = append(policyNamesPointers, currentPolicies.PolicyNames...)
258+
marker = *currentPolicies.Marker
259+
isTruncated = *currentPolicies.IsTruncated
260+
}
261+
var policyNameValues []string
262+
for _, val := range policyNamesPointers {
263+
policyNameValues = append(policyNameValues, *val)
264+
}
265+
return policyNameValues, nil
266+
}
267+
268+
// Paginate over attached policies
269+
func (i *IAMClient) listAttachedPolicies(roleName string) ([]iam.AttachedPolicy, error) {
270+
var policyPointers []*iam.AttachedPolicy
271+
isTruncated := true
272+
marker := "1"
273+
for isTruncated {
274+
currentPolicies, err := i.Client.ListAttachedRolePolicies(&iam.ListAttachedRolePoliciesInput{
275+
RoleName: &roleName,
276+
Marker: &marker,
277+
})
278+
if err != nil {
279+
return nil, err
280+
}
281+
policyPointers = append(policyPointers, currentPolicies.AttachedPolicies...)
282+
marker = *currentPolicies.Marker
283+
isTruncated = *currentPolicies.IsTruncated
284+
}
285+
var policyNameValues []iam.AttachedPolicy
286+
for _, val := range policyPointers {
287+
policyNameValues = append(policyNameValues, *val)
288+
}
289+
return policyNameValues, nil
290+
}

Diff for: pkg/controller/iamrole/iamrole_controller.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,25 @@ type ReconcileIAMRole struct {
8181
func (r *ReconcileIAMRole) Reconcile(request reconcile.Request) (reconcile.Result, error) {
8282
// Fetch the IAMRole instance
8383
iamRole := &iamv1beta1.IAMRole{}
84-
iamClient := iam.New(session.New())
84+
client := iam.New(session.New())
85+
iamClient := NewIAMClient(client, iamRole)
8586
err := r.Get(context.TODO(), request.NamespacedName, iamRole)
8687
if err != nil {
8788
// IAM role deleted
8889
if errors.IsNotFound(err) {
8990
iamRole.ObjectMeta.SetName(request.Name)
90-
err = DeleteIAMRole(iamClient, iamRole)
91+
err = iamClient.DeleteIAMRole()
92+
if err != nil {
93+
log.Println("Error deleting IAM Role", request.Name, ":", err)
94+
}
9195
return reconcile.Result{}, err
9296
}
9397
// Error reading the object - requeue the request.
9498
return reconcile.Result{}, err
9599
}
96100
// IAM Role exists in AWS; updating
97-
if iamRoleExists(iamClient, iamRole.ObjectMeta.GetName()) {
98-
err = SyncIAMRole(iamClient, iamRole)
101+
if iamClient.IAMRoleExists(iamRole.ObjectMeta.GetName()) {
102+
err = iamClient.SyncIAMRole()
99103
if err != nil {
100104
eventRecorder.Event(iamRole, "Warning", "ErrorSyncingIAMRole", err.Error())
101105
return reconcile.Result{}, err
@@ -104,7 +108,7 @@ func (r *ReconcileIAMRole) Reconcile(request reconcile.Request) (reconcile.Resul
104108
return reconcile.Result{}, nil
105109
}
106110
// IAM Role doesn't exist in AWS; creating
107-
err = CreateIAMRole(iamClient, iamRole)
111+
err = iamClient.CreateIAMRole()
108112
if err != nil {
109113
eventRecorder.Event(iamRole, "Warning", "ErrorCreatingIAMRole", err.Error())
110114
return reconcile.Result{}, err

0 commit comments

Comments
 (0)