@@ -63,32 +63,33 @@ var (
6363type Operator struct {
6464 queueinformer.Operator
6565
66- clock utilclock.Clock
67- logger * logrus.Logger
68- opClient operatorclient.ClientInterface
69- client versioned.Interface
70- lister operatorlister.OperatorLister
71- copiedCSVLister operatorsv1alpha1listers.ClusterServiceVersionLister
72- ogQueueSet * queueinformer.ResourceQueueSet
73- csvQueueSet * queueinformer.ResourceQueueSet
74- olmConfigQueue workqueue.RateLimitingInterface
75- csvCopyQueueSet * queueinformer.ResourceQueueSet
76- copiedCSVGCQueueSet * queueinformer.ResourceQueueSet
77- objGCQueueSet * queueinformer.ResourceQueueSet
78- nsQueueSet workqueue.RateLimitingInterface
79- apiServiceQueue workqueue.RateLimitingInterface
80- csvIndexers map [string ]cache.Indexer
81- recorder record.EventRecorder
82- resolver install.StrategyResolverInterface
83- apiReconciler APIIntersectionReconciler
84- apiLabeler labeler.Labeler
85- csvSetGenerator csvutility.SetGenerator
86- csvReplaceFinder csvutility.ReplaceFinder
87- csvNotification csvutility.WatchNotification
88- serviceAccountSyncer * scoped.UserDefinedServiceAccountSyncer
89- clientAttenuator * scoped.ClientAttenuator
90- serviceAccountQuerier * scoped.UserDefinedServiceAccountQuerier
91- clientFactory clients.Factory
66+ clock utilclock.Clock
67+ logger * logrus.Logger
68+ opClient operatorclient.ClientInterface
69+ client versioned.Interface
70+ lister operatorlister.OperatorLister
71+ protectedCopiedCSVNamespaces map [string ]struct {}
72+ copiedCSVLister operatorsv1alpha1listers.ClusterServiceVersionLister
73+ ogQueueSet * queueinformer.ResourceQueueSet
74+ csvQueueSet * queueinformer.ResourceQueueSet
75+ olmConfigQueue workqueue.RateLimitingInterface
76+ csvCopyQueueSet * queueinformer.ResourceQueueSet
77+ copiedCSVGCQueueSet * queueinformer.ResourceQueueSet
78+ objGCQueueSet * queueinformer.ResourceQueueSet
79+ nsQueueSet workqueue.RateLimitingInterface
80+ apiServiceQueue workqueue.RateLimitingInterface
81+ csvIndexers map [string ]cache.Indexer
82+ recorder record.EventRecorder
83+ resolver install.StrategyResolverInterface
84+ apiReconciler APIIntersectionReconciler
85+ apiLabeler labeler.Labeler
86+ csvSetGenerator csvutility.SetGenerator
87+ csvReplaceFinder csvutility.ReplaceFinder
88+ csvNotification csvutility.WatchNotification
89+ serviceAccountSyncer * scoped.UserDefinedServiceAccountSyncer
90+ clientAttenuator * scoped.ClientAttenuator
91+ serviceAccountQuerier * scoped.UserDefinedServiceAccountQuerier
92+ clientFactory clients.Factory
9293}
9394
9495func NewOperator (ctx context.Context , options ... OperatorOption ) (* Operator , error ) {
@@ -121,30 +122,31 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
121122 }
122123
123124 op := & Operator {
124- Operator : queueOperator ,
125- clock : config .clock ,
126- logger : config .logger ,
127- opClient : config .operatorClient ,
128- client : config .externalClient ,
129- ogQueueSet : queueinformer .NewEmptyResourceQueueSet (),
130- csvQueueSet : queueinformer .NewEmptyResourceQueueSet (),
131- olmConfigQueue : workqueue .NewNamedRateLimitingQueue (workqueue .DefaultControllerRateLimiter (), "olmConfig" ),
132- csvCopyQueueSet : queueinformer .NewEmptyResourceQueueSet (),
133- copiedCSVGCQueueSet : queueinformer .NewEmptyResourceQueueSet (),
134- objGCQueueSet : queueinformer .NewEmptyResourceQueueSet (),
135- apiServiceQueue : workqueue .NewNamedRateLimitingQueue (workqueue .DefaultControllerRateLimiter (), "apiservice" ),
136- resolver : config .strategyResolver ,
137- apiReconciler : config .apiReconciler ,
138- lister : lister ,
139- recorder : eventRecorder ,
140- apiLabeler : config .apiLabeler ,
141- csvIndexers : map [string ]cache.Indexer {},
142- csvSetGenerator : csvutility .NewSetGenerator (config .logger , lister ),
143- csvReplaceFinder : csvutility .NewReplaceFinder (config .logger , config .externalClient ),
144- serviceAccountSyncer : scoped .NewUserDefinedServiceAccountSyncer (config .logger , scheme , config .operatorClient , config .externalClient ),
145- clientAttenuator : scoped .NewClientAttenuator (config .logger , config .restConfig , config .operatorClient ),
146- serviceAccountQuerier : scoped .NewUserDefinedServiceAccountQuerier (config .logger , config .externalClient ),
147- clientFactory : clients .NewFactory (config .restConfig ),
125+ Operator : queueOperator ,
126+ clock : config .clock ,
127+ logger : config .logger ,
128+ opClient : config .operatorClient ,
129+ client : config .externalClient ,
130+ ogQueueSet : queueinformer .NewEmptyResourceQueueSet (),
131+ csvQueueSet : queueinformer .NewEmptyResourceQueueSet (),
132+ olmConfigQueue : workqueue .NewNamedRateLimitingQueue (workqueue .DefaultControllerRateLimiter (), "olmConfig" ),
133+ csvCopyQueueSet : queueinformer .NewEmptyResourceQueueSet (),
134+ copiedCSVGCQueueSet : queueinformer .NewEmptyResourceQueueSet (),
135+ objGCQueueSet : queueinformer .NewEmptyResourceQueueSet (),
136+ apiServiceQueue : workqueue .NewNamedRateLimitingQueue (workqueue .DefaultControllerRateLimiter (), "apiservice" ),
137+ resolver : config .strategyResolver ,
138+ apiReconciler : config .apiReconciler ,
139+ lister : lister ,
140+ recorder : eventRecorder ,
141+ apiLabeler : config .apiLabeler ,
142+ csvIndexers : map [string ]cache.Indexer {},
143+ csvSetGenerator : csvutility .NewSetGenerator (config .logger , lister ),
144+ csvReplaceFinder : csvutility .NewReplaceFinder (config .logger , config .externalClient ),
145+ serviceAccountSyncer : scoped .NewUserDefinedServiceAccountSyncer (config .logger , scheme , config .operatorClient , config .externalClient ),
146+ clientAttenuator : scoped .NewClientAttenuator (config .logger , config .restConfig , config .operatorClient ),
147+ serviceAccountQuerier : scoped .NewUserDefinedServiceAccountQuerier (config .logger , config .externalClient ),
148+ clientFactory : clients .NewFactory (config .restConfig ),
149+ protectedCopiedCSVNamespaces : config .protectedCopiedCSVNamespaces ,
148150 }
149151
150152 // Set up syncing for namespace-scoped resources
@@ -1299,20 +1301,31 @@ func (a *Operator) syncOLMConfig(obj interface{}) (syncError error) {
12991301 return err
13001302 }
13011303
1302- // Filter to unique copies
1303- uniqueCopiedCSVs := map [string ]struct {}{}
1304+ // Create a map that points from CSV name to a map of namespaces it is copied to
1305+ // for quick lookups.
1306+ copiedCSVNamespaces := map [string ]map [string ]struct {}{}
13041307 for _ , copiedCSV := range copiedCSVs {
1305- uniqueCopiedCSVs [copiedCSV .GetName ()] = struct {}{}
1308+ if _ , ok := copiedCSVNamespaces [copiedCSV .GetName ()]; ! ok {
1309+ copiedCSVNamespaces [copiedCSV .GetName ()] = map [string ]struct {}{}
1310+ }
1311+ copiedCSVNamespaces [copiedCSV .GetName ()][copiedCSV .GetNamespace ()] = struct {}{}
13061312 }
13071313
13081314 csvs , err := a .lister .OperatorsV1alpha1 ().ClusterServiceVersionLister ().ClusterServiceVersions (og .GetNamespace ()).List (labels .NewSelector ().Add (* nonCopiedCSVRequirement ))
13091315 if err != nil {
13101316 return err
13111317 }
13121318
1319+ namespaces , err := a .lister .CoreV1 ().NamespaceLister ().List (labels .Everything ())
1320+ if err != nil {
1321+ return err
1322+ }
1323+
1324+ copiedCSVEvaluatorFunc := getCopiedCSVEvaluatorFunc (olmConfig .CopiedCSVsAreEnabled (), namespaces , a .protectedCopiedCSVNamespaces )
1325+
13131326 for _ , csv := range csvs {
1314- // If the correct number of copied CSVs were found, continue
1315- if _ , ok := uniqueCopiedCSVs [csv .GetName ()]; ok == olmConfig . CopiedCSVsAreEnabled ( ) {
1327+ // Ignore NS where actual CSV is installed
1328+ if copiedCSVEvaluatorFunc ( copiedCSVNamespaces [csv .GetName ()]) {
13161329 continue
13171330 }
13181331
@@ -1324,7 +1337,7 @@ func (a *Operator) syncOLMConfig(obj interface{}) (syncError error) {
13241337 }
13251338
13261339 // Update the olmConfig status if it has changed.
1327- condition := getCopiedCSVsCondition (! olmConfig .CopiedCSVsAreEnabled (), csvIsRequeued )
1340+ condition := getCopiedCSVsCondition (olmConfig .CopiedCSVsAreEnabled (), csvIsRequeued )
13281341 if ! isStatusConditionPresentAndAreTypeReasonMessageStatusEqual (olmConfig .Status .Conditions , condition ) {
13291342 meta .SetStatusCondition (& olmConfig .Status .Conditions , condition )
13301343 if _ , err := a .client .OperatorsV1 ().OLMConfigs ().UpdateStatus (context .TODO (), olmConfig , metav1.UpdateOptions {}); err != nil {
@@ -1335,6 +1348,37 @@ func (a *Operator) syncOLMConfig(obj interface{}) (syncError error) {
13351348 return nil
13361349}
13371350
1351+ // getCopiedCSVEvaluatorFunc returns a function that evaluates if the a set of Copied CSVs exist in the expected namespaces.
1352+ func getCopiedCSVEvaluatorFunc (copiedCSVsEnabled bool , namespaces []* corev1.Namespace , protectedCopiedCSVNamespaces map [string ]struct {}) func (map [string ]struct {}) bool {
1353+ if copiedCSVsEnabled {
1354+ // Exclude the namespace hosting the original CSV
1355+ expectedCopiedCSVCount := - 1
1356+ for _ , ns := range namespaces {
1357+ if ns .Status .Phase == corev1 .NamespaceActive {
1358+ expectedCopiedCSVCount ++
1359+ }
1360+ }
1361+ return func (m map [string ]struct {}) bool {
1362+ return expectedCopiedCSVCount == len (m )
1363+ }
1364+ }
1365+
1366+ // Check that Copied CSVs exist in protected namespaces.
1367+ return func (m map [string ]struct {}) bool {
1368+ if len (protectedCopiedCSVNamespaces ) != len (m ) {
1369+ return false
1370+ }
1371+
1372+ for protectedNS := range protectedCopiedCSVNamespaces {
1373+ if _ , ok := m [protectedNS ]; ! ok {
1374+ return false
1375+ }
1376+ }
1377+
1378+ return true
1379+ }
1380+ }
1381+
13381382func isStatusConditionPresentAndAreTypeReasonMessageStatusEqual (conditions []metav1.Condition , condition metav1.Condition ) bool {
13391383 foundCondition := meta .FindStatusCondition (conditions , condition .Type )
13401384 if foundCondition == nil {
@@ -1346,13 +1390,13 @@ func isStatusConditionPresentAndAreTypeReasonMessageStatusEqual(conditions []met
13461390 foundCondition .Status == condition .Status
13471391}
13481392
1349- func getCopiedCSVsCondition (isDisabled , csvIsRequeued bool ) metav1.Condition {
1393+ func getCopiedCSVsCondition (enabled , csvIsRequeued bool ) metav1.Condition {
13501394 condition := metav1.Condition {
13511395 Type : operatorsv1 .DisabledCopiedCSVsConditionType ,
13521396 LastTransitionTime : metav1 .Now (),
13531397 Status : metav1 .ConditionFalse ,
13541398 }
1355- if ! isDisabled {
1399+ if enabled {
13561400 condition .Reason = "CopiedCSVsEnabled"
13571401 condition .Message = "Copied CSVs are enabled and present across the cluster"
13581402 if csvIsRequeued {
@@ -1361,15 +1405,14 @@ func getCopiedCSVsCondition(isDisabled, csvIsRequeued bool) metav1.Condition {
13611405 return condition
13621406 }
13631407
1408+ condition .Reason = "CopiedCSVsDisabled"
13641409 if csvIsRequeued {
1365- condition .Reason = "CopiedCSVsFound"
1366- condition .Message = "Copied CSVs are disabled and at least one copied CSV was found for an operator installed in AllNamespace mode"
1410+ condition .Message = "Copied CSVs are disabled and at least one unexpected copied CSV was found for an operator installed in AllNamespace mode"
13671411 return condition
13681412 }
13691413
13701414 condition .Status = metav1 .ConditionTrue
1371- condition .Reason = "NoCopiedCSVsFound"
1372- condition .Message = "Copied CSVs are disabled and none were found for operators installed in AllNamespace mode"
1415+ condition .Message = "Copied CSVs are disabled and no unexpected copied CSVs were found for operators installed in AllNamespace mode"
13731416
13741417 return condition
13751418}
@@ -1444,7 +1487,24 @@ func (a *Operator) syncCopyCSV(obj interface{}) (syncError error) {
14441487 return err
14451488 }
14461489
1490+ // Ensure that the Copied CSVs exist in the protected namespaces.
1491+ protectedNamespaces := []string {}
1492+ for ns := range a .protectedCopiedCSVNamespaces {
1493+ if ns == clusterServiceVersion .GetNamespace () {
1494+ continue
1495+ }
1496+ protectedNamespaces = append (protectedNamespaces , ns )
1497+ }
1498+
1499+ if err := a .ensureCSVsInNamespaces (clusterServiceVersion , operatorGroup , NewNamespaceSet (protectedNamespaces )); err != nil {
1500+ return err
1501+ }
1502+
1503+ // Delete Copied CSVs in namespaces that are not protected.
14471504 for _ , copiedCSV := range copiedCSVs {
1505+ if _ , ok := a .protectedCopiedCSVNamespaces [copiedCSV .Namespace ]; ok {
1506+ continue
1507+ }
14481508 err := a .client .OperatorsV1alpha1 ().ClusterServiceVersions (copiedCSV .Namespace ).Delete (context .TODO (), copiedCSV .Name , metav1.DeleteOptions {})
14491509 if err != nil && ! apierrors .IsNotFound (err ) {
14501510 return err
0 commit comments