@@ -14,9 +14,11 @@ import (
14
14
rbacv1 "k8s.io/api/rbac/v1"
15
15
extinf "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions"
16
16
k8serrors "k8s.io/apimachinery/pkg/api/errors"
17
+ "k8s.io/apimachinery/pkg/api/meta"
17
18
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
19
"k8s.io/apimachinery/pkg/labels"
19
20
"k8s.io/apimachinery/pkg/runtime"
21
+ "k8s.io/apimachinery/pkg/selection"
20
22
utilclock "k8s.io/apimachinery/pkg/util/clock"
21
23
utilerrors "k8s.io/apimachinery/pkg/util/errors"
22
24
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -68,6 +70,7 @@ type Operator struct {
68
70
copiedCSVLister operatorsv1alpha1listers.ClusterServiceVersionLister
69
71
ogQueueSet * queueinformer.ResourceQueueSet
70
72
csvQueueSet * queueinformer.ResourceQueueSet
73
+ olmConfigQueue workqueue.RateLimitingInterface
71
74
csvCopyQueueSet * queueinformer.ResourceQueueSet
72
75
copiedCSVGCQueueSet * queueinformer.ResourceQueueSet
73
76
objGCQueueSet * queueinformer.ResourceQueueSet
@@ -124,6 +127,7 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
124
127
client : config .externalClient ,
125
128
ogQueueSet : queueinformer .NewEmptyResourceQueueSet (),
126
129
csvQueueSet : queueinformer .NewEmptyResourceQueueSet (),
130
+ olmConfigQueue : workqueue .NewNamedRateLimitingQueue (workqueue .DefaultControllerRateLimiter (), "olmConfig" ),
127
131
csvCopyQueueSet : queueinformer .NewEmptyResourceQueueSet (),
128
132
copiedCSVGCQueueSet : queueinformer .NewEmptyResourceQueueSet (),
129
133
objGCQueueSet : queueinformer .NewEmptyResourceQueueSet (),
@@ -433,6 +437,26 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
433
437
return nil , err
434
438
}
435
439
440
+ // Register QueueInformer for olmConfig
441
+ olmConfigInformer := externalversions .NewSharedInformerFactoryWithOptions (
442
+ op .client ,
443
+ config .resyncPeriod (),
444
+ ).Operators ().V1 ().OLMConfigs ().Informer ()
445
+ olmConfigQueueInformer , err := queueinformer .NewQueueInformer (
446
+ ctx ,
447
+ queueinformer .WithInformer (olmConfigInformer ),
448
+ queueinformer .WithLogger (op .logger ),
449
+ queueinformer .WithQueue (op .olmConfigQueue ),
450
+ queueinformer .WithIndexer (olmConfigInformer .GetIndexer ()),
451
+ queueinformer .WithSyncer (queueinformer .LegacySyncHandler (op .syncOLMConfig ).ToSyncer ()),
452
+ )
453
+ if err != nil {
454
+ return nil , err
455
+ }
456
+ if err := op .RegisterQueueInformer (olmConfigQueueInformer ); err != nil {
457
+ return nil , err
458
+ }
459
+
436
460
k8sInformerFactory := informers .NewSharedInformerFactory (op .opClient .KubernetesInterface (), config .resyncPeriod ())
437
461
clusterRoleInformer := k8sInformerFactory .Rbac ().V1 ().ClusterRoles ()
438
462
op .lister .RbacV1 ().RegisterClusterRoleLister (clusterRoleInformer .Lister ())
@@ -1211,13 +1235,143 @@ func (a *Operator) syncClusterServiceVersion(obj interface{}) (syncError error)
1211
1235
return
1212
1236
}
1213
1237
1238
+ func (a * Operator ) allNamespaceOperatorGroups () ([]* v1.OperatorGroup , error ) {
1239
+ operatorGroups , err := a .lister .OperatorsV1 ().OperatorGroupLister ().List (labels .Everything ())
1240
+ if err != nil {
1241
+ return nil , err
1242
+ }
1243
+
1244
+ result := []* v1.OperatorGroup {}
1245
+ for _ , operatorGroup := range operatorGroups {
1246
+ if NewNamespaceSet (operatorGroup .Status .Namespaces ).IsAllNamespaces () {
1247
+ result = append (result , operatorGroup )
1248
+ }
1249
+ }
1250
+ return result , nil
1251
+ }
1252
+
1253
+ func (a * Operator ) syncOLMConfig (obj interface {}) (syncError error ) {
1254
+ a .logger .Info ("Processing olmConfig" )
1255
+ olmConfig , ok := obj .(* v1.OLMConfig )
1256
+ if ! ok {
1257
+ return fmt .Errorf ("casting OLMConfig failed" )
1258
+ }
1259
+
1260
+ // Generate an array of allNamespace OperatorGroups
1261
+ allNSOperatorGroups , err := a .allNamespaceOperatorGroups ()
1262
+ if err != nil {
1263
+ return err
1264
+ }
1265
+
1266
+ nonCopiedCSVRequirement , err := labels .NewRequirement (v1alpha1 .CopiedLabelKey , selection .DoesNotExist , []string {})
1267
+ if err != nil {
1268
+ return err
1269
+ }
1270
+
1271
+ csvIsRequeued := false
1272
+ for _ , og := range allNSOperatorGroups {
1273
+ // Get all copied CSVs owned by this operatorGroup
1274
+ copiedCSVRequirement , err := labels .NewRequirement (v1alpha1 .CopiedLabelKey , selection .Equals , []string {og .GetNamespace ()})
1275
+ if err != nil {
1276
+ return err
1277
+ }
1278
+
1279
+ copiedCSVs , err := a .copiedCSVLister .List (labels .NewSelector ().Add (* copiedCSVRequirement ))
1280
+ if err != nil {
1281
+ return err
1282
+ }
1283
+
1284
+ // Filter to unique copies
1285
+ uniqueCopiedCSVs := map [string ]struct {}{}
1286
+ for _ , copiedCSV := range copiedCSVs {
1287
+ uniqueCopiedCSVs [copiedCSV .GetName ()] = struct {}{}
1288
+ }
1289
+
1290
+ csvs , err := a .lister .OperatorsV1alpha1 ().ClusterServiceVersionLister ().ClusterServiceVersions (og .GetNamespace ()).List (labels .NewSelector ().Add (* nonCopiedCSVRequirement ))
1291
+ if err != nil {
1292
+ return err
1293
+ }
1294
+
1295
+ for _ , csv := range csvs {
1296
+ // If the correct number of copied CSVs were found, continue
1297
+ if _ , ok := uniqueCopiedCSVs [csv .GetName ()]; ok == olmConfig .CopiedCSVsAreEnabled () {
1298
+ continue
1299
+ }
1300
+
1301
+ if err := a .csvQueueSet .Requeue (csv .GetNamespace (), csv .GetName ()); err != nil {
1302
+ a .logger .WithError (err ).Warn ("unable to requeue" )
1303
+ }
1304
+ csvIsRequeued = true
1305
+ }
1306
+ }
1307
+
1308
+ // Update the olmConfig status if it has changed.
1309
+ condition := getCopiedCSVsCondition (! olmConfig .CopiedCSVsAreEnabled (), csvIsRequeued )
1310
+ if ! isStatusConditionPresentAndAreTypeReasonMessageStatusEqual (olmConfig .Status .Conditions , condition ) {
1311
+ meta .SetStatusCondition (& olmConfig .Status .Conditions , condition )
1312
+ if _ , err := a .client .OperatorsV1 ().OLMConfigs ().UpdateStatus (context .TODO (), olmConfig , metav1.UpdateOptions {}); err != nil {
1313
+ return err
1314
+ }
1315
+ }
1316
+
1317
+ return nil
1318
+ }
1319
+
1320
+ func isStatusConditionPresentAndAreTypeReasonMessageStatusEqual (conditions []metav1.Condition , condition metav1.Condition ) bool {
1321
+ foundCondition := meta .FindStatusCondition (conditions , condition .Type )
1322
+ if foundCondition == nil {
1323
+ return false
1324
+ }
1325
+ return foundCondition .Type == condition .Type &&
1326
+ foundCondition .Reason == condition .Reason &&
1327
+ foundCondition .Message == condition .Message &&
1328
+ foundCondition .Status == condition .Status
1329
+ }
1330
+
1331
+ func getCopiedCSVsCondition (isDisabled , csvIsRequeued bool ) metav1.Condition {
1332
+ condition := metav1.Condition {
1333
+ Type : v1 .DisabledCopiedCSVsConditionType ,
1334
+ LastTransitionTime : metav1 .Now (),
1335
+ Status : metav1 .ConditionFalse ,
1336
+ }
1337
+ if ! isDisabled {
1338
+ condition .Reason = "CopiedCSVsEnabled"
1339
+ condition .Message = "Copied CSVs are enabled and present accross the cluster"
1340
+ if csvIsRequeued {
1341
+ condition .Message = "Copied CSVs are enabled and at least one copied CSVs is missing"
1342
+ }
1343
+ return condition
1344
+ }
1345
+
1346
+ if csvIsRequeued {
1347
+ condition .Reason = "CopiedCSVsFound"
1348
+ condition .Message = "Copied CSVs are disabled and at least one copied CSV was found for an operator installed in AllNamespace mode"
1349
+ return condition
1350
+ }
1351
+
1352
+ condition .Status = metav1 .ConditionTrue
1353
+ condition .Reason = "NoCopiedCSVsFound"
1354
+ condition .Message = "Copied CSVs are disabled and none were found for operators installed in AllNamespace mode"
1355
+
1356
+ return condition
1357
+ }
1358
+
1214
1359
func (a * Operator ) syncCopyCSV (obj interface {}) (syncError error ) {
1215
1360
clusterServiceVersion , ok := obj .(* v1alpha1.ClusterServiceVersion )
1216
1361
if ! ok {
1217
1362
a .logger .Debugf ("wrong type: %#v" , obj )
1218
1363
return fmt .Errorf ("casting ClusterServiceVersion failed" )
1219
1364
}
1220
1365
1366
+ olmConfig , err := a .client .OperatorsV1 ().OLMConfigs ().Get (context .TODO (), "cluster" , metav1.GetOptions {})
1367
+ if err != nil && ! k8serrors .IsNotFound (err ) {
1368
+ return err
1369
+ }
1370
+
1371
+ if err == nil {
1372
+ go a .olmConfigQueue .AddAfter (olmConfig , time .Second * 5 )
1373
+ }
1374
+
1221
1375
logger := a .logger .WithFields (logrus.Fields {
1222
1376
"id" : queueinformer .NewLoopID (),
1223
1377
"csv" : clusterServiceVersion .GetName (),
@@ -1239,15 +1393,145 @@ func (a *Operator) syncCopyCSV(obj interface{}) (syncError error) {
1239
1393
"targetNamespaces" : strings .Join (operatorGroup .Status .Namespaces , "," ),
1240
1394
}).Debug ("copying csv to targets" )
1241
1395
1396
+ copiedCSVsAreEnabled , err := a .copiedCSVsAreEnabled ()
1397
+ if err != nil {
1398
+ return err
1399
+ }
1400
+
1242
1401
// Check if we need to do any copying / annotation for the operatorgroup
1243
- if err := a .ensureCSVsInNamespaces (clusterServiceVersion , operatorGroup , NewNamespaceSet (operatorGroup .Status .Namespaces )); err != nil {
1244
- logger .WithError (err ).Info ("couldn't copy CSV to target namespaces" )
1245
- syncError = err
1402
+ namespaceSet := NewNamespaceSet (operatorGroup .Status .Namespaces )
1403
+ if copiedCSVsAreEnabled || ! namespaceSet .IsAllNamespaces () {
1404
+ if err := a .ensureCSVsInNamespaces (clusterServiceVersion , operatorGroup , namespaceSet ); err != nil {
1405
+ logger .WithError (err ).Info ("couldn't copy CSV to target namespaces" )
1406
+ syncError = err
1407
+ }
1408
+
1409
+ // If the CSV was installed in AllNamespace mode, remove any "CSV Copying Disabled" events
1410
+ // in which the related object's name, namespace, and uid match the given CSV's.
1411
+ if namespaceSet .IsAllNamespaces () {
1412
+ if err := a .deleteCSVCopyingDisabledEvent (clusterServiceVersion ); err != nil {
1413
+ return err
1414
+ }
1415
+ }
1416
+ return
1417
+ }
1418
+
1419
+ requirement , err := labels .NewRequirement (v1alpha1 .CopiedLabelKey , selection .Equals , []string {clusterServiceVersion .Namespace })
1420
+ if err != nil {
1421
+ return err
1422
+ }
1423
+
1424
+ copiedCSVs , err := a .copiedCSVLister .List (labels .NewSelector ().Add (* requirement ))
1425
+ if err != nil {
1426
+ return err
1427
+ }
1428
+
1429
+ for _ , copiedCSV := range copiedCSVs {
1430
+ err := a .client .OperatorsV1alpha1 ().ClusterServiceVersions (copiedCSV .Namespace ).Delete (context .TODO (), copiedCSV .Name , metav1.DeleteOptions {})
1431
+ if err != nil && ! k8serrors .IsNotFound (err ) {
1432
+ return err
1433
+ }
1434
+ }
1435
+
1436
+ if err := a .createCSVCopyingDisabledEvent (clusterServiceVersion ); err != nil {
1437
+ return err
1246
1438
}
1247
1439
1248
1440
return
1249
1441
}
1250
1442
1443
+ // copiedCSVsAreEnabled determines if csv copying is enabled for OLM.
1444
+ //
1445
+ // This method will first attempt to get the "cluster" olmConfig resource,
1446
+ // if any error other than "IsNotFound" is encountered, false and the error
1447
+ // will be returned.
1448
+ //
1449
+ // If the "cluster" olmConfig resource is found, the value of
1450
+ // olmConfig.spec.features.disableCopiedCSVs will be returned along with a
1451
+ // nil error.
1452
+ //
1453
+ // If the "cluster" olmConfig resource is not found, true will be returned
1454
+ // without an error.
1455
+ func (a * Operator ) copiedCSVsAreEnabled () (bool , error ) {
1456
+ olmConfig , err := a .client .OperatorsV1 ().OLMConfigs ().Get (context .TODO (), "cluster" , metav1.GetOptions {})
1457
+ if err != nil {
1458
+ // Default to true if olmConfig singleton cannot be found
1459
+ if k8serrors .IsNotFound (err ) {
1460
+ return true , nil
1461
+ }
1462
+ // If there was an error that wasn't an IsNotFound, return the error
1463
+ return false , err
1464
+ }
1465
+
1466
+ // If there was no error, return value based on olmConfig singleton
1467
+ return olmConfig .CopiedCSVsAreEnabled (), nil
1468
+ }
1469
+
1470
+ func (a * Operator ) getCopiedCSVDisabledEventsForCSV (csv * v1alpha1.ClusterServiceVersion ) ([]corev1.Event , error ) {
1471
+ result := []corev1.Event {}
1472
+ if csv == nil {
1473
+ return result , nil
1474
+ }
1475
+
1476
+ events , err := a .opClient .KubernetesInterface ().CoreV1 ().Events (csv .GetNamespace ()).List (context .TODO (), metav1.ListOptions {})
1477
+ if err != nil {
1478
+ return nil , err
1479
+ }
1480
+
1481
+ for _ , event := range events .Items {
1482
+ if event .InvolvedObject .Namespace == csv .GetNamespace () &&
1483
+ event .InvolvedObject .Name == csv .GetName () &&
1484
+ event .InvolvedObject .UID == csv .GetUID () &&
1485
+ event .Reason == v1 .DisabledCopiedCSVsConditionType {
1486
+ result = append (result , event )
1487
+ }
1488
+ }
1489
+
1490
+ return result , nil
1491
+ }
1492
+
1493
+ func (a * Operator ) deleteCSVCopyingDisabledEvent (csv * v1alpha1.ClusterServiceVersion ) error {
1494
+ events , err := a .getCopiedCSVDisabledEventsForCSV (csv )
1495
+ if err != nil {
1496
+ return err
1497
+ }
1498
+
1499
+ // Remove existing events.
1500
+ return a .deleteEvents (events )
1501
+ }
1502
+
1503
+ func (a * Operator ) deleteEvents (events []corev1.Event ) error {
1504
+ for _ , event := range events {
1505
+ err := a .opClient .KubernetesInterface ().EventsV1 ().Events (event .GetNamespace ()).Delete (context .TODO (), event .GetName (), metav1.DeleteOptions {})
1506
+ if err != nil && ! k8serrors .IsNotFound (err ) {
1507
+ return err
1508
+ }
1509
+ }
1510
+ return nil
1511
+ }
1512
+
1513
+ func (a * Operator ) createCSVCopyingDisabledEvent (csv * v1alpha1.ClusterServiceVersion ) error {
1514
+ events , err := a .getCopiedCSVDisabledEventsForCSV (csv )
1515
+ if err != nil {
1516
+ return err
1517
+ }
1518
+
1519
+ if len (events ) == 1 {
1520
+ return nil
1521
+ }
1522
+
1523
+ // Remove existing events.
1524
+ if len (events ) > 1 {
1525
+ if err := a .deleteEvents (events ); err != nil {
1526
+ return err
1527
+ }
1528
+ }
1529
+
1530
+ a .recorder .Eventf (csv , corev1 .EventTypeWarning , v1 .DisabledCopiedCSVsConditionType , "CSV copying disabled for %s/%s" , csv .GetNamespace (), csv .GetName ())
1531
+
1532
+ return nil
1533
+ }
1534
+
1251
1535
func (a * Operator ) syncGcCsv (obj interface {}) (syncError error ) {
1252
1536
clusterServiceVersion , ok := obj .(* v1alpha1.ClusterServiceVersion )
1253
1537
if ! ok {
0 commit comments