Skip to content

Commit a8a6393

Browse files
authored
feat: sync assume role policy document (#74)
Issue #, if available: aws-controllers-k8s/community#1448, but this also includes `AssumeRolePolicyDocument` in general as these are not updated regardless of whether the document change is valid or not. Description of changes: Adds a call to sync the `spec.assumeRolePolicyDocument` object, alongside the corresponding AWS API calls. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 90330ae commit a8a6393

File tree

6 files changed

+113
-8
lines changed

6 files changed

+113
-8
lines changed

apis/v1alpha1/ack-generate-metadata.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
ack_generate_info:
2-
build_date: "2023-03-22T22:04:22Z"
3-
build_hash: fa24753ea8b657d8815ae3eac7accd0958f5f9fb
4-
go_version: go1.19
5-
version: v0.25.0
2+
build_date: "2023-04-03T14:40:28Z"
3+
build_hash: a6ae2078e57187b2daf47978bc07bd67072d2cba
4+
go_version: go1.19.6
5+
version: v0.25.0-1-ga6ae207
66
api_directory_checksum: 26341f700d12dfcd4033cf4203492fa381daa7b0
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.44.93

pkg/resource/role/hooks.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,25 @@ func (rm *resourceManager) removeInlinePolicy(
342342
return err
343343
}
344344

345+
// putAssumeRolePolicies calls the IAM API to set a given role's
346+
// assume role policy document.
347+
func (rm *resourceManager) putAssumeRolePolicy(
348+
ctx context.Context,
349+
r *resource,
350+
) (err error) {
351+
rlog := ackrtlog.FromContext(ctx)
352+
exit := rlog.Trace("rm.putAssumeRolePolicy")
353+
defer func() { exit(err) }()
354+
355+
input := &svcsdk.UpdateAssumeRolePolicyInput{
356+
RoleName: r.ko.Spec.Name,
357+
PolicyDocument: r.ko.Spec.AssumeRolePolicyDocument,
358+
}
359+
_, err = rm.sdkapi.UpdateAssumeRolePolicyWithContext(ctx, input)
360+
rm.metrics.RecordAPICall("UPDATE", "UpdateAssumeRolePolicy", err)
361+
return err
362+
}
363+
345364
// compareTags is a custom comparison function for comparing lists of Tag
346365
// structs where the order of the structs in the list is not important.
347366
func compareTags(

pkg/resource/role/sdk.go

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

templates/hooks/role/sdk_update_pre_build_request.go.tpl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
return nil, err
2323
}
2424
}
25-
if !delta.DifferentExcept("Spec.Tags", "Spec.Policies", "Spec.InlinePolicies", "Spec.PermissionsBoundary") {
25+
if delta.DifferentAt("Spec.AssumeRolePolicyDocument") {
26+
err = rm.putAssumeRolePolicy(ctx, desired)
27+
if err != nil {
28+
return nil, err
29+
}
30+
}
31+
if !delta.DifferentExcept("Spec.Tags", "Spec.Policies", "Spec.InlinePolicies", "Spec.PermissionsBoundary", "Spec.AssumeRolePolicyDocument") {
2632
return desired, nil
2733
}

test/e2e/role.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,16 @@ def get_inline_policies(role_name):
149149
return policies
150150
except c.exceptions.NoSuchEntityException:
151151
return None
152+
153+
def get_assume_role_policy(role_name):
154+
"""Returns a dict representing the assume role policy document for the supplied Role.
155+
156+
If no such Role exists, returns None.
157+
"""
158+
c = boto3.client('iam')
159+
try:
160+
resp = get(role_name)
161+
return resp['AssumeRolePolicyDocument']
162+
except c.exceptions.NoSuchEntityException:
163+
return None
164+

test/e2e/tests/test_role.py

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""Integration tests for the IAM Role resource"""
1515

1616
import logging
17+
import json
1718
import time
1819

1920
import pytest
@@ -212,8 +213,68 @@ def test_crud(self, simple_role):
212213

213214
cr = k8s.get_resource(ref)
214215
assert cr is not None
215-
assert 'spec' in cr
216-
assert 'inlinePolicies' not in cr['spec']
216+
assert "spec" in cr
217+
assert "inlinePolicies" not in cr["spec"]
217218

218219
latest_inline_policies = role.get_inline_policies(role_name)
219220
assert len(latest_inline_policies) == 0
221+
222+
# AssumeRolePolicyDocument tests
223+
224+
assume_role_policy_doc = '''{
225+
"Version": "2012-10-17",
226+
"Statement": [
227+
{
228+
"Effect": "Allow",
229+
"Principal": {
230+
"Service": ["ec2.amazonaws.com"]
231+
},
232+
"Action": ["sts:AssumeRole"]
233+
}
234+
]}'''
235+
236+
cr = k8s.get_resource(ref)
237+
assert cr is not None
238+
assert 'spec' in cr
239+
assert 'assumeRolePolicyDocument' in cr['spec']
240+
241+
assume_role_policy_as_obj = json.loads(assume_role_policy_doc)
242+
k8s_assume_role_policy = json.loads(cr['spec']['assumeRolePolicyDocument'])
243+
assert assume_role_policy_as_obj == k8s_assume_role_policy
244+
245+
assume_role_policy_to_deny_doc = '''{
246+
"Version": "2012-10-17",
247+
"Statement": [
248+
{
249+
"Effect": "Deny",
250+
"Principal": {
251+
"Service": ["ec2.amazonaws.com"]
252+
},
253+
"Action": ["sts:AssumeRole"]
254+
}
255+
]}'''
256+
257+
updates = {
258+
'spec': {
259+
'assumeRolePolicyDocument': assume_role_policy_to_deny_doc,
260+
}
261+
}
262+
263+
k8s.patch_custom_resource(ref, updates)
264+
time.sleep(MODIFY_WAIT_AFTER_SECONDS)
265+
266+
cr = k8s.get_resource(ref)
267+
assert cr is not None
268+
assert 'spec' in cr
269+
assert 'assumeRolePolicyDocument' in cr['spec']
270+
271+
assume_role_policy_deny_obj = json.loads(assume_role_policy_to_deny_doc)
272+
k8s_assume_role_policy_deny = json.loads(cr['spec']['assumeRolePolicyDocument'])
273+
assert assume_role_policy_deny_obj == k8s_assume_role_policy_deny
274+
275+
# AWS slightly modifies the JSON structure underneath us here, so the documents
276+
# are not identical. Instead, we can ensure that the change we made is reflected.
277+
latest_assume_role_policy_doc = role.get_assume_role_policy(role_name)
278+
assert latest_assume_role_policy_doc['Statement'][0]['Effect'] == k8s_assume_role_policy_deny['Statement'][0]['Effect']
279+
280+
# Assume role policies cannot be entirely deleted, so CRU is tested here.

0 commit comments

Comments
 (0)