Skip to content

Commit b94c178

Browse files
committed
Fix Role reconciliation logic for AssumeRolePolicyDocument
Addressed a bug in the Role reconciliation mechanism related to the configuration of a multi-line `AssumeRolePolicyDocument` for the `Role` resources. The problem was observed as an infinite loop of requeues and update atempts by the controller. The root cause was traced back to the behavior of the IAM api, which silently removed spaces and newline character from the `AssumeRolePolicyDocument`, leading to faulty comparisons during the reconciliation process This commit introduce a solution by removing all instances of space `(' ')` and newline `('\n')` characters from the targeted `AssumeRolePolicyDocument`. As aa result, the reconciliation process now accurately validates and updates the field, eliminating the previous issue Use @a-hilaly fork of github.com/micahhausler/aws-iam-policy while waiting for a proper patch/PR Signed-off-by: Amine Hilaly <[email protected]>
1 parent 4c6cfa9 commit b94c178

File tree

8 files changed

+78
-16
lines changed

8 files changed

+78
-16
lines changed
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
ack_generate_info:
2-
build_date: "2023-10-04T06:33:03Z"
3-
build_hash: 7445de38211639a12e79992d154adab6e60f01fd
4-
go_version: go1.21.0
5-
version: v0.27.1-1-g7445de3
2+
build_date: "2023-11-21T06:03:03Z"
3+
build_hash: 1cc9b5172d3d1676af578a3411e8672698ec29ce
4+
go_version: go1.21.1
5+
version: v0.27.1-5-g1cc9b51
66
api_directory_checksum: 5f836e773a26000be60b198c9166f83ea86405e1
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.44.93
99
generator_config_info:
10-
file_checksum: fe29e906cfb41430ae4df1e2dc17ea4a3fb0365f
10+
file_checksum: ee030a9c18c7fa34d8f7f4777997df355c029971
1111
original_file_name: generator.yaml
1212
last_modification:
1313
reason: API generation

apis/v1alpha1/generator.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ resources:
174174
Role:
175175
hooks:
176176
delta_pre_compare:
177-
code: compareTags(delta, a, b)
177+
code: customPreCompare(delta, a, b)
178178
sdk_read_one_post_set_output:
179179
template_path: hooks/role/sdk_read_one_post_set_output.go.tpl
180180
sdk_create_post_set_output:
@@ -232,6 +232,9 @@ resources:
232232
# policy document.
233233
InlinePolicies:
234234
type: map[string]*string
235+
AssumeRolePolicyDocument:
236+
compare:
237+
is_ignored: true
235238
Tags:
236239
compare:
237240
is_ignored: true

generator.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ resources:
174174
Role:
175175
hooks:
176176
delta_pre_compare:
177-
code: compareTags(delta, a, b)
177+
code: customPreCompare(delta, a, b)
178178
sdk_read_one_post_set_output:
179179
template_path: hooks/role/sdk_read_one_post_set_output.go.tpl
180180
sdk_create_post_set_output:
@@ -232,6 +232,9 @@ resources:
232232
# policy document.
233233
InlinePolicies:
234234
type: map[string]*string
235+
AssumeRolePolicyDocument:
236+
compare:
237+
is_ignored: true
235238
Tags:
236239
compare:
237240
is_ignored: true

