9
9
"github.com/go-logr/logr"
10
10
"github.com/pkg/errors"
11
11
"github.com/vmware/alb-sdk/go/models"
12
+ "golang.org/x/mod/semver"
12
13
corev1 "k8s.io/api/core/v1"
13
14
apierrors "k8s.io/apimachinery/pkg/api/errors"
14
15
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -241,7 +242,7 @@ func (r *AkoUserReconciler) reconcileAviUserNormal(
241
242
aviPassword := string (mcSecret .Data ["password" ][:])
242
243
243
244
// ensures the AVI User exists and matches the mc secret
244
- if _ , err = r .createOrUpdateAviUser (aviUsername , aviPassword , obj .Spec .Tenant .Name ); err != nil {
245
+ if err = r .createOrUpdateAviUser (log , aviUsername , aviPassword , obj .Spec .Tenant .Name ); err != nil {
245
246
log .Error (err , "Failed to create/update cluster avi user" )
246
247
return res , err
247
248
} else {
@@ -263,21 +264,31 @@ func (r *AkoUserReconciler) getAVIControllerCA(ctx context.Context, obj *akoov1a
263
264
}
264
265
265
266
// createOrUpdateAviUser create an avi user in avi controller
266
- func (r * AkoUserReconciler ) createOrUpdateAviUser (aviUsername , aviPassword , tenantName string ) (* models.User , error ) {
267
+ func (r * AkoUserReconciler ) createOrUpdateAviUser (log logr.Logger , aviUsername , aviPassword , tenantName string ) error {
268
+ version , err := r .aviClient .GetControllerVersion ()
269
+ if err != nil {
270
+ return err
271
+ }
272
+ // Add v prefix if not present so semver can parse it
273
+ if len (version ) > 0 && version [0 ] != 'v' {
274
+ version = "v" + version
275
+ }
276
+
267
277
aviUser , err := r .aviClient .UserGetByName (aviUsername )
268
278
// user not found, create one
269
279
if aviclient .IsAviUserNonExistentError (err ) {
280
+ log .Info ("AVI User not found, creating a new user" , "user" , aviUsername )
270
281
// for avi essential version the default tenant is admin
271
282
if tenantName == "" {
272
283
tenantName = "admin"
273
284
}
274
285
tenant , err := r .aviClient .TenantGet (tenantName )
275
286
if err != nil {
276
- return nil , err
287
+ return err
277
288
}
278
- role , err := r .getOrCreateAkoUserRole (tenant .URL )
289
+ role , err := r .getOrCreateAkoUserRole (log , tenant .URL , version )
279
290
if err != nil {
280
- return nil , err
291
+ return err
281
292
}
282
293
aviUser = & models.User {
283
294
Name : & aviUsername ,
@@ -291,50 +302,67 @@ func (r *AkoUserReconciler) createOrUpdateAviUser(aviUsername, aviPassword, tena
291
302
},
292
303
},
293
304
}
294
- return r .aviClient .UserCreate (aviUser )
305
+ // since v30.0.0, there is only enterprise edition
306
+ if semver .Compare (version , akoov1alpha1 .AVIControllerEnterpriseOnlyVersion ) >= 0 {
307
+ aviUser .Username = & aviUsername
308
+ }
309
+ if _ , err := r .aviClient .UserCreate (aviUser ); err != nil {
310
+ return err
311
+ }
312
+ return nil
313
+ } else if err != nil {
314
+ log .Info ("Failed to get AVI User" , "user" , aviUsername , "error" , err )
315
+ return err
295
316
}
296
317
297
- if err == nil {
298
- // ensure user's role align with latest essential permission when user found
299
- if _ , err := r .ensureAkoUserRole (); err != nil {
300
- return nil , err
301
- }
302
- // Update the password when user found, this is needed when the AVI user was
303
- // created before the mc Secret. And this operation will sync
304
- // the User's password to be the same as mc Secret's
318
+ // ensure user's role align with latest essential permission when user found
319
+ if _ , err := r .ensureAkoUserRole (log , version ); err != nil {
320
+ return err
321
+ }
322
+ // Update the password when user found, this is needed when the AVI user was
323
+ // created before the mc Secret. And this operation will sync
324
+ // the User's password to be the same as mc Secret's
325
+ if aviUser .Password == nil || * aviUser .Password != aviPassword {
326
+ log .Info ("AVI User found, updating the password" )
305
327
aviUser .Password = & aviPassword
306
- return r .aviClient .UserUpdate (aviUser )
328
+ if _ , err := r .aviClient .UserUpdate (aviUser ); err != nil {
329
+ return err
330
+ }
307
331
}
308
- return nil , err
332
+ return nil
309
333
}
310
334
311
335
// getOrCreateAkoUserRole get ako user's role, create one if not exist
312
- func (r * AkoUserReconciler ) getOrCreateAkoUserRole (roleTenantRef * string ) (* models.Role , error ) {
336
+ func (r * AkoUserReconciler ) getOrCreateAkoUserRole (log logr.Logger , roleTenantRef * string , version string ) (* models.Role , error ) {
337
+ log .Info ("Ensure AKO User Role" )
313
338
role , err := r .aviClient .RoleGetByName (akoov1alpha1 .AkoUserRoleName )
314
339
// not found ako user role, create one
315
340
if aviclient .IsAviRoleNonExistentError (err ) {
341
+ log .V (3 ).Info ("Creating AKO User Role since it's not found" , "role" , akoov1alpha1 .AkoUserRoleName )
342
+ log .Info ("current avi version" , "version" , version )
316
343
role = & models.Role {
317
344
Name : ptr .To (akoov1alpha1 .AkoUserRoleName ),
318
- Privileges : AkoRolePermission ,
345
+ Privileges : filterAkoRolePermissionByVersion ( log , AkoRolePermission , version ) ,
319
346
TenantRef : roleTenantRef ,
320
347
}
321
348
return r .aviClient .RoleCreate (role )
322
349
}
323
350
if err == nil {
324
- return r .ensureAkoUserRole ()
351
+ return r .ensureAkoUserRole (log , version )
325
352
}
326
353
return role , err
327
354
}
328
355
329
356
// ensureAkoUserRole ensure ako-essential-role has the latest permission
330
- func (r * AkoUserReconciler ) ensureAkoUserRole () (* models.Role , error ) {
357
+ func (r * AkoUserReconciler ) ensureAkoUserRole (log logr. Logger , version string ) (* models.Role , error ) {
331
358
role , err := r .aviClient .RoleGetByName (akoov1alpha1 .AkoUserRoleName )
332
359
if err != nil {
333
360
return role , err
334
361
}
335
362
336
363
// check if role needs to be synced
337
- if syncAkoUserRole (role ) {
364
+ if syncAkoUserRole (role , version ) {
365
+ log .Info ("Syncing AKO User Role with expected permissions" )
338
366
return r .aviClient .RoleUpdate (role )
339
367
}
340
368
@@ -346,15 +374,18 @@ func (r *AkoUserReconciler) ensureAkoUserRole() (*models.Role, error) {
346
374
// Any additional permissions on the Role that are not part of
347
375
// the desired AKO role are left as-is. It returns a bool
348
376
// indicating whether the Role was changed.
349
- func syncAkoUserRole (role * models.Role ) bool {
377
+ // It also filters out permissions that are deprecated in the current AVI version.
378
+ func syncAkoUserRole (role * models.Role , version string ) bool {
350
379
existingResources := sets .New [string ]()
351
380
updated := false
352
381
353
382
for i , permission := range role .Privileges {
354
383
desiredType , ok := AkoRolePermissionMap [* permission .Resource ]
355
384
if ! ok {
356
- // Existing AVI role has a permission that's not part of
357
- // the desired AKO role: leave it as-is.
385
+ // Existing AVI role in AVI Controller has a permission that's not part of
386
+ // the desired AKO role defined in AkoRolePermissionMap: leave it as-is.
387
+ // Since those could come from a new AVI Controller version that AKO-Operator
388
+ // Might not be aware of.
358
389
continue
359
390
}
360
391
@@ -370,6 +401,11 @@ func syncAkoUserRole(role *models.Role) bool {
370
401
371
402
for resource , desiredType := range AkoRolePermissionMap {
372
403
if ! existingResources .Has (resource ) {
404
+ // Filter out permissions that are deprecated in the current AVI version.
405
+ if deprecateVersion , ok := deprecatePermissionMap [resource ]; ok && semver .Compare (version , deprecateVersion ) >= 0 {
406
+ // Skip adding deprecated permissions to the role
407
+ continue
408
+ }
373
409
// Existing AVI role is missing a permission that's
374
410
// part of the desired AKO role: add it.
375
411
role .Privileges = append (role .Privileges , & models.Permission {
0 commit comments