github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/olm/operator.go (about) 1 package olm 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/labeller" 12 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins" 13 "github.com/sirupsen/logrus" 14 appsv1 "k8s.io/api/apps/v1" 15 corev1 "k8s.io/api/core/v1" 16 rbacv1 "k8s.io/api/rbac/v1" 17 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 18 apierrors "k8s.io/apimachinery/pkg/api/errors" 19 "k8s.io/apimachinery/pkg/api/meta" 20 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 "k8s.io/apimachinery/pkg/labels" 22 "k8s.io/apimachinery/pkg/runtime" 23 "k8s.io/apimachinery/pkg/runtime/schema" 24 "k8s.io/apimachinery/pkg/selection" 25 utilerrors "k8s.io/apimachinery/pkg/util/errors" 26 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 27 appsv1applyconfigurations "k8s.io/client-go/applyconfigurations/apps/v1" 28 rbacv1applyconfigurations "k8s.io/client-go/applyconfigurations/rbac/v1" 29 "k8s.io/client-go/informers" 30 k8sscheme "k8s.io/client-go/kubernetes/scheme" 31 "k8s.io/client-go/metadata/metadatainformer" 32 "k8s.io/client-go/metadata/metadatalister" 33 "k8s.io/client-go/tools/cache" 34 "k8s.io/client-go/tools/record" 35 "k8s.io/client-go/util/workqueue" 36 apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" 37 kagg "k8s.io/kube-aggregator/pkg/client/informers/externalversions" 38 utilclock "k8s.io/utils/clock" 39 "sigs.k8s.io/controller-runtime/pkg/client" 40 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 41 42 operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" 43 "github.com/operator-framework/api/pkg/operators/v1alpha1" 44 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" 45 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions" 46 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/certs" 47 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 48 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/overrides" 49 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver" 50 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clients" 51 csvutility "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv" 52 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/event" 53 index "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/index" 54 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/labeler" 55 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" 56 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister" 57 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 58 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/proxy" 59 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer" 60 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/scoped" 61 "github.com/operator-framework/operator-lifecycle-manager/pkg/metrics" 62 ) 63 64 var ( 65 ErrRequirementsNotMet = errors.New("requirements were not met") 66 ErrCRDOwnerConflict = errors.New("conflicting CRD owner in namespace") 67 ErrAPIServiceOwnerConflict = errors.New("unable to adopt APIService") 68 ) 69 70 // this unexported operator plugin slice provides an entrypoint for 71 // downstream to inject its own plugins to augment the controller behavior 72 var operatorPlugInFactoryFuncs []plugins.OperatorPlugInFactoryFunc 73 74 type Operator struct { 75 queueinformer.Operator 76 77 clock utilclock.Clock 78 logger *logrus.Logger 79 opClient operatorclient.ClientInterface 80 client versioned.Interface 81 lister operatorlister.OperatorLister 82 protectedCopiedCSVNamespaces map[string]struct{} 83 copiedCSVLister metadatalister.Lister 84 ogQueueSet *queueinformer.ResourceQueueSet 85 csvQueueSet *queueinformer.ResourceQueueSet 86 olmConfigQueue workqueue.TypedRateLimitingInterface[any] 87 csvCopyQueueSet *queueinformer.ResourceQueueSet 88 copiedCSVGCQueueSet *queueinformer.ResourceQueueSet 89 nsQueueSet workqueue.TypedRateLimitingInterface[any] 90 apiServiceQueue workqueue.TypedRateLimitingInterface[any] 91 csvIndexers map[string]cache.Indexer 92 recorder record.EventRecorder 93 resolver install.StrategyResolverInterface 94 apiReconciler APIIntersectionReconciler 95 apiLabeler labeler.Labeler 96 csvSetGenerator csvutility.SetGenerator 97 csvReplaceFinder csvutility.ReplaceFinder 98 csvNotification csvutility.WatchNotification 99 serviceAccountSyncer *scoped.UserDefinedServiceAccountSyncer 100 clientAttenuator *scoped.ClientAttenuator 101 serviceAccountQuerier *scoped.UserDefinedServiceAccountQuerier 102 clientFactory clients.Factory 103 plugins []plugins.OperatorPlugin 104 informersByNamespace map[string]*plugins.Informers 105 informersFiltered bool 106 107 ruleChecker func(*v1alpha1.ClusterServiceVersion) *install.CSVRuleChecker 108 ruleCheckerLock sync.RWMutex 109 resyncPeriod func() time.Duration 110 ctx context.Context 111 } 112 113 func (a *Operator) Informers() map[string]*plugins.Informers { 114 return a.informersByNamespace 115 } 116 117 func (a *Operator) getRuleChecker() func(*v1alpha1.ClusterServiceVersion) *install.CSVRuleChecker { 118 var ruleChecker func(*v1alpha1.ClusterServiceVersion) *install.CSVRuleChecker 119 a.ruleCheckerLock.RLock() 120 ruleChecker = a.ruleChecker 121 a.ruleCheckerLock.RUnlock() 122 if ruleChecker != nil { 123 return ruleChecker 124 } 125 126 a.ruleCheckerLock.Lock() 127 defer a.ruleCheckerLock.Unlock() 128 if a.ruleChecker != nil { 129 return a.ruleChecker 130 } 131 132 sif := informers.NewSharedInformerFactoryWithOptions(a.opClient.KubernetesInterface(), a.resyncPeriod()) 133 rolesLister := sif.Rbac().V1().Roles().Lister() 134 roleBindingsLister := sif.Rbac().V1().RoleBindings().Lister() 135 clusterRolesLister := sif.Rbac().V1().ClusterRoles().Lister() 136 clusterRoleBindingsLister := sif.Rbac().V1().ClusterRoleBindings().Lister() 137 138 sif.Start(a.ctx.Done()) 139 sif.WaitForCacheSync(a.ctx.Done()) 140 141 if a.ctx.Err() != nil { 142 a.ruleChecker = nil 143 return nil 144 } 145 146 a.ruleChecker = func(csv *v1alpha1.ClusterServiceVersion) *install.CSVRuleChecker { 147 return install.NewCSVRuleChecker( 148 rolesLister, roleBindingsLister, 149 clusterRolesLister, clusterRoleBindingsLister, 150 csv, 151 ) 152 } 153 return a.ruleChecker 154 } 155 156 func NewOperator(ctx context.Context, options ...OperatorOption) (*Operator, error) { 157 config := defaultOperatorConfig() 158 config.apply(options) 159 160 return newOperatorWithConfig(ctx, config) 161 } 162 163 func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operator, error) { 164 if err := config.validate(); err != nil { 165 return nil, err 166 } 167 168 queueOperator, err := queueinformer.NewOperator(config.operatorClient.KubernetesInterface().Discovery(), queueinformer.WithOperatorLogger(config.logger)) 169 if err != nil { 170 return nil, err 171 } 172 173 eventRecorder, err := event.NewRecorder(config.operatorClient.KubernetesInterface().CoreV1().Events(metav1.NamespaceAll)) 174 if err != nil { 175 return nil, err 176 } 177 178 lister := operatorlister.NewLister() 179 180 scheme := runtime.NewScheme() 181 if err := k8sscheme.AddToScheme(scheme); err != nil { 182 return nil, err 183 } 184 if err := metav1.AddMetaToScheme(scheme); err != nil { 185 return nil, err 186 } 187 188 canFilter, err := labeller.Validate(ctx, config.logger, config.metadataClient, config.externalClient, labeller.IdentityOLMOperator) 189 if err != nil { 190 return nil, err 191 } 192 193 op := &Operator{ 194 Operator: queueOperator, 195 clock: config.clock, 196 logger: config.logger, 197 opClient: config.operatorClient, 198 client: config.externalClient, 199 ogQueueSet: queueinformer.NewEmptyResourceQueueSet(), 200 csvQueueSet: queueinformer.NewEmptyResourceQueueSet(), 201 olmConfigQueue: workqueue.NewTypedRateLimitingQueueWithConfig[any]( 202 workqueue.DefaultTypedControllerRateLimiter[any](), 203 workqueue.TypedRateLimitingQueueConfig[any]{ 204 Name: "olmConfig", 205 }), 206 207 csvCopyQueueSet: queueinformer.NewEmptyResourceQueueSet(), 208 copiedCSVGCQueueSet: queueinformer.NewEmptyResourceQueueSet(), 209 apiServiceQueue: workqueue.NewTypedRateLimitingQueueWithConfig[any]( 210 workqueue.DefaultTypedControllerRateLimiter[any](), 211 workqueue.TypedRateLimitingQueueConfig[any]{ 212 Name: "apiservice", 213 }), 214 resolver: config.strategyResolver, 215 apiReconciler: config.apiReconciler, 216 lister: lister, 217 recorder: eventRecorder, 218 apiLabeler: config.apiLabeler, 219 csvIndexers: map[string]cache.Indexer{}, 220 csvSetGenerator: csvutility.NewSetGenerator(config.logger, lister), 221 csvReplaceFinder: csvutility.NewReplaceFinder(config.logger, config.externalClient), 222 serviceAccountSyncer: scoped.NewUserDefinedServiceAccountSyncer(config.logger, scheme, config.operatorClient, config.externalClient), 223 clientAttenuator: scoped.NewClientAttenuator(config.logger, config.restConfig, config.operatorClient), 224 serviceAccountQuerier: scoped.NewUserDefinedServiceAccountQuerier(config.logger, config.externalClient), 225 clientFactory: clients.NewFactory(config.restConfig), 226 protectedCopiedCSVNamespaces: config.protectedCopiedCSVNamespaces, 227 resyncPeriod: config.resyncPeriod, 228 ruleCheckerLock: sync.RWMutex{}, 229 ctx: ctx, 230 informersFiltered: canFilter, 231 } 232 233 informersByNamespace := map[string]*plugins.Informers{} 234 // Set up syncing for namespace-scoped resources 235 k8sSyncer := queueinformer.LegacySyncHandler(op.syncObject).ToSyncerWithDelete(op.handleDeletion) 236 for _, namespace := range config.watchedNamespaces { 237 informersByNamespace[namespace] = &plugins.Informers{} 238 // Wire CSVs 239 csvInformer := externalversions.NewSharedInformerFactoryWithOptions( 240 op.client, 241 config.resyncPeriod(), 242 externalversions.WithNamespace(namespace), 243 externalversions.WithTweakListOptions(func(options *metav1.ListOptions) { 244 options.LabelSelector = fmt.Sprintf("!%s", v1alpha1.CopiedLabelKey) 245 }), 246 ).Operators().V1alpha1().ClusterServiceVersions() 247 informersByNamespace[namespace].CSVInformer = csvInformer 248 op.lister.OperatorsV1alpha1().RegisterClusterServiceVersionLister(namespace, csvInformer.Lister()) 249 csvQueue := workqueue.NewTypedRateLimitingQueueWithConfig[any]( 250 workqueue.DefaultTypedControllerRateLimiter[any](), 251 workqueue.TypedRateLimitingQueueConfig[any]{ 252 Name: fmt.Sprintf("%s/csv", namespace), 253 }) 254 op.csvQueueSet.Set(namespace, csvQueue) 255 csvQueueInformer, err := queueinformer.NewQueueInformer( 256 ctx, 257 queueinformer.WithMetricsProvider(metrics.NewMetricsCSV(csvInformer.Lister())), 258 queueinformer.WithLogger(op.logger), 259 queueinformer.WithQueue(csvQueue), 260 queueinformer.WithInformer(csvInformer.Informer()), 261 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncClusterServiceVersion).ToSyncerWithDelete(op.handleClusterServiceVersionDeletion)), 262 ) 263 if err != nil { 264 return nil, err 265 } 266 if err := op.RegisterQueueInformer(csvQueueInformer); err != nil { 267 return nil, err 268 } 269 if err := csvInformer.Informer().AddIndexers(cache.Indexers{index.MetaLabelIndexFuncKey: index.MetaLabelIndexFunc}); err != nil { 270 return nil, err 271 } 272 csvIndexer := csvInformer.Informer().GetIndexer() 273 op.csvIndexers[namespace] = csvIndexer 274 275 // Register separate queue for copying csvs 276 csvCopyQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[any](), fmt.Sprintf("%s/csv-copy", namespace)) 277 op.csvCopyQueueSet.Set(namespace, csvCopyQueue) 278 csvCopyQueueInformer, err := queueinformer.NewQueueInformer( 279 ctx, 280 queueinformer.WithLogger(op.logger), 281 queueinformer.WithQueue(csvCopyQueue), 282 queueinformer.WithIndexer(csvIndexer), 283 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncCopyCSV).ToSyncer()), 284 ) 285 if err != nil { 286 return nil, err 287 } 288 if err := op.RegisterQueueInformer(csvCopyQueueInformer); err != nil { 289 return nil, err 290 } 291 292 // A separate informer solely for CSV copies. Object metadata requests are used 293 // by this informer in order to reduce cached size. 294 gvr := v1alpha1.SchemeGroupVersion.WithResource("clusterserviceversions") 295 copiedCSVInformer := metadatainformer.NewFilteredMetadataInformer( 296 config.metadataClient, 297 gvr, 298 namespace, 299 config.resyncPeriod(), 300 cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, 301 func(options *metav1.ListOptions) { 302 options.LabelSelector = v1alpha1.CopiedLabelKey 303 }, 304 ).Informer() 305 op.copiedCSVLister = metadatalister.New(copiedCSVInformer.GetIndexer(), gvr) 306 informersByNamespace[namespace].CopiedCSVInformer = copiedCSVInformer 307 informersByNamespace[namespace].CopiedCSVLister = op.copiedCSVLister 308 309 // Register separate queue for gcing copied csvs 310 copiedCSVGCQueue := workqueue.NewTypedRateLimitingQueueWithConfig[any]( 311 workqueue.DefaultTypedControllerRateLimiter[any](), 312 workqueue.TypedRateLimitingQueueConfig[any]{ 313 Name: fmt.Sprintf("%s/csv-gc", namespace), 314 }) 315 op.copiedCSVGCQueueSet.Set(namespace, copiedCSVGCQueue) 316 copiedCSVGCQueueInformer, err := queueinformer.NewQueueInformer( 317 ctx, 318 queueinformer.WithInformer(copiedCSVInformer), 319 queueinformer.WithLogger(op.logger), 320 queueinformer.WithQueue(copiedCSVGCQueue), 321 queueinformer.WithIndexer(copiedCSVInformer.GetIndexer()), 322 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncGcCsv).ToSyncer()), 323 ) 324 if err != nil { 325 return nil, err 326 } 327 if err := op.RegisterQueueInformer(copiedCSVGCQueueInformer); err != nil { 328 return nil, err 329 } 330 331 // Wire OperatorGroup reconciliation 332 extInformerFactory := externalversions.NewSharedInformerFactoryWithOptions(op.client, config.resyncPeriod(), externalversions.WithNamespace(namespace)) 333 operatorGroupInformer := extInformerFactory.Operators().V1().OperatorGroups() 334 informersByNamespace[namespace].OperatorGroupInformer = operatorGroupInformer 335 op.lister.OperatorsV1().RegisterOperatorGroupLister(namespace, operatorGroupInformer.Lister()) 336 ogQueue := workqueue.NewTypedRateLimitingQueueWithConfig[any]( 337 workqueue.DefaultTypedControllerRateLimiter[any](), 338 workqueue.TypedRateLimitingQueueConfig[any]{ 339 Name: fmt.Sprintf("%s/og", namespace), 340 }) 341 op.ogQueueSet.Set(namespace, ogQueue) 342 operatorGroupQueueInformer, err := queueinformer.NewQueueInformer( 343 ctx, 344 queueinformer.WithLogger(op.logger), 345 queueinformer.WithQueue(ogQueue), 346 queueinformer.WithInformer(operatorGroupInformer.Informer()), 347 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncOperatorGroups).ToSyncerWithDelete(op.operatorGroupDeleted)), 348 ) 349 if err != nil { 350 return nil, err 351 } 352 if err := op.RegisterQueueInformer(operatorGroupQueueInformer); err != nil { 353 return nil, err 354 } 355 356 // Register OperatorCondition QueueInformer 357 opConditionInformer := extInformerFactory.Operators().V2().OperatorConditions() 358 informersByNamespace[namespace].OperatorConditionInformer = opConditionInformer 359 op.lister.OperatorsV2().RegisterOperatorConditionLister(namespace, opConditionInformer.Lister()) 360 opConditionQueueInformer, err := queueinformer.NewQueueInformer( 361 ctx, 362 queueinformer.WithLogger(op.logger), 363 queueinformer.WithInformer(opConditionInformer.Informer()), 364 queueinformer.WithSyncer(k8sSyncer), 365 ) 366 if err != nil { 367 return nil, err 368 } 369 if err := op.RegisterQueueInformer(opConditionQueueInformer); err != nil { 370 return nil, err 371 } 372 373 subInformer := extInformerFactory.Operators().V1alpha1().Subscriptions() 374 informersByNamespace[namespace].SubscriptionInformer = subInformer 375 op.lister.OperatorsV1alpha1().RegisterSubscriptionLister(namespace, subInformer.Lister()) 376 subQueueInformer, err := queueinformer.NewQueueInformer( 377 ctx, 378 queueinformer.WithLogger(op.logger), 379 queueinformer.WithInformer(subInformer.Informer()), 380 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncSubscription).ToSyncerWithDelete(op.syncSubscriptionDeleted)), 381 ) 382 if err != nil { 383 return nil, err 384 } 385 if err := op.RegisterQueueInformer(subQueueInformer); err != nil { 386 return nil, err 387 } 388 389 // Wire Deployments 390 k8sInformerFactory := informers.NewSharedInformerFactoryWithOptions(op.opClient.KubernetesInterface(), config.resyncPeriod(), func() []informers.SharedInformerOption { 391 opts := []informers.SharedInformerOption{ 392 informers.WithNamespace(namespace), 393 } 394 if canFilter { 395 opts = append(opts, informers.WithTweakListOptions(func(options *metav1.ListOptions) { 396 options.LabelSelector = labels.SelectorFromSet(labels.Set{install.OLMManagedLabelKey: install.OLMManagedLabelValue}).String() 397 })) 398 } 399 return opts 400 }()...) 401 depInformer := k8sInformerFactory.Apps().V1().Deployments() 402 informersByNamespace[namespace].DeploymentInformer = depInformer 403 op.lister.AppsV1().RegisterDeploymentLister(namespace, depInformer.Lister()) 404 depQueueInformer, err := queueinformer.NewQueueInformer( 405 ctx, 406 queueinformer.WithLogger(op.logger), 407 queueinformer.WithInformer(depInformer.Informer()), 408 queueinformer.WithSyncer(k8sSyncer), 409 ) 410 if err != nil { 411 return nil, err 412 } 413 if err := op.RegisterQueueInformer(depQueueInformer); err != nil { 414 return nil, err 415 } 416 417 // Set up RBAC informers 418 roleInformer := k8sInformerFactory.Rbac().V1().Roles() 419 informersByNamespace[namespace].RoleInformer = roleInformer 420 op.lister.RbacV1().RegisterRoleLister(namespace, roleInformer.Lister()) 421 roleQueueInformer, err := queueinformer.NewQueueInformer( 422 ctx, 423 queueinformer.WithLogger(op.logger), 424 queueinformer.WithInformer(roleInformer.Informer()), 425 queueinformer.WithSyncer(k8sSyncer), 426 ) 427 if err != nil { 428 return nil, err 429 } 430 if err := op.RegisterQueueInformer(roleQueueInformer); err != nil { 431 return nil, err 432 } 433 434 roleBindingInformer := k8sInformerFactory.Rbac().V1().RoleBindings() 435 informersByNamespace[namespace].RoleBindingInformer = roleBindingInformer 436 op.lister.RbacV1().RegisterRoleBindingLister(namespace, roleBindingInformer.Lister()) 437 roleBindingQueueInformer, err := queueinformer.NewQueueInformer( 438 ctx, 439 queueinformer.WithLogger(op.logger), 440 queueinformer.WithInformer(roleBindingInformer.Informer()), 441 queueinformer.WithSyncer(k8sSyncer), 442 ) 443 if err != nil { 444 return nil, err 445 } 446 if err := op.RegisterQueueInformer(roleBindingQueueInformer); err != nil { 447 return nil, err 448 } 449 450 // Register Secret QueueInformer 451 secretInformer := informers.NewSharedInformerFactoryWithOptions(op.opClient.KubernetesInterface(), config.resyncPeriod(), informers.WithNamespace(namespace), informers.WithTweakListOptions(func(options *metav1.ListOptions) { 452 options.LabelSelector = labels.SelectorFromValidatedSet(map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}).String() 453 })).Core().V1().Secrets() 454 informersByNamespace[namespace].SecretInformer = secretInformer 455 op.lister.CoreV1().RegisterSecretLister(namespace, secretInformer.Lister()) 456 secretQueueInformer, err := queueinformer.NewQueueInformer( 457 ctx, 458 queueinformer.WithLogger(op.logger), 459 queueinformer.WithInformer(secretInformer.Informer()), 460 queueinformer.WithSyncer(k8sSyncer), 461 ) 462 if err != nil { 463 return nil, err 464 } 465 if err := op.RegisterQueueInformer(secretQueueInformer); err != nil { 466 return nil, err 467 } 468 469 // Register Service QueueInformer 470 serviceInformer := k8sInformerFactory.Core().V1().Services() 471 informersByNamespace[namespace].ServiceInformer = serviceInformer 472 op.lister.CoreV1().RegisterServiceLister(namespace, serviceInformer.Lister()) 473 serviceQueueInformer, err := queueinformer.NewQueueInformer( 474 ctx, 475 queueinformer.WithLogger(op.logger), 476 queueinformer.WithInformer(serviceInformer.Informer()), 477 queueinformer.WithSyncer(k8sSyncer), 478 ) 479 if err != nil { 480 return nil, err 481 } 482 if err := op.RegisterQueueInformer(serviceQueueInformer); err != nil { 483 return nil, err 484 } 485 486 // Register ServiceAccount QueueInformer 487 serviceAccountInformer := k8sInformerFactory.Core().V1().ServiceAccounts() 488 informersByNamespace[namespace].ServiceAccountInformer = serviceAccountInformer 489 op.lister.CoreV1().RegisterServiceAccountLister(metav1.NamespaceAll, serviceAccountInformer.Lister()) 490 serviceAccountQueueInformer, err := queueinformer.NewQueueInformer( 491 ctx, 492 queueinformer.WithLogger(op.logger), 493 queueinformer.WithInformer(serviceAccountInformer.Informer()), 494 queueinformer.WithSyncer(k8sSyncer), 495 ) 496 if err != nil { 497 return nil, err 498 } 499 if err := op.RegisterQueueInformer(serviceAccountQueueInformer); err != nil { 500 return nil, err 501 } 502 } 503 504 complete := map[schema.GroupVersionResource][]bool{} 505 completeLock := &sync.Mutex{} 506 507 labelObjects := func(gvr schema.GroupVersionResource, informer cache.SharedIndexInformer, sync func(done func() bool) queueinformer.LegacySyncHandler) error { 508 if canFilter { 509 return nil 510 } 511 512 // for each GVR, we may have more than one labelling controller active; each of which detects 513 // when it is done; we allocate a space in complete[gvr][idx] to hold that outcome and track it 514 var idx int 515 if _, exists := complete[gvr]; exists { 516 idx = len(complete[gvr]) 517 complete[gvr] = append(complete[gvr], false) 518 } else { 519 idx = 0 520 complete[gvr] = []bool{false} 521 } 522 logger := op.logger.WithFields(logrus.Fields{"gvr": gvr.String(), "index": idx}) 523 logger.Info("registering labeller") 524 525 queue := workqueue.NewTypedRateLimitingQueueWithConfig[any](workqueue.DefaultTypedControllerRateLimiter[any](), workqueue.TypedRateLimitingQueueConfig[any]{ 526 Name: gvr.String(), 527 }) 528 queueInformer, err := queueinformer.NewQueueInformer( 529 ctx, 530 queueinformer.WithQueue(queue), 531 queueinformer.WithLogger(op.logger), 532 queueinformer.WithInformer(informer), 533 queueinformer.WithSyncer(sync(func() bool { 534 // this function is called by the processor when it detects that it's work is done - so, for that 535 // particular labelling action on that particular GVR, all objects are in the correct state. when 536 // that action is done, we need to further know if that was the last action to be completed, as 537 // when every action we know about has been completed, we re-start the process to allow the future 538 // invocation of this process to filter informers (canFilter = true) and elide all this logic 539 completeLock.Lock() 540 logger.Info("labeller complete") 541 complete[gvr][idx] = true 542 allDone := true 543 for _, items := range complete { 544 for _, done := range items { 545 allDone = allDone && done 546 } 547 } 548 completeLock.Unlock() 549 return allDone 550 }).ToSyncer()), 551 ) 552 if err != nil { 553 return err 554 } 555 556 if err := op.RegisterQueueInformer(queueInformer); err != nil { 557 return err 558 } 559 560 return nil 561 } 562 563 deploymentsgvk := appsv1.SchemeGroupVersion.WithResource("deployments") 564 if err := labelObjects(deploymentsgvk, informersByNamespace[metav1.NamespaceAll].DeploymentInformer.Informer(), labeller.ObjectLabeler[*appsv1.Deployment, *appsv1applyconfigurations.DeploymentApplyConfiguration]( 565 ctx, op.logger, labeller.Filter(deploymentsgvk), 566 informersByNamespace[metav1.NamespaceAll].DeploymentInformer.Lister().List, 567 appsv1applyconfigurations.Deployment, 568 func(namespace string, ctx context.Context, cfg *appsv1applyconfigurations.DeploymentApplyConfiguration, opts metav1.ApplyOptions) (*appsv1.Deployment, error) { 569 return op.opClient.KubernetesInterface().AppsV1().Deployments(namespace).Apply(ctx, cfg, opts) 570 }, 571 )); err != nil { 572 return nil, err 573 } 574 575 // Register QueueInformer for olmConfig 576 olmConfigInformer := externalversions.NewSharedInformerFactoryWithOptions( 577 op.client, 578 config.resyncPeriod(), 579 ).Operators().V1().OLMConfigs() 580 informersByNamespace[metav1.NamespaceAll].OLMConfigInformer = olmConfigInformer 581 olmConfigQueueInformer, err := queueinformer.NewQueueInformer( 582 ctx, 583 queueinformer.WithInformer(olmConfigInformer.Informer()), 584 queueinformer.WithLogger(op.logger), 585 queueinformer.WithQueue(op.olmConfigQueue), 586 queueinformer.WithIndexer(olmConfigInformer.Informer().GetIndexer()), 587 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncOLMConfig).ToSyncer()), 588 ) 589 if err != nil { 590 return nil, err 591 } 592 if err := op.RegisterQueueInformer(olmConfigQueueInformer); err != nil { 593 return nil, err 594 } 595 596 k8sInformerFactory := informers.NewSharedInformerFactoryWithOptions(op.opClient.KubernetesInterface(), config.resyncPeriod(), func() []informers.SharedInformerOption { 597 if !canFilter { 598 return nil 599 } 600 return []informers.SharedInformerOption{informers.WithTweakListOptions(func(options *metav1.ListOptions) { 601 options.LabelSelector = labels.SelectorFromSet(labels.Set{install.OLMManagedLabelKey: install.OLMManagedLabelValue}).String() 602 })} 603 }()...) 604 clusterRoleInformer := k8sInformerFactory.Rbac().V1().ClusterRoles() 605 informersByNamespace[metav1.NamespaceAll].ClusterRoleInformer = clusterRoleInformer 606 op.lister.RbacV1().RegisterClusterRoleLister(clusterRoleInformer.Lister()) 607 clusterRoleQueueInformer, err := queueinformer.NewQueueInformer( 608 ctx, 609 queueinformer.WithLogger(op.logger), 610 queueinformer.WithInformer(clusterRoleInformer.Informer()), 611 queueinformer.WithSyncer(k8sSyncer), 612 ) 613 if err != nil { 614 return nil, err 615 } 616 if err := op.RegisterQueueInformer(clusterRoleQueueInformer); err != nil { 617 return nil, err 618 } 619 620 clusterrolesgvk := rbacv1.SchemeGroupVersion.WithResource("clusterroles") 621 if err := labelObjects(clusterrolesgvk, clusterRoleInformer.Informer(), labeller.ObjectLabeler[*rbacv1.ClusterRole, *rbacv1applyconfigurations.ClusterRoleApplyConfiguration]( 622 ctx, op.logger, labeller.Filter(clusterrolesgvk), 623 clusterRoleInformer.Lister().List, 624 func(name, _ string) *rbacv1applyconfigurations.ClusterRoleApplyConfiguration { 625 return rbacv1applyconfigurations.ClusterRole(name) 626 }, 627 func(_ string, ctx context.Context, cfg *rbacv1applyconfigurations.ClusterRoleApplyConfiguration, opts metav1.ApplyOptions) (*rbacv1.ClusterRole, error) { 628 return op.opClient.KubernetesInterface().RbacV1().ClusterRoles().Apply(ctx, cfg, opts) 629 }, 630 )); err != nil { 631 return nil, err 632 } 633 if err := labelObjects(clusterrolesgvk, clusterRoleInformer.Informer(), labeller.ContentHashLabeler[*rbacv1.ClusterRole, *rbacv1applyconfigurations.ClusterRoleApplyConfiguration]( 634 ctx, op.logger, labeller.ContentHashFilter, 635 func(clusterRole *rbacv1.ClusterRole) (string, error) { 636 return resolver.PolicyRuleHashLabelValue(clusterRole.Rules) 637 }, 638 clusterRoleInformer.Lister().List, 639 func(name, _ string) *rbacv1applyconfigurations.ClusterRoleApplyConfiguration { 640 return rbacv1applyconfigurations.ClusterRole(name) 641 }, 642 func(_ string, ctx context.Context, cfg *rbacv1applyconfigurations.ClusterRoleApplyConfiguration, opts metav1.ApplyOptions) (*rbacv1.ClusterRole, error) { 643 return op.opClient.KubernetesInterface().RbacV1().ClusterRoles().Apply(ctx, cfg, opts) 644 }, 645 )); err != nil { 646 return nil, err 647 } 648 649 clusterRoleBindingInformer := k8sInformerFactory.Rbac().V1().ClusterRoleBindings() 650 informersByNamespace[metav1.NamespaceAll].ClusterRoleBindingInformer = clusterRoleBindingInformer 651 op.lister.RbacV1().RegisterClusterRoleBindingLister(clusterRoleBindingInformer.Lister()) 652 clusterRoleBindingQueueInformer, err := queueinformer.NewQueueInformer( 653 ctx, 654 queueinformer.WithLogger(op.logger), 655 queueinformer.WithInformer(clusterRoleBindingInformer.Informer()), 656 queueinformer.WithSyncer(k8sSyncer), 657 ) 658 if err != nil { 659 return nil, err 660 } 661 if err := op.RegisterQueueInformer(clusterRoleBindingQueueInformer); err != nil { 662 return nil, err 663 } 664 665 clusterrolebindingssgvk := rbacv1.SchemeGroupVersion.WithResource("clusterrolebindings") 666 if err := labelObjects(clusterrolebindingssgvk, clusterRoleBindingInformer.Informer(), labeller.ObjectLabeler[*rbacv1.ClusterRoleBinding, *rbacv1applyconfigurations.ClusterRoleBindingApplyConfiguration]( 667 ctx, op.logger, labeller.Filter(clusterrolebindingssgvk), 668 clusterRoleBindingInformer.Lister().List, 669 func(name, _ string) *rbacv1applyconfigurations.ClusterRoleBindingApplyConfiguration { 670 return rbacv1applyconfigurations.ClusterRoleBinding(name) 671 }, 672 func(_ string, ctx context.Context, cfg *rbacv1applyconfigurations.ClusterRoleBindingApplyConfiguration, opts metav1.ApplyOptions) (*rbacv1.ClusterRoleBinding, error) { 673 return op.opClient.KubernetesInterface().RbacV1().ClusterRoleBindings().Apply(ctx, cfg, opts) 674 }, 675 )); err != nil { 676 return nil, err 677 } 678 if err := labelObjects(clusterrolebindingssgvk, clusterRoleBindingInformer.Informer(), labeller.ContentHashLabeler[*rbacv1.ClusterRoleBinding, *rbacv1applyconfigurations.ClusterRoleBindingApplyConfiguration]( 679 ctx, op.logger, labeller.ContentHashFilter, 680 func(clusterRoleBinding *rbacv1.ClusterRoleBinding) (string, error) { 681 return resolver.RoleReferenceAndSubjectHashLabelValue(clusterRoleBinding.RoleRef, clusterRoleBinding.Subjects) 682 }, 683 clusterRoleBindingInformer.Lister().List, 684 func(name, _ string) *rbacv1applyconfigurations.ClusterRoleBindingApplyConfiguration { 685 return rbacv1applyconfigurations.ClusterRoleBinding(name) 686 }, 687 func(_ string, ctx context.Context, cfg *rbacv1applyconfigurations.ClusterRoleBindingApplyConfiguration, opts metav1.ApplyOptions) (*rbacv1.ClusterRoleBinding, error) { 688 return op.opClient.KubernetesInterface().RbacV1().ClusterRoleBindings().Apply(ctx, cfg, opts) 689 }, 690 )); err != nil { 691 return nil, err 692 } 693 694 // register namespace queueinformer using a new informer factory - since namespaces won't have the labels 695 // that other k8s objects will 696 namespaceInformer := informers.NewSharedInformerFactory(op.opClient.KubernetesInterface(), config.resyncPeriod()).Core().V1().Namespaces() 697 informersByNamespace[metav1.NamespaceAll].NamespaceInformer = namespaceInformer 698 op.lister.CoreV1().RegisterNamespaceLister(namespaceInformer.Lister()) 699 op.nsQueueSet = workqueue.NewTypedRateLimitingQueueWithConfig[any]( 700 workqueue.DefaultTypedControllerRateLimiter[any](), 701 workqueue.TypedRateLimitingQueueConfig[any]{ 702 Name: "resolver", 703 }) 704 namespaceInformer.Informer().AddEventHandler( 705 &cache.ResourceEventHandlerFuncs{ 706 DeleteFunc: op.namespaceAddedOrRemoved, 707 AddFunc: op.namespaceAddedOrRemoved, 708 }, 709 ) 710 namespaceQueueInformer, err := queueinformer.NewQueueInformer( 711 ctx, 712 queueinformer.WithLogger(op.logger), 713 queueinformer.WithQueue(op.nsQueueSet), 714 queueinformer.WithInformer(namespaceInformer.Informer()), 715 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncNamespace).ToSyncer()), 716 ) 717 if err != nil { 718 return nil, err 719 } 720 if err := op.RegisterQueueInformer(namespaceQueueInformer); err != nil { 721 return nil, err 722 } 723 724 // Register APIService QueueInformer 725 apiServiceInformer := kagg.NewSharedInformerFactory(op.opClient.ApiregistrationV1Interface(), config.resyncPeriod()).Apiregistration().V1().APIServices() 726 informersByNamespace[metav1.NamespaceAll].APIServiceInformer = apiServiceInformer 727 op.lister.APIRegistrationV1().RegisterAPIServiceLister(apiServiceInformer.Lister()) 728 apiServiceQueueInformer, err := queueinformer.NewQueueInformer( 729 ctx, 730 queueinformer.WithLogger(op.logger), 731 queueinformer.WithQueue(op.apiServiceQueue), 732 queueinformer.WithInformer(apiServiceInformer.Informer()), 733 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncAPIService).ToSyncerWithDelete(op.handleDeletion)), 734 ) 735 if err != nil { 736 return nil, err 737 } 738 if err := op.RegisterQueueInformer(apiServiceQueueInformer); err != nil { 739 return nil, err 740 } 741 742 // Register CustomResourceDefinition QueueInformer. Object metadata requests are used 743 // by this informer in order to reduce cached size. 744 gvr := apiextensionsv1.SchemeGroupVersion.WithResource("customresourcedefinitions") 745 crdInformer := metadatainformer.NewFilteredMetadataInformer( 746 config.metadataClient, 747 gvr, 748 metav1.NamespaceAll, 749 config.resyncPeriod(), 750 cache.Indexers{}, 751 nil, 752 ).Informer() 753 crdLister := metadatalister.New(crdInformer.GetIndexer(), gvr) 754 informersByNamespace[metav1.NamespaceAll].CRDInformer = crdInformer 755 informersByNamespace[metav1.NamespaceAll].CRDLister = crdLister 756 op.lister.APIExtensionsV1().RegisterCustomResourceDefinitionLister(crdLister) 757 crdQueueInformer, err := queueinformer.NewQueueInformer( 758 ctx, 759 queueinformer.WithLogger(op.logger), 760 queueinformer.WithInformer(crdInformer), 761 queueinformer.WithSyncer(k8sSyncer), 762 ) 763 if err != nil { 764 return nil, err 765 } 766 if err := op.RegisterQueueInformer(crdQueueInformer); err != nil { 767 return nil, err 768 } 769 770 // setup proxy env var injection policies 771 discovery := config.operatorClient.KubernetesInterface().Discovery() 772 proxyAPIExists, err := proxy.IsAPIAvailable(discovery) 773 if err != nil { 774 op.logger.Errorf("error happened while probing for Proxy API support - %v", err) 775 return nil, err 776 } 777 778 proxyQuerierInUse := proxy.NoopQuerier() 779 if proxyAPIExists { 780 op.logger.Info("OpenShift Proxy API available - setting up watch for Proxy type") 781 782 proxyInformer, proxySyncer, proxyQuerier, err := proxy.NewSyncer(op.logger, config.configClient, discovery) 783 if err != nil { 784 err = fmt.Errorf("failed to initialize syncer for Proxy type - %v", err) 785 return nil, err 786 } 787 788 op.logger.Info("OpenShift Proxy query will be used to fetch cluster proxy configuration") 789 proxyQuerierInUse = proxyQuerier 790 791 informer, err := queueinformer.NewQueueInformer( 792 ctx, 793 queueinformer.WithLogger(op.logger), 794 queueinformer.WithInformer(proxyInformer.Informer()), 795 queueinformer.WithSyncer(queueinformer.LegacySyncHandler(proxySyncer.SyncProxy).ToSyncerWithDelete(proxySyncer.HandleProxyDelete)), 796 ) 797 if err != nil { 798 return nil, err 799 } 800 if err := op.RegisterQueueInformer(informer); err != nil { 801 return nil, err 802 } 803 } 804 805 overridesBuilderFunc := overrides.NewDeploymentInitializer(op.logger, proxyQuerierInUse, op.lister) 806 op.resolver = &install.StrategyResolver{ 807 OverridesBuilderFunc: overridesBuilderFunc.GetDeploymentInitializer, 808 } 809 op.informersByNamespace = informersByNamespace 810 811 // initialize plugins 812 for _, makePlugIn := range operatorPlugInFactoryFuncs { 813 plugin, err := makePlugIn(ctx, config, op) 814 if err != nil { 815 return nil, fmt.Errorf("error creating plugin: %s", err) 816 } 817 op.plugins = append(op.plugins, plugin) 818 } 819 820 if len(operatorPlugInFactoryFuncs) > 0 { 821 go func() { 822 // block until operator is done 823 <-op.Done() 824 825 // shutdown plug-ins 826 for _, plugin := range op.plugins { 827 if err := plugin.Shutdown(); err != nil { 828 if op.logger != nil { 829 op.logger.Warnf("error shutting down plug-in: %s", err) 830 } 831 } 832 } 833 }() 834 } 835 836 return op, nil 837 } 838 839 func (a *Operator) now() *metav1.Time { 840 now := metav1.NewTime(a.clock.Now().UTC()) 841 return &now 842 } 843 844 func (a *Operator) syncSubscription(obj interface{}) error { 845 sub, ok := obj.(*v1alpha1.Subscription) 846 if !ok { 847 a.logger.Debugf("wrong type: %#v\n", obj) 848 return fmt.Errorf("casting Subscription failed") 849 } 850 851 installedCSV := sub.Status.InstalledCSV 852 if installedCSV != "" { 853 a.logger.WithField("csv", installedCSV).Debug("subscription has changed, requeuing installed csv") 854 if err := a.csvQueueSet.Requeue(sub.GetNamespace(), installedCSV); err != nil { 855 a.logger.WithField("csv", installedCSV).Debug("failed to requeue installed csv") 856 return err 857 } 858 } 859 860 return nil 861 } 862 863 func (a *Operator) syncSubscriptionDeleted(obj interface{}) { 864 _, ok := obj.(*v1alpha1.Subscription) 865 if !ok { 866 a.logger.Debugf("casting Subscription failed, wrong type: %#v\n", obj) 867 } 868 } 869 870 func (a *Operator) syncAPIService(obj interface{}) (syncError error) { 871 apiService, ok := obj.(*apiregistrationv1.APIService) 872 if !ok { 873 a.logger.Debugf("wrong type: %#v", obj) 874 return fmt.Errorf("casting APIService failed") 875 } 876 877 logger := a.logger.WithFields(logrus.Fields{ 878 "id": queueinformer.NewLoopID(), 879 "apiService": apiService.GetName(), 880 }) 881 logger.Debug("syncing APIService") 882 883 if name, ns, ok := ownerutil.GetOwnerByKindLabel(apiService, v1alpha1.ClusterServiceVersionKind); ok { 884 _, err := a.lister.CoreV1().NamespaceLister().Get(ns) 885 if apierrors.IsNotFound(err) { 886 logger.Debug("Deleting api service since owning namespace is not found") 887 syncError = a.opClient.DeleteAPIService(apiService.GetName(), &metav1.DeleteOptions{}) 888 return 889 } 890 891 _, err = a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ns).Get(name) 892 if apierrors.IsNotFound(err) { 893 logger.Debug("Deleting api service since owning CSV is not found") 894 syncError = a.opClient.DeleteAPIService(apiService.GetName(), &metav1.DeleteOptions{}) 895 return 896 } else if err != nil { 897 syncError = err 898 return 899 } else { 900 if ownerutil.IsOwnedByKindLabel(apiService, v1alpha1.ClusterServiceVersionKind) { 901 logger.Debug("requeueing owner CSVs") 902 a.requeueOwnerCSVs(apiService) 903 } 904 } 905 } 906 907 return nil 908 } 909 910 func (a *Operator) GetCSVSetGenerator() csvutility.SetGenerator { 911 return a.csvSetGenerator 912 } 913 914 func (a *Operator) GetReplaceFinder() csvutility.ReplaceFinder { 915 return a.csvReplaceFinder 916 } 917 918 func (a *Operator) RegisterCSVWatchNotification(csvNotification csvutility.WatchNotification) { 919 if csvNotification == nil { 920 return 921 } 922 923 a.csvNotification = csvNotification 924 } 925 926 func (a *Operator) EnsureCSVMetric() error { 927 csvs, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().List(labels.Everything()) 928 if err != nil { 929 return err 930 } 931 for _, csv := range csvs { 932 logger := a.logger.WithFields(logrus.Fields{ 933 "name": csv.GetName(), 934 "namespace": csv.GetNamespace(), 935 "self": csv.GetSelfLink(), 936 }) 937 logger.Debug("emitting metrics for existing CSV") 938 metrics.EmitCSVMetric(csv, csv) 939 } 940 return nil 941 } 942 943 func (a *Operator) syncObject(obj interface{}) (syncError error) { 944 // Assert as metav1.Object 945 metaObj, ok := obj.(metav1.Object) 946 if !ok { 947 syncError = errors.New("object sync: casting to metav1.Object failed") 948 a.logger.Warn(syncError.Error()) 949 return 950 } 951 logger := a.logger.WithFields(logrus.Fields{ 952 "name": metaObj.GetName(), 953 "namespace": metaObj.GetNamespace(), 954 "self": metaObj.GetSelfLink(), 955 }) 956 957 // Objects that can't have ownerrefs (cluster -> namespace, cross-namespace) 958 // are handled by finalizer 959 960 // Requeue all owner CSVs 961 if ownerutil.IsOwnedByKind(metaObj, v1alpha1.ClusterServiceVersionKind) { 962 logger.Debug("requeueing owner csvs") 963 a.requeueOwnerCSVs(metaObj) 964 } 965 966 // Requeue CSVs with provided and required labels (for CRDs) 967 if labelSets, err := a.apiLabeler.LabelSetsFor(metaObj); err != nil { 968 logger.WithError(err).Warn("couldn't create label set") 969 } else if len(labelSets) > 0 { 970 logger.Debug("requeueing providing/requiring csvs") 971 a.requeueCSVsByLabelSet(logger, labelSets...) 972 } 973 974 // Requeue CSVs that have the reason of `CSVReasonComponentFailedNoRetry` in the case of an RBAC change 975 var errs []error 976 related, _ := scoped.IsObjectRBACRelated(metaObj) 977 if related { 978 csvList := a.csvSet(metaObj.GetNamespace(), v1alpha1.CSVPhaseFailed) 979 for _, csv := range csvList { 980 csv = csv.DeepCopy() 981 if csv.Status.Reason != v1alpha1.CSVReasonComponentFailedNoRetry { 982 continue 983 } 984 csv.SetPhase(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonDetectedClusterChange, "Cluster resources changed state", a.now()) 985 _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).UpdateStatus(context.TODO(), csv, metav1.UpdateOptions{}) 986 if err != nil { 987 errs = append(errs, err) 988 continue 989 } 990 if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { 991 errs = append(errs, err) 992 } 993 logger.Debug("Requeuing CSV due to detected RBAC change") 994 } 995 } 996 997 syncError = utilerrors.NewAggregate(errs) 998 return nil 999 } 1000 1001 func (a *Operator) namespaceAddedOrRemoved(obj interface{}) { 1002 // Check to see if any operator groups are associated with this namespace 1003 namespace, ok := obj.(*corev1.Namespace) 1004 if !ok { 1005 return 1006 } 1007 1008 logger := a.logger.WithFields(logrus.Fields{ 1009 "name": namespace.GetName(), 1010 }) 1011 1012 operatorGroupList, err := a.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(metav1.NamespaceAll).List(labels.Everything()) 1013 if err != nil { 1014 logger.WithError(err).Warn("lister failed") 1015 return 1016 } 1017 1018 for _, group := range operatorGroupList { 1019 if NewNamespaceSet(group.Status.Namespaces).Contains(namespace.GetName()) { 1020 if err := a.ogQueueSet.Requeue(group.Namespace, group.Name); err != nil { 1021 logger.WithError(err).Warn("error requeuing operatorgroup") 1022 } 1023 } 1024 } 1025 } 1026 1027 func (a *Operator) syncNamespace(obj interface{}) error { 1028 // Check to see if any operator groups are associated with this namespace 1029 namespace, ok := obj.(*corev1.Namespace) 1030 if !ok { 1031 a.logger.Debugf("wrong type: %#v\n", obj) 1032 return fmt.Errorf("casting Namespace failed") 1033 } 1034 1035 logger := a.logger.WithFields(logrus.Fields{ 1036 "name": namespace.GetName(), 1037 }) 1038 1039 operatorGroupList, err := a.lister.OperatorsV1().OperatorGroupLister().List(labels.Everything()) 1040 if err != nil { 1041 logger.WithError(err).Warn("lister failed") 1042 return err 1043 } 1044 1045 desiredGroupLabels := make(map[string]string) 1046 for _, group := range operatorGroupList { 1047 namespaceSet := NewNamespaceSet(group.Status.Namespaces) 1048 1049 // Apply the label if not an All Namespaces OperatorGroup. 1050 if namespaceSet.Contains(namespace.GetName()) && !namespaceSet.IsAllNamespaces() { 1051 k, v, err := group.OGLabelKeyAndValue() 1052 if err != nil { 1053 return err 1054 } 1055 desiredGroupLabels[k] = v 1056 } 1057 } 1058 1059 if changed := func() bool { 1060 for ke, ve := range namespace.Labels { 1061 if !operatorsv1.IsOperatorGroupLabel(ke) { 1062 continue 1063 } 1064 if vd, ok := desiredGroupLabels[ke]; !ok || vd != ve { 1065 return true 1066 } 1067 } 1068 for kd, vd := range desiredGroupLabels { 1069 if ve, ok := namespace.Labels[kd]; !ok || ve != vd { 1070 return true 1071 } 1072 } 1073 return false 1074 }(); !changed { 1075 return nil 1076 } 1077 1078 namespace = namespace.DeepCopy() 1079 for k := range namespace.Labels { 1080 if operatorsv1.IsOperatorGroupLabel(k) { 1081 delete(namespace.Labels, k) 1082 } 1083 } 1084 if namespace.Labels == nil && len(desiredGroupLabels) > 0 { 1085 namespace.Labels = make(map[string]string) 1086 } 1087 for k, v := range desiredGroupLabels { 1088 namespace.Labels[k] = v 1089 } 1090 1091 _, err = a.opClient.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), namespace, metav1.UpdateOptions{}) 1092 1093 return err 1094 } 1095 1096 func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) { 1097 clusterServiceVersion, ok := obj.(*v1alpha1.ClusterServiceVersion) 1098 if !ok { 1099 tombstone, ok := obj.(cache.DeletedFinalStateUnknown) 1100 if !ok { 1101 utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj)) 1102 return 1103 } 1104 1105 clusterServiceVersion, ok = tombstone.Obj.(*v1alpha1.ClusterServiceVersion) 1106 if !ok { 1107 utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a ClusterServiceVersion %#v", obj)) 1108 return 1109 } 1110 } 1111 1112 if a.csvNotification != nil { 1113 a.csvNotification.OnDelete(clusterServiceVersion) 1114 } 1115 1116 logger := a.logger.WithFields(logrus.Fields{ 1117 "id": queueinformer.NewLoopID(), 1118 "csv": clusterServiceVersion.GetName(), 1119 "namespace": clusterServiceVersion.GetNamespace(), 1120 "phase": clusterServiceVersion.Status.Phase, 1121 }) 1122 1123 logger.Debug("start deleting CSV") 1124 defer logger.Debug("end deleting CSV") 1125 1126 metrics.DeleteCSVMetric(clusterServiceVersion) 1127 1128 if clusterServiceVersion.IsCopied() { 1129 logger.Warning("deleted csv is copied. skipping additional cleanup steps") // should not happen? 1130 return 1131 } 1132 1133 defer func(csv v1alpha1.ClusterServiceVersion) { 1134 if clusterServiceVersion.IsCopied() { 1135 logger.Debug("deleted csv is copied. skipping operatorgroup requeue") 1136 return 1137 } 1138 1139 // Requeue all OperatorGroups in the namespace 1140 logger.Debug("requeueing operatorgroups in namespace") 1141 operatorGroups, err := a.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(csv.GetNamespace()).List(labels.Everything()) 1142 if err != nil { 1143 logger.WithError(err).Warnf("an error occurred while listing operatorgroups to requeue after csv deletion") 1144 return 1145 } 1146 1147 for _, operatorGroup := range operatorGroups { 1148 logger := logger.WithField("operatorgroup", operatorGroup.GetName()) 1149 logger.Debug("requeueing") 1150 if err := a.ogQueueSet.Requeue(operatorGroup.GetNamespace(), operatorGroup.GetName()); err != nil { 1151 logger.WithError(err).Debug("error requeueing operatorgroup") 1152 } 1153 } 1154 }(*clusterServiceVersion) 1155 1156 targetNamespaces, ok := clusterServiceVersion.Annotations[operatorsv1.OperatorGroupTargetsAnnotationKey] 1157 if !ok { 1158 logger.Debug("missing target namespaces annotation on csv") 1159 return 1160 } 1161 1162 operatorNamespace, ok := clusterServiceVersion.Annotations[operatorsv1.OperatorGroupNamespaceAnnotationKey] 1163 if !ok { 1164 logger.Debug("missing operator namespace annotation on csv") 1165 return 1166 } 1167 1168 if _, ok = clusterServiceVersion.Annotations[operatorsv1.OperatorGroupAnnotationKey]; !ok { 1169 logger.Debug("missing operatorgroup name annotation on csv") 1170 return 1171 } 1172 1173 logger.Info("gcing children") 1174 namespaces := make([]string, 0) 1175 if targetNamespaces == "" { 1176 namespaceList, err := a.opClient.KubernetesInterface().CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) 1177 if err != nil { 1178 logger.WithError(err).Warn("cannot list all namespaces to requeue child csvs for deletion") 1179 return 1180 } 1181 for _, namespace := range namespaceList.Items { 1182 namespaces = append(namespaces, namespace.GetName()) 1183 } 1184 } else { 1185 namespaces = strings.Split(targetNamespaces, ",") 1186 } 1187 for _, namespace := range namespaces { 1188 if namespace != operatorNamespace { 1189 logger.WithField("targetNamespace", namespace).Debug("requeueing child csv for deletion") 1190 if err := a.copiedCSVGCQueueSet.Requeue(namespace, clusterServiceVersion.GetName()); err != nil { 1191 logger.WithError(err).Warn("unable to requeue") 1192 } 1193 } 1194 } 1195 1196 for _, desc := range clusterServiceVersion.Spec.APIServiceDefinitions.Owned { 1197 apiServiceName := fmt.Sprintf("%s.%s", desc.Version, desc.Group) 1198 fetched, err := a.lister.APIRegistrationV1().APIServiceLister().Get(apiServiceName) 1199 if apierrors.IsNotFound(err) { 1200 continue 1201 } 1202 if err != nil { 1203 logger.WithError(err).Warn("api service get failure") 1204 continue 1205 } 1206 apiServiceLabels := fetched.GetLabels() 1207 if clusterServiceVersion.GetName() == apiServiceLabels[ownerutil.OwnerKey] && clusterServiceVersion.GetNamespace() == apiServiceLabels[ownerutil.OwnerNamespaceKey] { 1208 logger.Infof("gcing api service %v", apiServiceName) 1209 err := a.opClient.DeleteAPIService(apiServiceName, &metav1.DeleteOptions{}) 1210 if err != nil { 1211 logger.WithError(err).Warn("cannot delete orphaned api service") 1212 } 1213 } 1214 } 1215 1216 // Conversion webhooks are defined within a CRD. 1217 // In an effort to prevent customer dataloss, OLM does not delete CRDs associated with a CSV when it is deleted. 1218 // Deleting a CSV that introduced a conversion webhook removes the deployment that serviced the conversion webhook calls. 1219 // If a conversion webhook is defined and the service isn't available, all requests against the CR associated with the CRD will fail. 1220 // This ultimately breaks kubernetes garbage collection and prevents OLM from reinstalling the CSV as CR validation against the new CRD's 1221 // openapiv3 schema fails. 1222 // As such, when a CSV is deleted OLM will check if it is being replaced. If the CSV is not being replaced, OLM will remove the conversion 1223 // webhook from the CRD definition. 1224 csvs, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(clusterServiceVersion.GetNamespace()).List(labels.Everything()) 1225 if err != nil { 1226 logger.Errorf("error listing csvs: %v\n", err) 1227 } 1228 for _, csv := range csvs { 1229 if csv.Spec.Replaces == clusterServiceVersion.GetName() { 1230 return 1231 } 1232 } 1233 1234 for _, desc := range clusterServiceVersion.Spec.WebhookDefinitions { 1235 if desc.Type != v1alpha1.ConversionWebhook || len(desc.ConversionCRDs) == 0 { 1236 continue 1237 } 1238 1239 for i, crdName := range desc.ConversionCRDs { 1240 crd, err := a.opClient.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crdName, metav1.GetOptions{}) 1241 if err != nil { 1242 logger.Errorf("error getting CRD %v which was defined in CSVs spec.WebhookDefinition[%d]: %v\n", crdName, i, err) 1243 continue 1244 } 1245 1246 copy := crd.DeepCopy() 1247 copy.Spec.Conversion.Strategy = apiextensionsv1.NoneConverter 1248 copy.Spec.Conversion.Webhook = nil 1249 1250 if _, err = a.opClient.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), copy, metav1.UpdateOptions{}); err != nil { 1251 logger.Errorf("error updating conversion strategy for CRD %v: %v\n", crdName, err) 1252 } 1253 } 1254 } 1255 } 1256 1257 func (a *Operator) removeDanglingChildCSVs(csv *metav1.PartialObjectMetadata) error { 1258 logger := a.logger.WithFields(logrus.Fields{ 1259 "id": queueinformer.NewLoopID(), 1260 "csv": csv.GetName(), 1261 "namespace": csv.GetNamespace(), 1262 "labels": csv.GetLabels(), 1263 "annotations": csv.GetAnnotations(), 1264 }) 1265 1266 if !v1alpha1.IsCopied(csv) { 1267 logger.Warning("removeDanglingChild called on a parent. this is a no-op but should be avoided.") 1268 return nil 1269 } 1270 1271 operatorNamespace, ok := csv.Annotations[operatorsv1.OperatorGroupNamespaceAnnotationKey] 1272 if !ok { 1273 logger.Debug("missing operator namespace annotation on copied CSV") 1274 return a.deleteChild(csv, logger) 1275 } 1276 1277 logger = logger.WithField("parentNamespace", operatorNamespace) 1278 parent, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(operatorNamespace).Get(csv.GetName()) 1279 if apierrors.IsNotFound(err) || apierrors.IsGone(err) || parent == nil { 1280 logger.Debug("deleting copied CSV since parent is missing") 1281 return a.deleteChild(csv, logger) 1282 } 1283 1284 if parent.Status.Phase == v1alpha1.CSVPhaseFailed && parent.Status.Reason == v1alpha1.CSVReasonInterOperatorGroupOwnerConflict { 1285 logger.Debug("deleting copied CSV since parent has intersecting operatorgroup conflict") 1286 return a.deleteChild(csv, logger) 1287 } 1288 1289 if annotations := parent.GetAnnotations(); annotations != nil { 1290 if !NewNamespaceSetFromString(annotations[operatorsv1.OperatorGroupTargetsAnnotationKey]).Contains(csv.GetNamespace()) { 1291 logger.WithField("parentTargets", annotations[operatorsv1.OperatorGroupTargetsAnnotationKey]). 1292 Debug("deleting copied CSV since parent no longer lists this as a target namespace") 1293 return a.deleteChild(csv, logger) 1294 } 1295 } 1296 1297 if parent.GetNamespace() == csv.GetNamespace() { 1298 logger.Debug("deleting copied CSV since it has incorrect parent annotations") 1299 return a.deleteChild(csv, logger) 1300 } 1301 1302 return nil 1303 } 1304 1305 func (a *Operator) deleteChild(csv *metav1.PartialObjectMetadata, logger *logrus.Entry) error { 1306 logger.Debug("gcing csv") 1307 return a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Delete(context.TODO(), csv.GetName(), metav1.DeleteOptions{}) 1308 } 1309 1310 // Return values, err, ok; ok == true: continue Reconcile, ok == false: exit Reconcile 1311 func (a *Operator) processFinalizer(csv *v1alpha1.ClusterServiceVersion, log *logrus.Entry) (error, bool) { 1312 myFinalizerName := "operators.coreos.com/csv-cleanup" 1313 1314 if csv.ObjectMeta.DeletionTimestamp.IsZero() { 1315 // CSV is not being deleted, add finalizer if not present 1316 if !controllerutil.ContainsFinalizer(csv, myFinalizerName) { 1317 controllerutil.AddFinalizer(csv, myFinalizerName) 1318 _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Update(a.ctx, csv, metav1.UpdateOptions{}) 1319 if err != nil { 1320 log.WithError(err).Error("Adding finalizer") 1321 return err, false 1322 } 1323 } 1324 return nil, true 1325 } 1326 1327 if !controllerutil.ContainsFinalizer(csv, myFinalizerName) { 1328 // Finalizer has been removed; stop reconciliation as the CSV is being deleted 1329 return nil, false 1330 } 1331 1332 log.Info("started finalizer") 1333 defer log.Info("completed finalizer") 1334 1335 // CSV is being deleted and the finalizer still present; do any clean up 1336 ownerSelector := ownerutil.CSVOwnerSelector(csv) 1337 listOptions := metav1.ListOptions{ 1338 LabelSelector: ownerSelector.String(), 1339 } 1340 deleteOptions := metav1.DeleteOptions{} 1341 // Look for resources owned by this CSV, and delete them. 1342 log.WithFields(logrus.Fields{"selector": ownerSelector}).Info("Cleaning up resources after CSV deletion") 1343 var errs []error 1344 1345 err := a.opClient.KubernetesInterface().RbacV1().ClusterRoleBindings().DeleteCollection(a.ctx, deleteOptions, listOptions) 1346 if client.IgnoreNotFound(err) != nil { 1347 log.WithError(err).Error("Deleting ClusterRoleBindings on CSV delete") 1348 errs = append(errs, err) 1349 } 1350 1351 err = a.opClient.KubernetesInterface().RbacV1().ClusterRoles().DeleteCollection(a.ctx, deleteOptions, listOptions) 1352 if client.IgnoreNotFound(err) != nil { 1353 log.WithError(err).Error("Deleting ClusterRoles on CSV delete") 1354 errs = append(errs, err) 1355 } 1356 err = a.opClient.KubernetesInterface().AdmissionregistrationV1().MutatingWebhookConfigurations().DeleteCollection(a.ctx, deleteOptions, listOptions) 1357 if client.IgnoreNotFound(err) != nil { 1358 log.WithError(err).Error("Deleting MutatingWebhookConfigurations on CSV delete") 1359 errs = append(errs, err) 1360 } 1361 1362 err = a.opClient.KubernetesInterface().AdmissionregistrationV1().ValidatingWebhookConfigurations().DeleteCollection(a.ctx, deleteOptions, listOptions) 1363 if client.IgnoreNotFound(err) != nil { 1364 log.WithError(err).Error("Deleting ValidatingWebhookConfigurations on CSV delete") 1365 errs = append(errs, err) 1366 } 1367 1368 // Make sure things are deleted 1369 crbList, err := a.lister.RbacV1().ClusterRoleBindingLister().List(ownerSelector) 1370 if err != nil { 1371 errs = append(errs, err) 1372 } else if len(crbList) != 0 { 1373 errs = append(errs, fmt.Errorf("waiting for ClusterRoleBindings to delete")) 1374 } 1375 1376 crList, err := a.lister.RbacV1().ClusterRoleLister().List(ownerSelector) 1377 if err != nil { 1378 errs = append(errs, err) 1379 } else if len(crList) != 0 { 1380 errs = append(errs, fmt.Errorf("waiting for ClusterRoles to delete")) 1381 } 1382 1383 // Return any errors 1384 if err := utilerrors.NewAggregate(errs); err != nil { 1385 return err, false 1386 } 1387 1388 // If no errors, remove our finalizer from the CSV and update 1389 controllerutil.RemoveFinalizer(csv, myFinalizerName) 1390 _, err = a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Update(a.ctx, csv, metav1.UpdateOptions{}) 1391 if err != nil { 1392 log.WithError(err).Error("Removing finalizer") 1393 return err, false 1394 } 1395 1396 // Stop reconciliation as the csv is being deleted 1397 return nil, false 1398 } 1399 1400 // syncClusterServiceVersion is the method that gets called when we see a CSV event in the cluster 1401 func (a *Operator) syncClusterServiceVersion(obj interface{}) (syncError error) { 1402 clusterServiceVersion, ok := obj.(*v1alpha1.ClusterServiceVersion) 1403 if !ok { 1404 a.logger.Debugf("wrong type: %#v", obj) 1405 return fmt.Errorf("casting ClusterServiceVersion failed") 1406 } 1407 1408 logger := a.logger.WithFields(logrus.Fields{ 1409 "id": queueinformer.NewLoopID(), 1410 "csv": clusterServiceVersion.GetName(), 1411 "namespace": clusterServiceVersion.GetNamespace(), 1412 "phase": clusterServiceVersion.Status.Phase, 1413 }) 1414 logger.Debug("start syncing CSV") 1415 defer logger.Debug("end syncing CSV") 1416 1417 // get an up-to-date clusterServiceVersion from the cache 1418 clusterServiceVersion, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(clusterServiceVersion.GetNamespace()).Get(clusterServiceVersion.GetName()) 1419 if apierrors.IsNotFound(err) { 1420 logger.Info("CSV has beeen deleted") 1421 return nil 1422 } else if err != nil { 1423 logger.Info("Error getting latest version of CSV") 1424 return err 1425 } 1426 1427 if err, ok := a.processFinalizer(clusterServiceVersion, logger); !ok { 1428 return err 1429 } 1430 1431 if a.csvNotification != nil { 1432 a.csvNotification.OnAddOrUpdate(clusterServiceVersion) 1433 } 1434 1435 if clusterServiceVersion.IsCopied() { 1436 logger.Warning("skipping copied csv transition") // should not happen? 1437 return 1438 } 1439 1440 outCSV, syncError := a.transitionCSVState(*clusterServiceVersion) 1441 1442 if outCSV == nil { 1443 return 1444 } 1445 1446 // status changed, update CSV 1447 if !(outCSV.Status.LastUpdateTime.Equal(clusterServiceVersion.Status.LastUpdateTime) && 1448 outCSV.Status.Phase == clusterServiceVersion.Status.Phase && 1449 outCSV.Status.Reason == clusterServiceVersion.Status.Reason && 1450 outCSV.Status.Message == clusterServiceVersion.Status.Message) { 1451 // Update CSV with status of transition. Log errors if we can't write them to the status. 1452 _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(outCSV.GetNamespace()).UpdateStatus(context.TODO(), outCSV, metav1.UpdateOptions{}) 1453 if err != nil { 1454 updateErr := errors.New("error updating ClusterServiceVersion status: " + err.Error()) 1455 if syncError == nil { 1456 logger.Info(updateErr) 1457 syncError = updateErr 1458 } else { 1459 syncError = fmt.Errorf("error transitioning ClusterServiceVersion: %s and error updating CSV status: %s", syncError, updateErr) 1460 } 1461 } else { 1462 metrics.EmitCSVMetric(clusterServiceVersion, outCSV) 1463 } 1464 } 1465 1466 operatorGroup := a.operatorGroupFromAnnotations(logger, clusterServiceVersion) 1467 if operatorGroup == nil { 1468 logger.WithField("reason", "no operatorgroup found for active CSV").Debug("skipping potential RBAC creation in target namespaces") 1469 return 1470 } 1471 1472 if len(operatorGroup.Status.Namespaces) == 1 && operatorGroup.Status.Namespaces[0] == operatorGroup.GetNamespace() { 1473 logger.Debug("skipping copy for OwnNamespace operatorgroup") 1474 return 1475 } 1476 // Ensure operator has access to targetnamespaces with cluster RBAC 1477 // (roles/rolebindings are checked for each target namespace in syncCopyCSV) 1478 if err := a.ensureRBACInTargetNamespace(clusterServiceVersion, operatorGroup); err != nil { 1479 logger.WithError(err).Info("couldn't ensure RBAC in target namespaces") 1480 syncError = err 1481 } 1482 1483 if !outCSV.IsUncopiable() { 1484 if err := a.csvCopyQueueSet.Requeue(outCSV.GetNamespace(), outCSV.GetName()); err != nil { 1485 logger.WithError(err).Warn("unable to requeue") 1486 } 1487 } 1488 1489 logger.Debug("done syncing CSV") 1490 return 1491 } 1492 1493 func (a *Operator) allNamespaceOperatorGroups() ([]*operatorsv1.OperatorGroup, error) { 1494 operatorGroups, err := a.lister.OperatorsV1().OperatorGroupLister().List(labels.Everything()) 1495 if err != nil { 1496 return nil, err 1497 } 1498 1499 result := []*operatorsv1.OperatorGroup{} 1500 for _, operatorGroup := range operatorGroups { 1501 if NewNamespaceSet(operatorGroup.Status.Namespaces).IsAllNamespaces() { 1502 result = append(result, operatorGroup.DeepCopy()) 1503 } 1504 } 1505 return result, nil 1506 } 1507 1508 func (a *Operator) syncOLMConfig(obj interface{}) (syncError error) { 1509 a.logger.Debug("Processing olmConfig") 1510 olmConfig, ok := obj.(*operatorsv1.OLMConfig) 1511 if !ok { 1512 return fmt.Errorf("casting OLMConfig failed") 1513 } 1514 1515 // Generate an array of allNamespace OperatorGroups 1516 allNSOperatorGroups, err := a.allNamespaceOperatorGroups() 1517 if err != nil { 1518 return err 1519 } 1520 1521 nonCopiedCSVRequirement, err := labels.NewRequirement(v1alpha1.CopiedLabelKey, selection.DoesNotExist, []string{}) 1522 if err != nil { 1523 return err 1524 } 1525 1526 csvIsRequeued := false 1527 for _, og := range allNSOperatorGroups { 1528 // Get all copied CSVs owned by this operatorGroup 1529 copiedCSVRequirement, err := labels.NewRequirement(v1alpha1.CopiedLabelKey, selection.Equals, []string{og.GetNamespace()}) 1530 if err != nil { 1531 return err 1532 } 1533 1534 copiedCSVs, err := a.copiedCSVLister.List(labels.NewSelector().Add(*copiedCSVRequirement)) 1535 if err != nil { 1536 return err 1537 } 1538 1539 // Create a map that points from CSV name to a map of namespaces it is copied to 1540 // for quick lookups. 1541 copiedCSVNamespaces := map[string]map[string]struct{}{} 1542 for _, copiedCSV := range copiedCSVs { 1543 if _, ok := copiedCSVNamespaces[copiedCSV.GetName()]; !ok { 1544 copiedCSVNamespaces[copiedCSV.GetName()] = map[string]struct{}{} 1545 } 1546 copiedCSVNamespaces[copiedCSV.GetName()][copiedCSV.GetNamespace()] = struct{}{} 1547 } 1548 1549 csvs, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(og.GetNamespace()).List(labels.NewSelector().Add(*nonCopiedCSVRequirement)) 1550 if err != nil { 1551 return err 1552 } 1553 1554 namespaces, err := a.lister.CoreV1().NamespaceLister().List(labels.Everything()) 1555 if err != nil { 1556 return err 1557 } 1558 1559 copiedCSVEvaluatorFunc := getCopiedCSVEvaluatorFunc(olmConfig.CopiedCSVsAreEnabled(), namespaces, a.protectedCopiedCSVNamespaces) 1560 1561 for _, csv := range csvs { 1562 // Ignore NS where actual CSV is installed 1563 if copiedCSVEvaluatorFunc(copiedCSVNamespaces[csv.GetName()]) { 1564 continue 1565 } 1566 1567 if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { 1568 a.logger.WithError(err).Warn("unable to requeue") 1569 } 1570 csvIsRequeued = true 1571 } 1572 } 1573 1574 // Update the olmConfig status if it has changed. 1575 condition := getCopiedCSVsCondition(olmConfig.CopiedCSVsAreEnabled(), csvIsRequeued) 1576 if !isStatusConditionPresentAndAreTypeReasonMessageStatusEqual(olmConfig.Status.Conditions, condition) { 1577 meta.SetStatusCondition(&olmConfig.Status.Conditions, condition) 1578 if _, err := a.client.OperatorsV1().OLMConfigs().UpdateStatus(context.TODO(), olmConfig, metav1.UpdateOptions{}); err != nil { 1579 return err 1580 } 1581 } 1582 1583 return nil 1584 } 1585 1586 // getCopiedCSVEvaluatorFunc returns a function that evaluates if the a set of Copied CSVs exist in the expected namespaces. 1587 func getCopiedCSVEvaluatorFunc(copiedCSVsEnabled bool, namespaces []*corev1.Namespace, protectedCopiedCSVNamespaces map[string]struct{}) func(map[string]struct{}) bool { 1588 if copiedCSVsEnabled { 1589 // Exclude the namespace hosting the original CSV 1590 expectedCopiedCSVCount := -1 1591 for _, ns := range namespaces { 1592 if ns.Status.Phase == corev1.NamespaceActive { 1593 expectedCopiedCSVCount++ 1594 } 1595 } 1596 return func(m map[string]struct{}) bool { 1597 return expectedCopiedCSVCount == len(m) 1598 } 1599 } 1600 1601 // Check that Copied CSVs exist in protected namespaces. 1602 return func(m map[string]struct{}) bool { 1603 if len(protectedCopiedCSVNamespaces) != len(m) { 1604 return false 1605 } 1606 1607 for protectedNS := range protectedCopiedCSVNamespaces { 1608 if _, ok := m[protectedNS]; !ok { 1609 return false 1610 } 1611 } 1612 1613 return true 1614 } 1615 } 1616 1617 func isStatusConditionPresentAndAreTypeReasonMessageStatusEqual(conditions []metav1.Condition, condition metav1.Condition) bool { 1618 foundCondition := meta.FindStatusCondition(conditions, condition.Type) 1619 if foundCondition == nil { 1620 return false 1621 } 1622 return foundCondition.Type == condition.Type && 1623 foundCondition.Reason == condition.Reason && 1624 foundCondition.Message == condition.Message && 1625 foundCondition.Status == condition.Status 1626 } 1627 1628 func getCopiedCSVsCondition(enabled, csvIsRequeued bool) metav1.Condition { 1629 condition := metav1.Condition{ 1630 Type: operatorsv1.DisabledCopiedCSVsConditionType, 1631 LastTransitionTime: metav1.Now(), 1632 Status: metav1.ConditionFalse, 1633 } 1634 if enabled { 1635 condition.Reason = "CopiedCSVsEnabled" 1636 condition.Message = "Copied CSVs are enabled and present across the cluster" 1637 if csvIsRequeued { 1638 condition.Message = "Copied CSVs are enabled and at least one copied CSVs is missing" 1639 } 1640 return condition 1641 } 1642 1643 condition.Reason = "CopiedCSVsDisabled" 1644 if csvIsRequeued { 1645 condition.Message = "Copied CSVs are disabled and at least one unexpected copied CSV was found for an operator installed in AllNamespace mode" 1646 return condition 1647 } 1648 1649 condition.Status = metav1.ConditionTrue 1650 condition.Message = "Copied CSVs are disabled and no unexpected copied CSVs were found for operators installed in AllNamespace mode" 1651 1652 return condition 1653 } 1654 1655 func (a *Operator) syncCopyCSV(obj interface{}) (syncError error) { 1656 clusterServiceVersion, ok := obj.(*v1alpha1.ClusterServiceVersion) 1657 if !ok { 1658 a.logger.Debugf("wrong type: %#v", obj) 1659 return fmt.Errorf("casting ClusterServiceVersion failed") 1660 } 1661 1662 olmConfig, err := a.client.OperatorsV1().OLMConfigs().Get(context.TODO(), "cluster", metav1.GetOptions{}) 1663 if err != nil && !apierrors.IsNotFound(err) { 1664 return err 1665 } 1666 1667 if err == nil { 1668 go a.olmConfigQueue.AddAfter(olmConfig, time.Second*5) 1669 } 1670 1671 logger := a.logger.WithFields(logrus.Fields{ 1672 "id": queueinformer.NewLoopID(), 1673 "csv": clusterServiceVersion.GetName(), 1674 "namespace": clusterServiceVersion.GetNamespace(), 1675 "phase": clusterServiceVersion.Status.Phase, 1676 }) 1677 1678 logger.Debug("copying CSV") 1679 1680 operatorGroup := a.operatorGroupFromAnnotations(logger, clusterServiceVersion) 1681 if operatorGroup == nil { 1682 // since syncClusterServiceVersion is the only enqueuer, annotations should be present 1683 logger.WithField("reason", "no operatorgroup found for active CSV").Error("operatorgroup should have annotations") 1684 syncError = fmt.Errorf("operatorGroup for csv '%v' should have annotations", clusterServiceVersion.GetName()) 1685 return 1686 } 1687 1688 logger.WithFields(logrus.Fields{ 1689 "targetNamespaces": strings.Join(operatorGroup.Status.Namespaces, ","), 1690 }).Debug("copying csv to targets") 1691 1692 copiedCSVsAreEnabled, err := a.copiedCSVsAreEnabled() 1693 if err != nil { 1694 return err 1695 } 1696 1697 // Check if we need to do any copying / annotation for the operatorgroup 1698 namespaceSet := NewNamespaceSet(operatorGroup.Status.Namespaces) 1699 if copiedCSVsAreEnabled || !namespaceSet.IsAllNamespaces() { 1700 if err := a.ensureCSVsInNamespaces(clusterServiceVersion, operatorGroup, namespaceSet); err != nil { 1701 logger.WithError(err).Info("couldn't copy CSV to target namespaces") 1702 syncError = err 1703 } 1704 1705 // If the CSV was installed in AllNamespace mode, remove any "CSV Copying Disabled" events 1706 // in which the related object's name, namespace, and uid match the given CSV's. 1707 if namespaceSet.IsAllNamespaces() { 1708 if err := a.deleteCSVCopyingDisabledEvent(clusterServiceVersion); err != nil { 1709 return err 1710 } 1711 } 1712 return 1713 } 1714 1715 requirement, err := labels.NewRequirement(v1alpha1.CopiedLabelKey, selection.Equals, []string{clusterServiceVersion.Namespace}) 1716 if err != nil { 1717 return err 1718 } 1719 1720 copiedCSVs, err := a.copiedCSVLister.List(labels.NewSelector().Add(*requirement)) 1721 if err != nil { 1722 return err 1723 } 1724 1725 // Ensure that the Copied CSVs exist in the protected namespaces. 1726 protectedNamespaces := []string{} 1727 for ns := range a.protectedCopiedCSVNamespaces { 1728 if ns == clusterServiceVersion.GetNamespace() { 1729 continue 1730 } 1731 protectedNamespaces = append(protectedNamespaces, ns) 1732 } 1733 1734 if err := a.ensureCSVsInNamespaces(clusterServiceVersion, operatorGroup, NewNamespaceSet(protectedNamespaces)); err != nil { 1735 return err 1736 } 1737 1738 // Delete Copied CSVs in namespaces that are not protected. 1739 for _, copiedCSV := range copiedCSVs { 1740 if _, ok := a.protectedCopiedCSVNamespaces[copiedCSV.Namespace]; ok { 1741 continue 1742 } 1743 err := a.client.OperatorsV1alpha1().ClusterServiceVersions(copiedCSV.Namespace).Delete(context.TODO(), copiedCSV.Name, metav1.DeleteOptions{}) 1744 if err != nil && !apierrors.IsNotFound(err) { 1745 return err 1746 } 1747 } 1748 1749 if err := a.createCSVCopyingDisabledEvent(clusterServiceVersion); err != nil { 1750 return err 1751 } 1752 1753 return 1754 } 1755 1756 // copiedCSVsAreEnabled determines if csv copying is enabled for OLM. 1757 // 1758 // This method will first attempt to get the "cluster" olmConfig resource, 1759 // if any error other than "IsNotFound" is encountered, false and the error 1760 // will be returned. 1761 // 1762 // If the "cluster" olmConfig resource is found, the value of 1763 // olmConfig.spec.features.disableCopiedCSVs will be returned along with a 1764 // nil error. 1765 // 1766 // If the "cluster" olmConfig resource is not found, true will be returned 1767 // without an error. 1768 func (a *Operator) copiedCSVsAreEnabled() (bool, error) { 1769 olmConfig, err := a.client.OperatorsV1().OLMConfigs().Get(context.TODO(), "cluster", metav1.GetOptions{}) 1770 if err != nil { 1771 // Default to true if olmConfig singleton cannot be found 1772 if apierrors.IsNotFound(err) { 1773 return true, nil 1774 } 1775 // If there was an error that wasn't an IsNotFound, return the error 1776 return false, err 1777 } 1778 1779 // If there was no error, return value based on olmConfig singleton 1780 return olmConfig.CopiedCSVsAreEnabled(), nil 1781 } 1782 1783 func (a *Operator) getCopiedCSVDisabledEventsForCSV(csv *v1alpha1.ClusterServiceVersion) ([]corev1.Event, error) { 1784 result := []corev1.Event{} 1785 if csv == nil { 1786 return result, nil 1787 } 1788 1789 events, err := a.opClient.KubernetesInterface().CoreV1().Events(csv.GetNamespace()).List(context.TODO(), metav1.ListOptions{}) 1790 if err != nil { 1791 return nil, err 1792 } 1793 1794 for _, event := range events.Items { 1795 if event.InvolvedObject.Namespace == csv.GetNamespace() && 1796 event.InvolvedObject.Name == csv.GetName() && 1797 event.InvolvedObject.UID == csv.GetUID() && 1798 event.Reason == operatorsv1.DisabledCopiedCSVsConditionType { 1799 result = append(result, *event.DeepCopy()) 1800 } 1801 } 1802 1803 return result, nil 1804 } 1805 1806 func (a *Operator) deleteCSVCopyingDisabledEvent(csv *v1alpha1.ClusterServiceVersion) error { 1807 events, err := a.getCopiedCSVDisabledEventsForCSV(csv) 1808 if err != nil { 1809 return err 1810 } 1811 1812 // Remove existing events. 1813 return a.deleteEvents(events) 1814 } 1815 1816 func (a *Operator) deleteEvents(events []corev1.Event) error { 1817 for _, event := range events { 1818 err := a.opClient.KubernetesInterface().EventsV1().Events(event.GetNamespace()).Delete(context.TODO(), event.GetName(), metav1.DeleteOptions{}) 1819 if err != nil && !apierrors.IsNotFound(err) { 1820 return err 1821 } 1822 } 1823 return nil 1824 } 1825 1826 func (a *Operator) createCSVCopyingDisabledEvent(csv *v1alpha1.ClusterServiceVersion) error { 1827 events, err := a.getCopiedCSVDisabledEventsForCSV(csv) 1828 if err != nil { 1829 return err 1830 } 1831 1832 if len(events) == 1 { 1833 return nil 1834 } 1835 1836 // Remove existing events. 1837 if len(events) > 1 { 1838 if err := a.deleteEvents(events); err != nil { 1839 return err 1840 } 1841 } 1842 1843 a.recorder.Eventf(csv, corev1.EventTypeWarning, operatorsv1.DisabledCopiedCSVsConditionType, "CSV copying disabled for %s/%s", csv.GetNamespace(), csv.GetName()) 1844 1845 return nil 1846 } 1847 1848 func (a *Operator) syncGcCsv(obj interface{}) (syncError error) { 1849 clusterServiceVersion, ok := obj.(*metav1.PartialObjectMetadata) 1850 if !ok { 1851 a.logger.Debugf("wrong type: %#v", obj) 1852 return fmt.Errorf("casting ClusterServiceVersion failed") 1853 } 1854 if v1alpha1.IsCopied(clusterServiceVersion) { 1855 syncError = a.removeDanglingChildCSVs(clusterServiceVersion) 1856 return 1857 } 1858 return 1859 } 1860 1861 // operatorGroupFromAnnotations returns the OperatorGroup for the CSV only if the CSV is active one in the group 1862 func (a *Operator) operatorGroupFromAnnotations(logger *logrus.Entry, csv *v1alpha1.ClusterServiceVersion) *operatorsv1.OperatorGroup { 1863 annotations := csv.GetAnnotations() 1864 1865 // Not part of a group yet 1866 if annotations == nil { 1867 logger.Info("not part of any operatorgroup, no annotations") 1868 return nil 1869 } 1870 1871 // Not in the OperatorGroup namespace 1872 if annotations[operatorsv1.OperatorGroupNamespaceAnnotationKey] != csv.GetNamespace() { 1873 logger.Info("not in operatorgroup namespace") 1874 return nil 1875 } 1876 1877 operatorGroupName, ok := annotations[operatorsv1.OperatorGroupAnnotationKey] 1878 1879 // No OperatorGroup annotation 1880 if !ok { 1881 logger.Info("no olm.operatorGroup annotation") 1882 return nil 1883 } 1884 1885 logger = logger.WithField("operatorgroup", operatorGroupName) 1886 1887 operatorGroup, err := a.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(csv.GetNamespace()).Get(operatorGroupName) 1888 // OperatorGroup not found 1889 if err != nil { 1890 logger.Info("operatorgroup not found") 1891 return nil 1892 } 1893 1894 targets, ok := annotations[operatorsv1.OperatorGroupTargetsAnnotationKey] 1895 1896 // No target annotation 1897 if !ok { 1898 logger.Info("no olm.targetNamespaces annotation") 1899 return nil 1900 } 1901 1902 // Target namespaces don't match 1903 if targets != operatorGroup.BuildTargetNamespaces() { 1904 logger.Info("olm.targetNamespaces annotation doesn't match operatorgroup status") 1905 return nil 1906 } 1907 1908 return operatorGroup.DeepCopy() 1909 } 1910 1911 func (a *Operator) operatorGroupForCSV(csv *v1alpha1.ClusterServiceVersion, logger *logrus.Entry) (*operatorsv1.OperatorGroup, error) { 1912 now := a.now() 1913 1914 // Attempt to associate an OperatorGroup with the CSV. 1915 operatorGroups, err := a.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(csv.GetNamespace()).List(labels.Everything()) 1916 if err != nil { 1917 logger.Errorf("error occurred while attempting to associate csv with operatorgroup") 1918 return nil, err 1919 } 1920 var operatorGroup *operatorsv1.OperatorGroup 1921 1922 switch len(operatorGroups) { 1923 case 0: 1924 err = fmt.Errorf("csv in namespace with no operatorgroups") 1925 logger.Warn(err) 1926 csv.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonNoOperatorGroup, err.Error(), now, a.recorder) 1927 return nil, err 1928 case 1: 1929 operatorGroup = operatorGroups[0] 1930 logger = logger.WithField("opgroup", operatorGroup.GetName()) 1931 if a.operatorGroupAnnotationsDiffer(&csv.ObjectMeta, operatorGroup) { 1932 a.setOperatorGroupAnnotations(&csv.ObjectMeta, operatorGroup, true) 1933 if _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Update(context.TODO(), csv, metav1.UpdateOptions{}); err != nil { 1934 logger.WithError(err).Warn("error adding operatorgroup annotations") 1935 return nil, err 1936 } 1937 if targetNamespaceList, err := a.getOperatorGroupTargets(operatorGroup); err == nil && len(targetNamespaceList) == 0 { 1938 csv.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonNoTargetNamespaces, "no targetNamespaces are matched operatorgroups namespace selection", now, a.recorder) 1939 } 1940 logger.Debug("CSV not in operatorgroup, requeuing operator group") 1941 // this requeue helps when an operator group has not annotated a CSV due to a permissions error 1942 // but the permissions issue has now been resolved 1943 if err := a.ogQueueSet.Requeue(operatorGroup.GetNamespace(), operatorGroup.GetName()); err != nil { 1944 return nil, err 1945 } 1946 return nil, nil 1947 } 1948 logger.Debug("csv in operatorgroup") 1949 return operatorGroup.DeepCopy(), nil 1950 default: 1951 err = fmt.Errorf("csv created in namespace with multiple operatorgroups, can't pick one automatically") 1952 logger.WithError(err).Warn("csv failed to become an operatorgroup member") 1953 if csv.Status.Reason != v1alpha1.CSVReasonTooManyOperatorGroups { 1954 csv.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonTooManyOperatorGroups, err.Error(), now, a.recorder) 1955 } 1956 return nil, err 1957 } 1958 } 1959 1960 // transitionCSVState moves the CSV status state machine along based on the current value and the current cluster state. 1961 // SyncError should be returned when an additional reconcile of the CSV might fix the issue. 1962 func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v1alpha1.ClusterServiceVersion, syncError error) { 1963 logger := a.logger.WithFields(logrus.Fields{ 1964 "id": queueinformer.NewLoopID(), 1965 "csv": in.GetName(), 1966 "namespace": in.GetNamespace(), 1967 "phase": in.Status.Phase, 1968 }) 1969 1970 if in.Status.Reason == v1alpha1.CSVReasonComponentFailedNoRetry { 1971 // will change phase out of failed in the event of an intentional requeue 1972 logger.Debugf("skipping sync for CSV in failed-no-retry state") 1973 return 1974 } 1975 1976 out = in.DeepCopy() 1977 now := a.now() 1978 1979 operatorSurface, err := apiSurfaceOfCSV(out) 1980 if err != nil { 1981 // If the resolver is unable to retrieve the operator info from the CSV the CSV requires changes, a syncError should not be returned. 1982 logger.WithError(err).Warn("Unable to retrieve operator information from CSV") 1983 return 1984 } 1985 1986 // Ensure required and provided API labels 1987 if labelSets, err := a.apiLabeler.LabelSetsFor(operatorSurface); err != nil { 1988 logger.WithError(err).Warn("couldn't create label set") 1989 } else if len(labelSets) > 0 { 1990 updated, err := a.ensureLabels(out, labelSets...) 1991 if err != nil { 1992 logger.WithError(err).Warn("issue ensuring csv api labels") 1993 syncError = err 1994 return 1995 } 1996 // Update the underlying value of out to preserve changes 1997 *out = *updated 1998 } 1999 2000 // Verify CSV operatorgroup (and update annotations if needed) 2001 operatorGroup, err := a.operatorGroupForCSV(out, logger) 2002 if operatorGroup == nil { 2003 // when err is nil, we still want to exit, but we don't want to re-add the csv ratelimited to the queue 2004 syncError = err 2005 logger.WithError(err).Info("operatorgroup incorrect") 2006 return 2007 } 2008 2009 modeSet, err := v1alpha1.NewInstallModeSet(out.Spec.InstallModes) 2010 if err != nil { 2011 logger.WithError(err).Warn("csv has invalid installmodes") 2012 out.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInvalidInstallModes, err.Error(), now, a.recorder) 2013 return 2014 } 2015 2016 // Check if the CSV supports its operatorgroup's selected namespaces 2017 targets, ok := out.GetAnnotations()[operatorsv1.OperatorGroupTargetsAnnotationKey] 2018 if ok { 2019 namespaces := strings.Split(targets, ",") 2020 2021 if err := modeSet.Supports(out.GetNamespace(), namespaces); err != nil { 2022 logger.WithField("reason", err.Error()).Info("installmodeset does not support operatorgroups namespace selection") 2023 out.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonUnsupportedOperatorGroup, err.Error(), now, a.recorder) 2024 return 2025 } 2026 } else { 2027 logger.Info("csv missing olm.targetNamespaces annotation") 2028 out.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonNoTargetNamespaces, "csv missing olm.targetNamespaces annotation", now, a.recorder) 2029 return 2030 } 2031 2032 // Check for intersecting provided APIs in intersecting OperatorGroups 2033 allGroups, err := a.lister.OperatorsV1().OperatorGroupLister().List(labels.Everything()) 2034 if err != nil { 2035 logger.WithError(err).Warn("failed to list operatorgroups") 2036 return 2037 } 2038 otherGroups := make([]operatorsv1.OperatorGroup, 0, len(allGroups)) 2039 for _, g := range allGroups { 2040 if g.GetName() != operatorGroup.GetName() || g.GetNamespace() != operatorGroup.GetNamespace() { 2041 otherGroups = append(otherGroups, *g) 2042 } 2043 } 2044 2045 groupSurface := NewOperatorGroup(operatorGroup) 2046 otherGroupSurfaces := NewOperatorGroupSurfaces(otherGroups...) 2047 providedAPIs := operatorSurface.ProvidedAPIs.StripPlural() 2048 2049 switch result := a.apiReconciler.Reconcile(providedAPIs, groupSurface, otherGroupSurfaces...); { 2050 case operatorGroup.Spec.StaticProvidedAPIs && (result == AddAPIs || result == RemoveAPIs): 2051 // Transition the CSV to FAILED with status reason "CannotModifyStaticOperatorGroupProvidedAPIs" 2052 if out.Status.Reason != v1alpha1.CSVReasonInterOperatorGroupOwnerConflict { 2053 logger.WithField("apis", providedAPIs).Warn("cannot modify provided apis of static provided api operatorgroup") 2054 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs, "static provided api operatorgroup cannot be modified by these apis", now, a.recorder) 2055 a.cleanupCSVDeployments(logger, out) 2056 } 2057 return 2058 case result == APIConflict: 2059 // Transition the CSV to FAILED with status reason "InterOperatorGroupOwnerConflict" 2060 if out.Status.Reason != v1alpha1.CSVReasonInterOperatorGroupOwnerConflict { 2061 logger.WithField("apis", providedAPIs).Warn("intersecting operatorgroups provide the same apis") 2062 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInterOperatorGroupOwnerConflict, "intersecting operatorgroups provide the same apis", now, a.recorder) 2063 a.cleanupCSVDeployments(logger, out) 2064 } 2065 return 2066 case result == AddAPIs: 2067 // Add the CSV's provided APIs to its OperatorGroup's annotation 2068 logger.WithField("apis", providedAPIs).Debug("adding csv provided apis to operatorgroup") 2069 union := groupSurface.ProvidedAPIs().Union(providedAPIs) 2070 unionedAnnotations := operatorGroup.GetAnnotations() 2071 if unionedAnnotations == nil { 2072 unionedAnnotations = make(map[string]string) 2073 } 2074 if unionedAnnotations[operatorsv1.OperatorGroupProvidedAPIsAnnotationKey] == union.String() { 2075 // resolver may think apis need adding with invalid input, so continue when there's no work 2076 // to be done so that the CSV can progress far enough to get requirements checked 2077 a.logger.Debug("operator group annotations up to date, continuing") 2078 break 2079 } 2080 unionedAnnotations[operatorsv1.OperatorGroupProvidedAPIsAnnotationKey] = union.String() 2081 operatorGroup.SetAnnotations(unionedAnnotations) 2082 if _, err := a.client.OperatorsV1().OperatorGroups(operatorGroup.GetNamespace()).Update(context.TODO(), operatorGroup, metav1.UpdateOptions{}); err != nil && !apierrors.IsNotFound(err) { 2083 syncError = fmt.Errorf("could not update operatorgroups %s annotation: %v", operatorsv1.OperatorGroupProvidedAPIsAnnotationKey, err) 2084 } 2085 if err := a.csvQueueSet.Requeue(out.GetNamespace(), out.GetName()); err != nil { 2086 a.logger.WithError(err).Warn("unable to requeue") 2087 } 2088 return 2089 case result == RemoveAPIs: 2090 // Remove the CSV's provided APIs from its OperatorGroup's annotation 2091 logger.WithField("apis", providedAPIs).Debug("removing csv provided apis from operatorgroup") 2092 difference := groupSurface.ProvidedAPIs().Difference(providedAPIs) 2093 if diffedAnnotations := operatorGroup.GetAnnotations(); diffedAnnotations != nil { 2094 diffedAnnotations[operatorsv1.OperatorGroupProvidedAPIsAnnotationKey] = difference.String() 2095 operatorGroup.SetAnnotations(diffedAnnotations) 2096 if _, err := a.client.OperatorsV1().OperatorGroups(operatorGroup.GetNamespace()).Update(context.TODO(), operatorGroup, metav1.UpdateOptions{}); err != nil && !apierrors.IsNotFound(err) { 2097 syncError = fmt.Errorf("could not update operatorgroups %s annotation: %v", operatorsv1.OperatorGroupProvidedAPIsAnnotationKey, err) 2098 } 2099 } 2100 if err := a.csvQueueSet.Requeue(out.GetNamespace(), out.GetName()); err != nil { 2101 a.logger.WithError(err).Warn("unable to requeue") 2102 } 2103 return 2104 default: 2105 logger.WithField("apis", providedAPIs).Debug("no intersecting operatorgroups provide the same apis") 2106 } 2107 2108 switch out.Status.Phase { 2109 case v1alpha1.CSVPhaseNone: 2110 logger.Info("scheduling ClusterServiceVersion for requirement verification") 2111 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonRequirementsUnknown, "requirements not yet checked", now, a.recorder) 2112 case v1alpha1.CSVPhasePending: 2113 // Check previous version's Upgradeable condition 2114 replacedCSV := a.isReplacing(out) 2115 if replacedCSV != nil { 2116 operatorUpgradeable, condErr := a.isOperatorUpgradeable(replacedCSV) 2117 if !operatorUpgradeable { 2118 out.SetPhaseWithEventIfChanged(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonOperatorConditionNotUpgradeable, fmt.Sprintf("operator is not upgradeable: %s", condErr), now, a.recorder) 2119 return 2120 } 2121 } 2122 met, statuses, err := a.requirementAndPermissionStatus(out) 2123 if err != nil { 2124 // TODO: account for Bad Rule as well 2125 logger.Info("invalid install strategy") 2126 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInvalidStrategy, fmt.Sprintf("install strategy invalid: %s", err.Error()), now, a.recorder) 2127 return 2128 } 2129 out.SetRequirementStatus(statuses) 2130 2131 // Check if we need to requeue the previous 2132 if prev := a.isReplacing(out); prev != nil { 2133 if prev.Status.Phase == v1alpha1.CSVPhaseSucceeded { 2134 if err := a.csvQueueSet.Requeue(prev.GetNamespace(), prev.GetName()); err != nil { 2135 a.logger.WithError(err).Warn("error requeueing previous") 2136 } 2137 } 2138 } 2139 2140 if !met { 2141 logger.Info("requirements were not met") 2142 out.SetPhaseWithEventIfChanged(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonRequirementsNotMet, "one or more requirements couldn't be found", now, a.recorder) 2143 syncError = ErrRequirementsNotMet 2144 return 2145 } 2146 2147 // Create a map to track unique names 2148 webhookNames := map[string]struct{}{} 2149 // Check if Webhooks have valid rules and unique names 2150 // TODO: Move this to validating library 2151 for _, desc := range out.Spec.WebhookDefinitions { 2152 _, present := webhookNames[desc.GenerateName] 2153 if present { 2154 logger.WithError(fmt.Errorf("repeated WebhookDescription name %s", desc.GenerateName)).Warn("CSV is invalid") 2155 out.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInvalidWebhookDescription, "CSV contains repeated WebhookDescription name", now, a.recorder) 2156 return 2157 } 2158 webhookNames[desc.GenerateName] = struct{}{} 2159 if err = install.ValidWebhookRules(desc.Rules); err != nil { 2160 logger.WithError(err).Warnf("WebhookDescription %s includes invalid rules", desc.GenerateName) 2161 out.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInvalidWebhookDescription, err.Error(), now, a.recorder) 2162 return 2163 } 2164 } 2165 2166 // Check for CRD ownership conflicts 2167 if syncError = a.crdOwnerConflicts(out, a.csvSet(out.GetNamespace(), v1alpha1.CSVPhaseAny)); syncError != nil { 2168 if syncError == ErrCRDOwnerConflict { 2169 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonOwnerConflict, syncError.Error(), now, a.recorder) 2170 } 2171 return 2172 } 2173 2174 // Check for APIServices ownership conflicts 2175 if syncError = a.apiServiceOwnerConflicts(out); syncError != nil { 2176 if syncError == ErrAPIServiceOwnerConflict { 2177 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonOwnerConflict, syncError.Error(), now, a.recorder) 2178 } 2179 return 2180 } 2181 2182 // Check if we're not ready to install part of the replacement chain yet 2183 if prev := a.isReplacing(out); prev != nil { 2184 if prev.Status.Phase != v1alpha1.CSVPhaseReplacing { 2185 logger.WithError(fmt.Errorf("CSV being replaced is in phase %s instead of %s", prev.Status.Phase, v1alpha1.CSVPhaseReplacing)).Warn("Unable to replace previous CSV") 2186 return 2187 } 2188 } 2189 2190 logger.Info("scheduling ClusterServiceVersion for install") 2191 out.SetPhaseWithEvent(v1alpha1.CSVPhaseInstallReady, v1alpha1.CSVReasonRequirementsMet, "all requirements found, attempting install", now, a.recorder) 2192 case v1alpha1.CSVPhaseInstallReady: 2193 installer, strategy := a.parseStrategiesAndUpdateStatus(out) 2194 if strategy == nil { 2195 return 2196 } 2197 2198 if syncError = installer.Install(strategy); syncError != nil { 2199 if install.IsErrorUnrecoverable(syncError) { 2200 logger.Infof("Setting CSV reason to failed without retry: %v", syncError) 2201 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonComponentFailedNoRetry, fmt.Sprintf("install strategy failed: %s", syncError), now, a.recorder) 2202 return 2203 } 2204 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonComponentFailed, fmt.Sprintf("install strategy failed: %s", syncError), now, a.recorder) 2205 return 2206 } 2207 2208 if installer.CertsRotated() { 2209 now := metav1.Now() 2210 rotateTime := metav1.NewTime(installer.CertsRotateAt()) 2211 out.Status.CertsLastUpdated = &now 2212 out.Status.CertsRotateAt = &rotateTime 2213 } 2214 2215 out.SetPhaseWithEvent(v1alpha1.CSVPhaseInstalling, v1alpha1.CSVReasonInstallSuccessful, "waiting for install components to report healthy", now, a.recorder) 2216 err := a.csvQueueSet.Requeue(out.GetNamespace(), out.GetName()) 2217 if err != nil { 2218 a.logger.Warn(err.Error()) 2219 } 2220 return 2221 2222 case v1alpha1.CSVPhaseInstalling: 2223 installer, strategy := a.parseStrategiesAndUpdateStatus(out) 2224 if strategy == nil { 2225 return 2226 } 2227 2228 strategy, err := a.updateDeploymentSpecsWithAPIServiceData(out, strategy) 2229 if err != nil { 2230 logger.WithError(err).Debug("Unable to calculate expected deployment") 2231 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonNeedsReinstall, "calculated deployment install is bad", now, a.recorder) 2232 return 2233 } 2234 if installErr := a.updateInstallStatus(out, installer, strategy, v1alpha1.CSVPhaseInstalling, v1alpha1.CSVReasonWaiting); installErr != nil { 2235 // Re-sync if kube-apiserver was unavailable 2236 if apierrors.IsServiceUnavailable(installErr) { 2237 logger.WithError(installErr).Info("could not update install status") 2238 syncError = installErr 2239 return 2240 } 2241 // Set phase to failed if it's been a long time since the last transition (5 minutes) 2242 if out.Status.LastTransitionTime != nil && a.now().Sub(out.Status.LastTransitionTime.Time) >= 5*time.Minute { 2243 logger.Warn("install timed out") 2244 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInstallCheckFailed, "install timeout", now, a.recorder) 2245 return 2246 } 2247 } 2248 logger.WithField("strategy", out.Spec.InstallStrategy.StrategyName).Infof("install strategy successful") 2249 2250 case v1alpha1.CSVPhaseSucceeded: 2251 // Check if the current CSV is being replaced, return with replacing status if so 2252 if err := a.checkReplacementsAndUpdateStatus(out); err != nil { 2253 logger.WithError(err).Info("replacement check") 2254 return 2255 } 2256 2257 installer, strategy := a.parseStrategiesAndUpdateStatus(out) 2258 if strategy == nil { 2259 return 2260 } 2261 2262 // Check if any generated resources are missing 2263 if err := a.checkAPIServiceResources(out, certs.PEMSHA256); err != nil { 2264 logger.WithError(err).Debug("API Resources are unavailable") 2265 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonAPIServiceResourceIssue, err.Error(), now, a.recorder) 2266 return 2267 } 2268 2269 // Check if it's time to refresh owned APIService certs 2270 if shouldRotate, err := installer.ShouldRotateCerts(strategy); err != nil { 2271 logger.WithError(err).Info("cert validity check") 2272 return 2273 } else if shouldRotate { 2274 logger.Debug("CSV owns resources that require a cert refresh") 2275 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonNeedsCertRotation, "CSV owns resources that require a cert refresh", now, a.recorder) 2276 return 2277 } 2278 2279 // Ensure requirements are still present 2280 met, statuses, err := a.requirementAndPermissionStatus(out) 2281 if err != nil { 2282 logger.Info("invalid install strategy") 2283 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInvalidStrategy, fmt.Sprintf("install strategy invalid: %s", err.Error()), now, a.recorder) 2284 return 2285 } else if !met { 2286 logger.Debug("CSV Requirements are no longer met") 2287 out.SetRequirementStatus(statuses) 2288 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonRequirementsNotMet, "requirements no longer met", now, a.recorder) 2289 return 2290 } 2291 2292 // Check install status 2293 strategy, err = a.updateDeploymentSpecsWithAPIServiceData(out, strategy) 2294 if err != nil { 2295 logger.WithError(err).Debug("Unable to calculate expected deployment") 2296 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonNeedsReinstall, "calculated deployment install is bad", now, a.recorder) 2297 return 2298 } 2299 if installErr := a.updateInstallStatus(out, installer, strategy, v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonComponentUnhealthy); installErr != nil { 2300 // Re-sync if kube-apiserver was unavailable 2301 if apierrors.IsServiceUnavailable(installErr) { 2302 logger.WithError(installErr).Info("could not update install status") 2303 syncError = installErr 2304 return 2305 } 2306 logger.WithField("strategy", out.Spec.InstallStrategy.StrategyName).Warnf("unhealthy component: %s", installErr) 2307 return 2308 } 2309 2310 // Ensure cluster roles exist for using provided apis 2311 if err := a.ensureClusterRolesForCSV(out); err != nil { 2312 logger.WithError(err).Info("couldn't ensure clusterroles for provided api types") 2313 syncError = err 2314 return 2315 } 2316 2317 case v1alpha1.CSVPhaseFailed: 2318 // Transition to the replacing phase if FailForward is enabled and a CSV exists that replaces the operator. 2319 if operatorGroup.UpgradeStrategy() == operatorsv1.UpgradeStrategyUnsafeFailForward { 2320 if replacement := a.isBeingReplaced(out, a.csvSet(out.GetNamespace(), v1alpha1.CSVPhaseAny)); replacement != nil { 2321 msg := fmt.Sprintf("Fail Forward is enabled, allowing %s csv to be replaced by csv: %s", out.Status.Phase, replacement.GetName()) 2322 out.SetPhaseWithEvent(v1alpha1.CSVPhaseReplacing, v1alpha1.CSVReasonBeingReplaced, msg, a.now(), a.recorder) 2323 metrics.CSVUpgradeCount.Inc() 2324 return 2325 } 2326 } 2327 installer, strategy := a.parseStrategiesAndUpdateStatus(out) 2328 if strategy == nil { 2329 return 2330 } 2331 2332 // Check if failed due to unsupported InstallModes 2333 if out.Status.Reason == v1alpha1.CSVReasonNoTargetNamespaces || 2334 out.Status.Reason == v1alpha1.CSVReasonNoOperatorGroup || 2335 out.Status.Reason == v1alpha1.CSVReasonTooManyOperatorGroups || 2336 out.Status.Reason == v1alpha1.CSVReasonUnsupportedOperatorGroup { 2337 logger.Info("InstallModes now support target namespaces. Transitioning to Pending...") 2338 // Check occurred before switch, safe to transition to pending 2339 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonRequirementsUnknown, "InstallModes now support target namespaces", now, a.recorder) 2340 return 2341 } 2342 2343 // Check if failed due to conflicting OperatorGroups 2344 if out.Status.Reason == v1alpha1.CSVReasonInterOperatorGroupOwnerConflict { 2345 logger.Info("OperatorGroup no longer intersecting with conflicting owner. Transitioning to Pending...") 2346 // Check occurred before switch, safe to transition to pending 2347 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonRequirementsUnknown, "OperatorGroup no longer intersecting with conflicting owner", now, a.recorder) 2348 return 2349 } 2350 2351 // Check if failed due to an attempt to modify a static OperatorGroup 2352 if out.Status.Reason == v1alpha1.CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs { 2353 logger.Info("static OperatorGroup and intersecting groups now support providedAPIs...") 2354 // Check occurred before switch, safe to transition to pending 2355 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonRequirementsUnknown, "static OperatorGroup and intersecting groups now support providedAPIs", now, a.recorder) 2356 return 2357 } 2358 2359 // Check if requirements exist 2360 met, statuses, err := a.requirementAndPermissionStatus(out) 2361 if err != nil && out.Status.Reason != v1alpha1.CSVReasonInvalidStrategy { 2362 logger.Warn("invalid install strategy") 2363 out.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInvalidStrategy, fmt.Sprintf("install strategy invalid: %s", err.Error()), now, a.recorder) 2364 return 2365 } else if !met { 2366 logger.Debug("CSV Requirements are not met") 2367 out.SetRequirementStatus(statuses) 2368 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonRequirementsNotMet, "requirements not met", now, a.recorder) 2369 return 2370 } 2371 2372 // Check if any generated resources are missing and that OLM can action on them 2373 if err := a.checkAPIServiceResources(out, certs.PEMSHA256); err != nil { 2374 if a.apiServiceResourceErrorActionable(err) { 2375 logger.WithError(err).Debug("API Resources are unavailable") 2376 // Check if API services are adoptable. If not, keep CSV as Failed state 2377 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonAPIServiceResourcesNeedReinstall, err.Error(), now, a.recorder) 2378 } 2379 return 2380 } 2381 2382 // Check if it's time to refresh owned APIService certs 2383 if shouldRotate, err := installer.ShouldRotateCerts(strategy); err != nil { 2384 logger.WithError(err).Info("cert validity check") 2385 return 2386 } else if shouldRotate { 2387 logger.Debug("CSV owns resources that require a cert refresh") 2388 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonNeedsCertRotation, "owned APIServices need cert refresh", now, a.recorder) 2389 return 2390 } 2391 2392 // Check install status 2393 strategy, err = a.updateDeploymentSpecsWithAPIServiceData(out, strategy) 2394 if err != nil { 2395 logger.WithError(err).Debug("Unable to calculate expected deployment") 2396 out.SetPhaseWithEvent(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonNeedsReinstall, "calculated deployment install is bad", now, a.recorder) 2397 return 2398 } 2399 if installErr := a.updateInstallStatus(out, installer, strategy, v1alpha1.CSVPhasePending, v1alpha1.CSVReasonNeedsReinstall); installErr != nil { 2400 // Re-sync if kube-apiserver was unavailable 2401 if apierrors.IsServiceUnavailable(installErr) { 2402 logger.WithError(installErr).Info("could not update install status") 2403 syncError = installErr 2404 return 2405 } 2406 logger.WithField("strategy", out.Spec.InstallStrategy.StrategyName).Warnf("needs reinstall: %s", installErr) 2407 } 2408 2409 case v1alpha1.CSVPhaseReplacing: 2410 // determine CSVs that are safe to delete by finding a replacement chain to a CSV that's running 2411 // since we don't know what order we'll process replacements, we have to guard against breaking that chain 2412 2413 // if this isn't the earliest csv in a replacement chain, skip gc. 2414 // marking an intermediate for deletion will break the replacement chain 2415 if prev := a.isReplacing(out); prev != nil { 2416 logger.Debugf("being replaced, but is not a leaf. skipping gc") 2417 return 2418 } 2419 2420 // If there is a succeeded replacement, mark this for deletion 2421 next := a.isBeingReplaced(out, a.csvSet(out.GetNamespace(), v1alpha1.CSVPhaseAny)) 2422 // Get the newest CSV in the replacement chain if fail forward upgrades are enabled. 2423 if operatorGroup.UpgradeStrategy() == operatorsv1.UpgradeStrategyUnsafeFailForward { 2424 csvs, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(next.GetNamespace()).List(labels.Everything()) 2425 if err != nil { 2426 syncError = err 2427 return 2428 } 2429 2430 lastCSVInChain, err := resolver.WalkReplacementChain(next, resolver.ReplacementMapping(csvs), resolver.WithUniqueCSVs()) 2431 if err != nil { 2432 syncError = err 2433 return 2434 } 2435 2436 if lastCSVInChain == nil { 2437 syncError = fmt.Errorf("fail forward upgrades enabled, unable to identify last CSV in replacement chain") 2438 return 2439 } 2440 2441 next = lastCSVInChain 2442 } 2443 if next != nil { 2444 if next.Status.Phase == v1alpha1.CSVPhaseSucceeded { 2445 out.SetPhaseWithEvent(v1alpha1.CSVPhaseDeleting, v1alpha1.CSVReasonReplaced, "has been replaced by a newer ClusterServiceVersion that has successfully installed.", now, a.recorder) 2446 } else { 2447 // If there's a replacement, but it's not yet succeeded, requeue both (this is an active replacement) 2448 if err := a.csvQueueSet.Requeue(next.GetNamespace(), next.GetName()); err != nil { 2449 a.logger.Warn(err.Error()) 2450 } 2451 if err := a.csvQueueSet.Requeue(out.GetNamespace(), out.GetName()); err != nil { 2452 a.logger.Warn(err.Error()) 2453 } 2454 } 2455 } else { 2456 syncError = fmt.Errorf("marked as replacement, but no replacement CSV found in cluster") 2457 } 2458 case v1alpha1.CSVPhaseDeleting: 2459 syncError = a.client.OperatorsV1alpha1().ClusterServiceVersions(out.GetNamespace()).Delete(context.TODO(), out.GetName(), metav1.DeleteOptions{}) 2460 if syncError != nil { 2461 logger.Debugf("unable to get delete csv marked for deletion: %s", syncError.Error()) 2462 } 2463 } 2464 2465 return 2466 } 2467 2468 // csvSet gathers all CSVs in the given namespace into a map keyed by CSV name; if metav1.NamespaceAll gets the set across all namespaces 2469 func (a *Operator) csvSet(namespace string, phase v1alpha1.ClusterServiceVersionPhase) map[string]*v1alpha1.ClusterServiceVersion { 2470 return a.csvSetGenerator.WithNamespace(namespace, phase) 2471 } 2472 2473 // checkReplacementsAndUpdateStatus returns an error if we can find a newer CSV and sets the status if so 2474 func (a *Operator) checkReplacementsAndUpdateStatus(csv *v1alpha1.ClusterServiceVersion) error { 2475 if csv.Status.Phase == v1alpha1.CSVPhaseReplacing || csv.Status.Phase == v1alpha1.CSVPhaseDeleting { 2476 return nil 2477 } 2478 if replacement := a.isBeingReplaced(csv, a.csvSet(csv.GetNamespace(), v1alpha1.CSVPhaseAny)); replacement != nil { 2479 a.logger.Infof("newer csv replacing %s, no-op", csv.GetName()) 2480 msg := fmt.Sprintf("being replaced by csv: %s", replacement.GetName()) 2481 csv.SetPhaseWithEvent(v1alpha1.CSVPhaseReplacing, v1alpha1.CSVReasonBeingReplaced, msg, a.now(), a.recorder) 2482 metrics.CSVUpgradeCount.Inc() 2483 2484 return fmt.Errorf("replacing") 2485 } 2486 return nil 2487 } 2488 2489 func (a *Operator) updateInstallStatus(csv *v1alpha1.ClusterServiceVersion, installer install.StrategyInstaller, strategy install.Strategy, requeuePhase v1alpha1.ClusterServiceVersionPhase, requeueConditionReason v1alpha1.ConditionReason) error { 2490 strategyInstalled, strategyErr := installer.CheckInstalled(strategy) 2491 now := a.now() 2492 2493 if strategyErr != nil { 2494 a.logger.WithError(strategyErr).Debug("operator not installed") 2495 } 2496 2497 apiServicesInstalled, apiServiceErr := a.areAPIServicesAvailable(csv) 2498 webhooksInstalled, webhookErr := a.areWebhooksAvailable(csv) 2499 2500 if strategyInstalled && apiServicesInstalled && webhooksInstalled { 2501 // if there's no error, we're successfully running 2502 csv.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseSucceeded, v1alpha1.CSVReasonInstallSuccessful, "install strategy completed with no errors", now, a.recorder) 2503 return nil 2504 } 2505 2506 if err := findFirstError(apierrors.IsServiceUnavailable, strategyErr, apiServiceErr, webhookErr); err != nil { 2507 return err 2508 } 2509 2510 // installcheck determined we can't progress (e.g. deployment failed to come up in time) 2511 if install.IsErrorUnrecoverable(strategyErr) { 2512 csv.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInstallCheckFailed, fmt.Sprintf("install failed: %s", strategyErr), now, a.recorder) 2513 return strategyErr 2514 } 2515 2516 if apiServiceErr != nil { 2517 csv.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonAPIServiceInstallFailed, fmt.Sprintf("APIService install failed: %s", apiServiceErr), now, a.recorder) 2518 return apiServiceErr 2519 } 2520 2521 if !apiServicesInstalled { 2522 msg := "apiServices not installed" 2523 csv.SetPhaseWithEventIfChanged(requeuePhase, requeueConditionReason, msg, now, a.recorder) 2524 if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { 2525 a.logger.Warn(err.Error()) 2526 } 2527 2528 return errors.New(msg) 2529 } 2530 2531 if webhookErr != nil { 2532 csv.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseInstallReady, requeueConditionReason, fmt.Sprintf("Webhook install failed: %s", webhookErr), now, a.recorder) 2533 return webhookErr 2534 } 2535 2536 if !webhooksInstalled { 2537 msg := "webhooks not installed" 2538 csv.SetPhaseWithEventIfChanged(requeuePhase, requeueConditionReason, msg, now, a.recorder) 2539 if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { 2540 a.logger.Warn(err.Error()) 2541 } 2542 2543 return errors.New(msg) 2544 } 2545 2546 if strategyErr != nil { 2547 reasonForError := install.ReasonForError(strategyErr) 2548 if reasonForError == install.StrategyErrDeploymentUpdated || reasonForError == install.StrategyErrReasonAnnotationsMissing { 2549 csv.SetPhaseWithEventIfChanged(v1alpha1.CSVPhaseInstallReady, requeueConditionReason, fmt.Sprintf("installing: %s", strategyErr), now, a.recorder) 2550 } else { 2551 csv.SetPhaseWithEventIfChanged(requeuePhase, requeueConditionReason, fmt.Sprintf("installing: %s", strategyErr), now, a.recorder) 2552 } 2553 if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { 2554 a.logger.Warn(err.Error()) 2555 } 2556 2557 return strategyErr 2558 } 2559 2560 return nil 2561 } 2562 2563 func findFirstError(f func(error) bool, errs ...error) error { 2564 for _, err := range errs { 2565 if f(err) { 2566 return err 2567 } 2568 } 2569 return nil 2570 } 2571 2572 // parseStrategiesAndUpdateStatus returns a StrategyInstaller and a Strategy for a CSV if it can, else it sets a status on the CSV and returns 2573 func (a *Operator) parseStrategiesAndUpdateStatus(csv *v1alpha1.ClusterServiceVersion) (install.StrategyInstaller, install.Strategy) { 2574 strategy, err := a.resolver.UnmarshalStrategy(csv.Spec.InstallStrategy) 2575 if err != nil { 2576 csv.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonInvalidStrategy, fmt.Sprintf("install strategy invalid: %s", err), a.now(), a.recorder) 2577 return nil, nil 2578 } 2579 2580 previousCSV := a.isReplacing(csv) 2581 var previousStrategy install.Strategy 2582 if previousCSV != nil { 2583 err = a.csvQueueSet.Requeue(previousCSV.Namespace, previousCSV.Name) 2584 if err != nil { 2585 a.logger.Warn(err.Error()) 2586 } 2587 2588 previousStrategy, err = a.resolver.UnmarshalStrategy(previousCSV.Spec.InstallStrategy) 2589 if err != nil { 2590 previousStrategy = nil 2591 } 2592 } 2593 2594 // If an admin has specified a service account to the operator group 2595 // associated with the namespace then we should use a scoped client that is 2596 // bound to the service account. 2597 querierFunc := a.serviceAccountQuerier.NamespaceQuerier(csv.GetNamespace()) 2598 attenuate, err := a.clientAttenuator.AttenuateToServiceAccount(querierFunc) 2599 if err != nil { 2600 a.logger.Errorf("failed to get a client for operator deployment - %v", err) 2601 return nil, nil 2602 } 2603 kubeclient, err := a.clientFactory.WithConfigTransformer(attenuate).NewOperatorClient() 2604 if err != nil { 2605 a.logger.Errorf("failed to get an operator client for operator deployment - %v", err) 2606 return nil, nil 2607 } 2608 2609 strName := strategy.GetStrategyName() 2610 installer := a.resolver.InstallerForStrategy(strName, kubeclient, a.lister, csv, csv.GetAnnotations(), csv.GetAllAPIServiceDescriptions(), csv.Spec.WebhookDefinitions, previousStrategy) 2611 return installer, strategy 2612 } 2613 2614 func (a *Operator) crdOwnerConflicts(in *v1alpha1.ClusterServiceVersion, csvsInNamespace map[string]*v1alpha1.ClusterServiceVersion) error { 2615 csvsInChain := a.getReplacementChain(in, csvsInNamespace) 2616 // find csvs in the namespace that are not part of the replacement chain 2617 for name, csv := range csvsInNamespace { 2618 if _, ok := csvsInChain[name]; ok { 2619 continue 2620 } 2621 for _, crd := range in.Spec.CustomResourceDefinitions.Owned { 2622 if name != in.GetName() && csv.OwnsCRD(crd.Name) { 2623 return ErrCRDOwnerConflict 2624 } 2625 } 2626 } 2627 2628 return nil 2629 } 2630 2631 func (a *Operator) getReplacementChain(in *v1alpha1.ClusterServiceVersion, csvsInNamespace map[string]*v1alpha1.ClusterServiceVersion) map[string]struct{} { 2632 current := in.GetName() 2633 csvsInChain := map[string]struct{}{ 2634 current: {}, 2635 } 2636 2637 replacement := func(csvName string) *string { 2638 for _, csv := range csvsInNamespace { 2639 if csv.Spec.Replaces == csvName { 2640 name := csv.GetName() 2641 return &name 2642 } 2643 } 2644 return nil 2645 } 2646 2647 replaces := func(replaces string) *string { 2648 for _, csv := range csvsInNamespace { 2649 name := csv.GetName() 2650 if name == replaces { 2651 rep := csv.Spec.Replaces 2652 return &rep 2653 } 2654 } 2655 return nil 2656 } 2657 2658 next := replacement(current) 2659 for next != nil { 2660 if _, ok := csvsInChain[*next]; ok { 2661 break // cycle 2662 } 2663 csvsInChain[*next] = struct{}{} 2664 current = *next 2665 next = replacement(current) 2666 } 2667 2668 current = in.Spec.Replaces 2669 prev := replaces(current) 2670 if prev != nil { 2671 csvsInChain[current] = struct{}{} 2672 } 2673 for prev != nil && *prev != "" { 2674 if _, ok := csvsInChain[*prev]; ok { 2675 break // cycle 2676 } 2677 current = *prev 2678 csvsInChain[current] = struct{}{} 2679 prev = replaces(current) 2680 } 2681 return csvsInChain 2682 } 2683 2684 func (a *Operator) apiServiceOwnerConflicts(csv *v1alpha1.ClusterServiceVersion) error { 2685 for _, desc := range csv.GetOwnedAPIServiceDescriptions() { 2686 // Check if the APIService exists 2687 apiService, err := a.lister.APIRegistrationV1().APIServiceLister().Get(desc.GetName()) 2688 if err != nil && !apierrors.IsNotFound(err) && !apierrors.IsGone(err) { 2689 return err 2690 } 2691 2692 if apiService == nil { 2693 continue 2694 } 2695 2696 adoptable, err := install.IsAPIServiceAdoptable(a.lister, csv, apiService) 2697 if err != nil { 2698 a.logger.WithFields(logrus.Fields{"obj": "apiService", "labels": apiService.GetLabels()}).Errorf("adoption check failed - %v", err) 2699 } 2700 2701 if !adoptable { 2702 return ErrAPIServiceOwnerConflict 2703 } 2704 } 2705 2706 return nil 2707 } 2708 2709 func (a *Operator) isBeingReplaced(in *v1alpha1.ClusterServiceVersion, csvsInNamespace map[string]*v1alpha1.ClusterServiceVersion) (replacedBy *v1alpha1.ClusterServiceVersion) { 2710 return a.csvReplaceFinder.IsBeingReplaced(in, csvsInNamespace) 2711 } 2712 2713 func (a *Operator) isReplacing(in *v1alpha1.ClusterServiceVersion) *v1alpha1.ClusterServiceVersion { 2714 return a.csvReplaceFinder.IsReplacing(in) 2715 } 2716 2717 func (a *Operator) handleDeletion(obj interface{}) { 2718 metaObj, ok := obj.(metav1.Object) 2719 if !ok { 2720 tombstone, ok := obj.(cache.DeletedFinalStateUnknown) 2721 if !ok { 2722 utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj)) 2723 return 2724 } 2725 2726 metaObj, ok = tombstone.Obj.(metav1.Object) 2727 if !ok { 2728 utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a metav1.Object %#v", obj)) 2729 return 2730 } 2731 } 2732 logger := a.logger.WithFields(logrus.Fields{ 2733 "name": metaObj.GetName(), 2734 "namespace": metaObj.GetNamespace(), 2735 "self": metaObj.GetSelfLink(), 2736 }) 2737 logger.Debug("handling resource deletion") 2738 2739 logger.Debug("requeueing owner csvs due to deletion") 2740 a.requeueOwnerCSVs(metaObj) 2741 2742 // Requeue CSVs with provided and required labels (for CRDs) 2743 if labelSets, err := a.apiLabeler.LabelSetsFor(metaObj); err != nil { 2744 logger.WithError(err).Warn("couldn't create label set") 2745 } else if len(labelSets) > 0 { 2746 logger.Debug("requeueing providing/requiring csvs due to deletion") 2747 a.requeueCSVsByLabelSet(logger, labelSets...) 2748 } 2749 } 2750 2751 func (a *Operator) requeueCSVsByLabelSet(logger *logrus.Entry, labelSets ...labels.Set) { 2752 keys, err := index.LabelIndexKeys(a.csvIndexers, labelSets...) 2753 if err != nil { 2754 logger.WithError(err).Debug("issue getting csvs by label index") 2755 return 2756 } 2757 2758 for _, key := range keys { 2759 if err := a.csvQueueSet.RequeueByKey(key); err != nil { 2760 logger.WithError(err).Debug("cannot requeue requiring/providing csv") 2761 } else { 2762 logger.WithField("key", key).Debug("csv successfully requeued on crd change") 2763 } 2764 } 2765 } 2766 2767 func (a *Operator) requeueOwnerCSVs(ownee metav1.Object) { 2768 logger := a.logger.WithFields(logrus.Fields{ 2769 "ownee": ownee.GetName(), 2770 "selflink": ownee.GetSelfLink(), 2771 "namespace": ownee.GetNamespace(), 2772 }) 2773 2774 // Attempt to requeue CSV owners in the same namespace as the object 2775 owners := ownerutil.GetOwnersByKind(ownee, v1alpha1.ClusterServiceVersionKind) 2776 if len(owners) > 0 && ownee.GetNamespace() != metav1.NamespaceAll { 2777 for _, ownerCSV := range owners { 2778 _, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ownee.GetNamespace()).Get(ownerCSV.Name) 2779 if apierrors.IsNotFound(err) { 2780 logger.Debugf("skipping requeue since CSV %v is not in cache", ownerCSV.Name) 2781 continue 2782 } 2783 // Since cross-namespace CSVs can't exist we're guaranteed the owner will be in the same namespace 2784 err = a.csvQueueSet.Requeue(ownee.GetNamespace(), ownerCSV.Name) 2785 if err != nil { 2786 logger.Warn(err.Error()) 2787 } 2788 } 2789 return 2790 } 2791 2792 // Requeue owners based on labels 2793 if name, ns, ok := ownerutil.GetOwnerByKindLabel(ownee, v1alpha1.ClusterServiceVersionKind); ok { 2794 _, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ns).Get(name) 2795 if apierrors.IsNotFound(err) { 2796 logger.Debugf("skipping requeue since CSV %v is not in cache", name) 2797 return 2798 } 2799 2800 err = a.csvQueueSet.Requeue(ns, name) 2801 if err != nil { 2802 logger.Warn(err.Error()) 2803 } 2804 } 2805 } 2806 2807 func (a *Operator) cleanupCSVDeployments(logger *logrus.Entry, csv *v1alpha1.ClusterServiceVersion) { 2808 // Extract the InstallStrategy for the deployment 2809 strategy, err := a.resolver.UnmarshalStrategy(csv.Spec.InstallStrategy) 2810 if err != nil { 2811 logger.Warn("could not parse install strategy while cleaning up CSV deployment") 2812 return 2813 } 2814 2815 // Assume the strategy is for a deployment 2816 strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) 2817 if !ok { 2818 logger.Warnf("could not cast install strategy as type %T", strategyDetailsDeployment) 2819 return 2820 } 2821 2822 // Delete deployments 2823 for _, spec := range strategyDetailsDeployment.DeploymentSpecs { 2824 logger := logger.WithField("deployment", spec.Name) 2825 logger.Debug("cleaning up CSV deployment") 2826 if err := a.opClient.DeleteDeployment(csv.GetNamespace(), spec.Name, &metav1.DeleteOptions{}); err != nil { 2827 logger.WithField("err", err).Warn("error cleaning up CSV deployment") 2828 } 2829 } 2830 } 2831 2832 // ensureLabels merges a label set with a CSV's labels and attempts to update the CSV if the merged set differs from the CSV's original labels. 2833 func (a *Operator) ensureLabels(in *v1alpha1.ClusterServiceVersion, labelSets ...labels.Set) (*v1alpha1.ClusterServiceVersion, error) { 2834 csvLabelSet := labels.Set(in.GetLabels()) 2835 merged := csvLabelSet 2836 for _, labelSet := range labelSets { 2837 merged = labels.Merge(merged, labelSet) 2838 } 2839 if labels.Equals(csvLabelSet, merged) { 2840 return in, nil 2841 } 2842 2843 logger := a.logger.WithFields(logrus.Fields{"labels": merged, "csv": in.GetName(), "ns": in.GetNamespace()}) 2844 logger.Info("updated labels") 2845 2846 out := in.DeepCopy() 2847 out.SetLabels(merged) 2848 out, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(out.GetNamespace()).Update(context.TODO(), out, metav1.UpdateOptions{}) 2849 return out, err 2850 }