@@ -15,9 +15,10 @@ package access_entry
1515
1616import (
1717 "context"
18- "reflect"
1918
19+ ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
2020 ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
21+ "github.com/aws/aws-sdk-go/aws"
2122 svcsdk "github.com/aws/aws-sdk-go/service/eks"
2223
2324 "github.com/aws-controllers-k8s/eks-controller/apis/v1alpha1"
@@ -87,34 +88,21 @@ func (rm *resourceManager) syncAccessPolicies(ctx context.Context, desired, late
8788 rlog := ackrtlog .FromContext (ctx )
8889 exit := rlog .Trace ("rm.syncAccessPolicies" )
8990 defer func () { exit (err ) }()
90- toAdd := []* v1alpha1.AssociateAccessPolicyInput {}
91- toDelete := []* string {}
9291
9392 existingPolicies := latest .ko .Spec .AccessPolicies
93+ desiredPolicies := desired .ko .Spec .AccessPolicies
9494
95- // find the policies to add
96- for _ , p := range desired .ko .Spec .AccessPolicies {
97- if ! exactMatchInAccessPolicies (p , existingPolicies ) {
98- toAdd = append (toAdd , p )
99- }
100- }
95+ toAdd , toDelete := computeAccessPoliciesDelta (desiredPolicies , existingPolicies )
10196
102- // find the policies to delete
103- for _ , p := range existingPolicies {
104- if ! inAccessPolicies (p , desired .ko .Spec .AccessPolicies ) {
105- toDelete = append (toDelete , p .PolicyARN )
106- }
107- }
108-
109- // manage policies...
97+ // remove policies first (to avoid conflicts)
11098 for _ , p := range toDelete {
111- rlog .Debug ("disassociating access policy from role " , "policy_arn" , * p )
99+ rlog .Debug ("disassociating access policy from access entry " , "policy_arn" , * p )
112100 if err = rm .disassociateAccessPolicy (ctx , desired , p ); err != nil {
113101 return err
114102 }
115103 }
116104 for _ , p := range toAdd {
117- rlog .Debug ("associate access policy to access entry" , "policy_arn" , * p .PolicyARN )
105+ rlog .Debug ("associating access policy to access entry" , "policy_arn" , * p .PolicyARN )
118106 if err = rm .associateAccessPolicy (ctx , desired , p ); err != nil {
119107 return err
120108 }
@@ -123,28 +111,6 @@ func (rm *resourceManager) syncAccessPolicies(ctx context.Context, desired, late
123111 return nil
124112}
125113
126- // inAccessPolicies returns true if the supplied AccessPolicy ARN exists
127- // in the slice of AccessPolicy objects.
128- func inAccessPolicies (policy * v1alpha1.AssociateAccessPolicyInput , policies []* v1alpha1.AssociateAccessPolicyInput ) bool {
129- for _ , p := range policies {
130- if p .PolicyARN == policy .PolicyARN {
131- return false
132- }
133- }
134- return false
135- }
136-
137- // exactMatchInAccessPolicies returns true if the supplied AccessPolicy is in the
138- // slice of AccessPolicy objects and the AccessScope is exactly the same.
139- func exactMatchInAccessPolicies (policy * v1alpha1.AssociateAccessPolicyInput , policies []* v1alpha1.AssociateAccessPolicyInput ) bool {
140- for _ , p := range policies {
141- if p .PolicyARN == policy .PolicyARN {
142- return reflect .DeepEqual (p .AccessScope , policy .AccessScope )
143- }
144- }
145- return false
146- }
147-
148114// associateAccessPolicy adds the supplied AccessPolicy to the supplied
149115// AccessEntry resource.
150116func (rm * resourceManager ) associateAccessPolicy (
@@ -153,7 +119,7 @@ func (rm *resourceManager) associateAccessPolicy(
153119 entry * v1alpha1.AssociateAccessPolicyInput ,
154120) (err error ) {
155121 rlog := ackrtlog .FromContext (ctx )
156- exit := rlog .Trace ("rm.addManagedPolicy " )
122+ exit := rlog .Trace ("rm.associateAccessPolicy " )
157123 defer func () { exit (err ) }()
158124
159125 input := & svcsdk.AssociateAccessPolicyInput {
@@ -190,3 +156,116 @@ func (rm *resourceManager) disassociateAccessPolicy(
190156 rm .metrics .RecordAPICall ("UPDATE" , "DisassociateAccessPolicy" , err )
191157 return err
192158}
159+
160+ // inAccessPolicies returns true if the supplied AccessPolicy ARN exists
161+ // in the slice of AccessPolicy objects.
162+ func inAccessPolicies (policy * v1alpha1.AssociateAccessPolicyInput , policies []* v1alpha1.AssociateAccessPolicyInput ) bool {
163+ for _ , p := range policies {
164+ if * p .PolicyARN == * policy .PolicyARN {
165+ return true
166+ }
167+ }
168+ return false
169+ }
170+
171+ // equalAccessScopes returns true if the supplied AccessScope objects are
172+ // exactly the same.
173+ func equalAccessScopes (a , b * v1alpha1.AccessScope ) bool {
174+ if a == nil && b == nil {
175+ return true
176+ }
177+ if a == nil {
178+ return equalZeroString (b .Type ) && len (b .Namespaces ) == 0
179+ }
180+ if b == nil {
181+ return equalZeroString (b .Type ) && len (a .Namespaces ) == 0
182+ }
183+ return equalStrings (a .Type , b .Type ) && ackcompare .SliceStringPEqual (a .Namespaces , b .Namespaces )
184+ }
185+
186+ // exactMatchInAccessPolicies returns true if the supplied AccessPolicy is in the
187+ // slice of AccessPolicy objects and the AccessScope is exactly the same.
188+ func exactMatchInAccessPolicies (policy * v1alpha1.AssociateAccessPolicyInput , policies []* v1alpha1.AssociateAccessPolicyInput ) bool {
189+ for _ , p := range policies {
190+ if p .PolicyARN == nil {
191+ continue
192+ }
193+ if * p .PolicyARN == * policy .PolicyARN {
194+ return equalAccessScopes (p .AccessScope , policy .AccessScope )
195+ }
196+ }
197+ return false
198+ }
199+
200+ // computeAccessPoliciesDelta returns two slices of AccessPolicy objects: one
201+ // slice of AccessPolicy objects that are in the desired slice but not in the
202+ // latest slice, and one slice of AccessPolicy objects that are in the latest
203+ // slice but not in the desired slice.
204+ func computeAccessPoliciesDelta (desired , latest []* v1alpha1.AssociateAccessPolicyInput ) (toAdd []* v1alpha1.AssociateAccessPolicyInput , toDelete []* string ) {
205+ // useful for the toDelete elements
206+ visited := map [string ]bool {}
207+
208+ // First we need to loop through the desired policies and see if they are
209+ // in the latest policies. If they are, we need to check if the AccessScope
210+ // is the same. If it is, we don't need to do anything. If it's not, we need
211+ // to add the policy to the toAdd slice and remove it from the toDelete slice.
212+ //
213+ // The delete is necessary because the API dosen't allow us to update the
214+ // AccessScope of an existing policy. We need to disassociate the policy and
215+ // then reassociate it.
216+ for _ , p := range desired {
217+ visited [* p .PolicyARN ] = true
218+ // If it's an exact match, we don't need to do anything.
219+ if exactMatchInAccessPolicies (p , latest ) {
220+ continue
221+ }
222+ // If it's in the latest policies, but the AccessScope is different, we
223+ // need to remove it from the toDelete slice (update).
224+ if inAccessPolicies (p , latest ) {
225+ toAdd = append (toAdd , p )
226+ toDelete = append (toDelete , p .PolicyARN )
227+ } else {
228+ // If it's not in the latest policies, we need to add it.
229+ toAdd = append (toAdd , p )
230+ }
231+ }
232+
233+ // Now that we've handled the desired policies, we need to loop through the
234+ // latest policies and see if they are in the desired policies. If they are
235+ // not, we need to add them to the toDelete slice.
236+ for _ , p := range latest {
237+ // If we've already visited this policy, we don't need to do anything.
238+ if visited [* p .PolicyARN ] {
239+ continue
240+ }
241+ // If it's not in the desired policies, we need to remove it.
242+ if ! inAccessPolicies (p , desired ) {
243+ toDelete = append (toDelete , p .PolicyARN )
244+ }
245+ }
246+ return toAdd , toDelete
247+ }
248+
249+ func customPreCompare (delta * ackcompare.Delta , a , b * resource ) {
250+ if len (a .ko .Spec .AccessPolicies ) != len (b .ko .Spec .AccessPolicies ) {
251+ delta .Add ("Spec.AccessPolicies" , a .ko .Spec .AccessPolicies , b .ko .Spec .AccessPolicies )
252+ } else if toAdd , toRemove := computeAccessPoliciesDelta (a .ko .Spec .AccessPolicies , b .ko .Spec .AccessPolicies ); len (toAdd ) > 0 || len (toRemove ) > 0 {
253+ delta .Add ("Spec.AccessPolicies" , a .ko .Spec .AccessPolicies , b .ko .Spec .AccessPolicies )
254+ }
255+ }
256+
257+ // EqualStrings returns true if two strings are equal e.g., both are nil, one is
258+ // nil and the other is empty string, or both non-zero strings are equal.
259+ func equalStrings (a , b * string ) bool {
260+ if a == nil {
261+ return b == nil || * b == ""
262+ }
263+ if b == nil {
264+ return * a == ""
265+ }
266+ return * a == * b
267+ }
268+
269+ func equalZeroString (a * string ) bool {
270+ return equalStrings (a , aws .String ("" ))
271+ }
0 commit comments