go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ require (
66
github.com/aws-controllers-k8s/runtime v0.27.1
77
github.com/aws/aws-sdk-go v1.44.93
88
github.com/go-logr/logr v1.2.3
9+
github.com/google/go-cmp v0.5.9
10+
github.com/micahhausler/aws-iam-policy v0.4.2
911
github.com/samber/lo v1.37.0
1012
github.com/spf13/pflag v1.0.5
1113
k8s.io/api v0.26.8
@@ -14,6 +16,10 @@ require (
1416
sigs.k8s.io/controller-runtime v0.14.5
1517
)
1618

19+
// Temporary fix for github.com/micahhausler/aws-iam-policy. Awaiting for a-hilaly to send
20+
// a PR to micahhausler/aws-iam-policy to build Equal() method for PolicyDocument struct.
21+
replace github.com/micahhausler/aws-iam-policy => github.com/a-hilaly/aws-iam-policy v0.0.0-20231121054900-2c56e839ca53
22+
1723
require (
1824
github.com/beorn7/perks v1.0.1 // indirect
1925
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
@@ -30,7 +36,6 @@ require (
3036
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
3137
github.com/golang/protobuf v1.5.2 // indirect
3238
github.com/google/gnostic v0.5.7-v3refs // indirect
33-
github.com/google/go-cmp v0.5.9 // indirect
3439
github.com/google/gofuzz v1.1.0 // indirect
3540
github.com/google/uuid v1.3.0 // indirect
3641
github.com/imdario/mergo v0.3.12 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
3333
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
3434
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
3535
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
36+
github.com/a-hilaly/aws-iam-policy v0.0.0-20231121054900-2c56e839ca53 h1:2uNM0nR2WUDN88EYFxjEaroH+PZJ6k/h9kl+KO0dWVc=
37+
github.com/a-hilaly/aws-iam-policy v0.0.0-20231121054900-2c56e839ca53/go.mod h1:Ojgst9ZFn+VEEJpqtuw/LxVGqEf2+hwWBlkYWvF/XWM=
3638
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
3739
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
3840
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=

pkg/resource/role/delta.go

Lines changed: 1 addition & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/resource/role/hooks.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ package role
1515

1616
import (
1717
"context"
18+
"encoding/json"
1819
"net/url"
20+
"reflect"
1921

2022
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
2123
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
2224
ackutil "github.com/aws-controllers-k8s/runtime/pkg/util"
2325
svcsdk "github.com/aws/aws-sdk-go/service/iam"
26+
awsiampolicy "github.com/micahhausler/aws-iam-policy/policy"
2427
"github.com/samber/lo"
2528

2629
svcapitypes "github.com/aws-controllers-k8s/iam-controller/apis/v1alpha1"
@@ -366,6 +369,17 @@ func (rm *resourceManager) putAssumeRolePolicy(
366369
return err
367370
}
368371

372+
// customPreCompare contains logic that help compare two iam Roles. This
373+
// function is injected in newResourceDelta function.
374+
func customPreCompare(
375+
delta *ackcompare.Delta,
376+
a *resource,
377+
b *resource,
378+
) {
379+
compareTags(delta, a, b)
380+
compareAssumeRolePolicyDocument(delta, a, b)
381+
}
382+
369383
// compareTags is a custom comparison function for comparing lists of Tag
370384
// structs where the order of the structs in the list is not important.
371385
func compareTags(
@@ -382,6 +396,42 @@ func compareTags(
382396
}
383397
}
384398

399+
// compareAssumeRolePolicyDocument is a custom comparison function for
400+
// assumeRolePolicyDocuments. The reason why we need a custom function for
401+
// this fields is the API logic that trims all the trailing while spaces
402+
// string of provided documents.
403+
func compareAssumeRolePolicyDocument(
404+
delta *ackcompare.Delta,
405+
a *resource,
406+
b *resource,
407+
) {
408+
// To handle the variability in shapes of JSON objects representing IAM policies,
409+
// especially when it comes to statements, actions, and other fields, we need
410+
// a custom json.Unmarshaller approach crafted to our specific needs. Luckily,
411+
// it happens that @micahhausler buildta library dedicated to this very special
412+
// need: github.com/micahhausler/aws-iam-policy.
413+
//
414+
// NOTE(a-hilaly): I'm pretty aware that there is an error that should be handled.
415+
// However, unfortunetly, the `newResourceDelta` cannot return errors (for now),
416+
// leaving us with only two solutions, panicking or ignoring the error. The first
417+
// solution is an overkill as it will interrupt all the goroutines from functioning
418+
// and causing the controller to enter in a 'CrashLoopBackOff' state, which is not
419+
// fair, given that it's also is responsible of managing multiple objects of other
420+
// different resources.
421+
//
422+
// TOOD(a-hilaly): To address this issue, concider changing the delta signature
423+
// to return an error or take a context.Context to use the runtime logger. Both
424+
// of these changes require runtime/code-generator changes.
425+
var policyDocumentA awsiampolicy.Policy
426+
_ = json.Unmarshal([]byte(*a.ko.Spec.AssumeRolePolicyDocument), &policyDocumentA)
427+
var policyDocumentB awsiampolicy.Policy
428+
_ = json.Unmarshal([]byte(*b.ko.Spec.AssumeRolePolicyDocument), &policyDocumentB)
429+
430+
if !reflect.DeepEqual(policyDocumentA, policyDocumentB) {
431+
delta.Add("Spec.AssumeRolePolicyDocument", a.ko.Spec.AssumeRolePolicyDocument, b.ko.Spec.AssumeRolePolicyDocument)
432+
}
433+
}
434+
385435
// syncTags examines the Tags in the supplied Role and calls the ListRoleTags,
386436
// TagRole and UntagRole APIs to ensure that the set of associated Tags stays
387437
// in sync with the Role.Spec.Tags

test/e2e/tests/test_role.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,9 @@ def test_crud(self, simple_role):
293293
k8s_assume_role_policy = json.loads(cr['spec']['assumeRolePolicyDocument'])
294294
assert assume_role_policy_as_obj == k8s_assume_role_policy
295295

296+
# make sure the resource is not in an "update infinite loop"
297+
condition.assert_synced(ref)
298+
296299
assume_role_policy_to_deny_doc = '''{
297300
"Version": "2012-10-17",
298301
"Statement": [
@@ -329,3 +332,6 @@ def test_crud(self, simple_role):
329332
assert latest_assume_role_policy_doc['Statement'][0]['Effect'] == k8s_assume_role_policy_deny['Statement'][0]['Effect']
330333

331334
# Assume role policies cannot be entirely deleted, so CRU is tested here.
335+
336+
# make sure the resource is not in an "update infinite loop"
337+
condition.assert_synced(ref)

0 commit comments

Comments
 (0)