github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/olm/operatorgroup.go (about) 1 package olm 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "fmt" 7 "math/big" 8 "reflect" 9 "strings" 10 11 "k8s.io/apimachinery/pkg/api/equality" 12 13 "github.com/sirupsen/logrus" 14 corev1 "k8s.io/api/core/v1" 15 rbacv1 "k8s.io/api/rbac/v1" 16 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 17 apierrors "k8s.io/apimachinery/pkg/api/errors" 18 "k8s.io/apimachinery/pkg/api/meta" 19 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 "k8s.io/apimachinery/pkg/labels" 21 "k8s.io/apimachinery/pkg/util/errors" 22 23 utillabels "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/labels" 24 25 operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" 26 "github.com/operator-framework/api/pkg/operators/v1alpha1" 27 opregistry "github.com/operator-framework/operator-registry/pkg/registry" 28 29 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 30 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators" 31 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" 32 hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash" 33 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 34 ) 35 36 const ( 37 AdminSuffix = "admin" 38 EditSuffix = "edit" 39 ViewSuffix = "view" 40 41 // kubeResourceNameLimit is the maximum length of a Kubernetes resource name 42 kubeResourceNameLimit = 253 43 44 // operatorGroupClusterRoleNameFmt template for ClusterRole names owned by OperatorGroups\ 45 // e.g. olm.og.my-group.admin-<hash> 46 operatorGroupClusterRoleNameFmt = "olm.og.%s.%s-%s" 47 ) 48 49 var ( 50 AdminVerbs = []string{"*"} 51 EditVerbs = []string{"create", "update", "patch", "delete"} 52 ViewVerbs = []string{"get", "list", "watch"} 53 Suffices = []string{AdminSuffix, EditSuffix, ViewSuffix} 54 VerbsForSuffix = map[string][]string{ 55 AdminSuffix: AdminVerbs, 56 EditSuffix: EditVerbs, 57 ViewSuffix: ViewVerbs, 58 } 59 ) 60 61 func aggregationLabelFromAPIKey(k opregistry.APIKey, suffix string) (string, error) { 62 hash, err := cache.APIKeyToGVKHash(k) 63 if err != nil { 64 return "", err 65 } 66 return fmt.Sprintf("olm.opgroup.permissions/aggregate-to-%s-%s", hash, suffix), nil 67 } 68 69 func (a *Operator) syncOperatorGroups(obj interface{}) error { 70 op, ok := obj.(*operatorsv1.OperatorGroup) 71 if !ok { 72 a.logger.Debugf("wrong type: %#v\n", obj) 73 return fmt.Errorf("casting OperatorGroup failed") 74 } 75 76 logger := a.logger.WithFields(logrus.Fields{ 77 "operatorGroup": op.GetName(), 78 "namespace": op.GetNamespace(), 79 }) 80 81 // Query OG in this namespace 82 groups, err := a.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(op.GetNamespace()).List(labels.Everything()) 83 if err != nil { 84 logger.WithError(err).Warnf("failed to list OperatorGroups in the namespace") 85 } 86 87 // Check if there is a stale multiple OG condition and clear it if existed. 88 if len(groups) == 1 { 89 og := groups[0].DeepCopy() 90 if c := meta.FindStatusCondition(og.Status.Conditions, operatorsv1.MutlipleOperatorGroupCondition); c != nil { 91 meta.RemoveStatusCondition(&og.Status.Conditions, operatorsv1.MutlipleOperatorGroupCondition) 92 if og.GetName() == op.GetName() { 93 meta.RemoveStatusCondition(&op.Status.Conditions, operatorsv1.MutlipleOperatorGroupCondition) 94 } 95 _, err = a.client.OperatorsV1().OperatorGroups(op.GetNamespace()).UpdateStatus(context.TODO(), og, metav1.UpdateOptions{}) 96 if err != nil { 97 logger.Warnf("fail to upgrade operator group status og=%s with condition %+v: %s", og.GetName(), c, err.Error()) 98 } 99 } 100 } else if len(groups) > 1 { 101 // Add to all OG's status conditions to indicate they're multiple OGs in the 102 // same namespace which is not allowed. 103 cond := metav1.Condition{ 104 Type: operatorsv1.MutlipleOperatorGroupCondition, 105 Status: metav1.ConditionTrue, 106 Reason: operatorsv1.MultipleOperatorGroupsReason, 107 Message: "Multiple OperatorGroup found in the same namespace", 108 } 109 for i := range groups { 110 og := groups[i].DeepCopy() 111 if c := meta.FindStatusCondition(og.Status.Conditions, operatorsv1.MutlipleOperatorGroupCondition); c != nil { 112 continue 113 } 114 meta.SetStatusCondition(&og.Status.Conditions, cond) 115 if og.GetName() == op.GetName() { 116 meta.SetStatusCondition(&op.Status.Conditions, cond) 117 } 118 _, err = a.client.OperatorsV1().OperatorGroups(op.GetNamespace()).UpdateStatus(context.TODO(), og, metav1.UpdateOptions{}) 119 if err != nil { 120 logger.Warnf("fail to upgrade operator group status og=%s with condition %+v: %s", og.GetName(), cond, err.Error()) 121 } 122 } 123 } 124 125 previousRef := op.Status.ServiceAccountRef.DeepCopy() 126 op, err = a.serviceAccountSyncer.SyncOperatorGroup(op) 127 if err != nil { 128 logger.Errorf("error updating service account - %v", err) 129 return err 130 } 131 if op.Status.ServiceAccountRef != previousRef { 132 csvList, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().List(labels.Everything()) 133 if err != nil { 134 return err 135 } 136 for i := range csvList { 137 csv := csvList[i].DeepCopy() 138 if group, ok := csv.GetAnnotations()[operatorsv1.OperatorGroupAnnotationKey]; !ok || group != op.GetName() { 139 continue 140 } 141 if csv.Status.Reason == v1alpha1.CSVReasonComponentFailedNoRetry { 142 csv.SetPhase(v1alpha1.CSVPhasePending, v1alpha1.CSVReasonDetectedClusterChange, "Cluster resources changed state", a.now()) 143 _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).UpdateStatus(context.TODO(), csv, metav1.UpdateOptions{}) 144 if err != nil { 145 return err 146 } 147 if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { 148 return err 149 } 150 logger.Debug("Requeuing CSV due to detected service account change") 151 } 152 } 153 } 154 155 targetNamespaces, err := a.updateNamespaceList(op) 156 if err != nil { 157 logger.WithError(err).Warn("issue getting operatorgroup target namespaces") 158 return err 159 } 160 logger.WithField("targetNamespaces", targetNamespaces).Debug("updated target namespaces") 161 162 if namespacesChanged(targetNamespaces, op.Status.Namespaces) { 163 logger.Debug("OperatorGroup namespaces change detected") 164 outOfSyncNamespaces := namespacesAddedOrRemoved(op.Status.Namespaces, targetNamespaces) 165 166 // Update operatorgroup target namespace selection 167 logger.WithField("targets", targetNamespaces).Debug("namespace change detected") 168 op.Status = operatorsv1.OperatorGroupStatus{ 169 Namespaces: targetNamespaces, 170 LastUpdated: a.now(), 171 Conditions: op.Status.Conditions, 172 } 173 174 if _, err = a.client.OperatorsV1().OperatorGroups(op.GetNamespace()).UpdateStatus(context.TODO(), op, metav1.UpdateOptions{}); err != nil && !apierrors.IsNotFound(err) { 175 logger.WithError(err).Warn("operatorgroup update failed") 176 return err 177 } 178 179 logger.Debug("operatorgroup status updated") 180 181 // Requeueing out of sync namespaces 182 logger.Debug("Requeueing out of sync namespaces") 183 for _, ns := range outOfSyncNamespaces { 184 logger.WithField("namespace", ns).Debug("requeueing") 185 a.nsQueueSet.Add(ns) 186 } 187 188 // CSV requeue is handled by the succeeding sync in `annotateCSVs` 189 return nil 190 } 191 192 logger.Debug("check that operatorgroup has updated CSV anotations") 193 err = a.annotateCSVs(op, targetNamespaces, logger) 194 if err != nil { 195 logger.WithError(err).Warn("failed to annotate CSVs in operatorgroup after group change") 196 return err 197 } 198 logger.Debug("OperatorGroup CSV annotation completed") 199 200 // Requeue all CSVs that provide the same APIs (including those removed). This notifies conflicting CSVs in 201 // intersecting groups that their conflict has possibly been resolved, either through resizing or through 202 // deletion of the conflicting CSV. 203 groupSurface := NewOperatorGroup(op) 204 groupProvidedAPIs := groupSurface.ProvidedAPIs() 205 providedAPIsForCSVs := a.providedAPIsFromCSVs(op, logger) 206 providedAPIsForGroup := make(cache.APISet) 207 for api := range providedAPIsForCSVs { 208 providedAPIsForGroup[api] = struct{}{} 209 } 210 for api := range groupProvidedAPIs { 211 providedAPIsForGroup[api] = struct{}{} 212 } 213 214 if err := a.ensureOpGroupClusterRoles(op, providedAPIsForGroup); err != nil { 215 logger.WithError(err).Warn("failed to ensure operatorgroup clusterroles") 216 return err 217 } 218 logger.Debug("operatorgroup clusterroles ensured") 219 220 csvs, err := a.findCSVsThatProvideAnyOf(providedAPIsForGroup) 221 if err != nil { 222 logger.WithError(err).Warn("could not find csvs that provide group apis") 223 } 224 for _, csv := range csvs { 225 logger.WithFields(logrus.Fields{ 226 "csv": csv.GetName(), 227 "namespace": csv.GetNamespace(), 228 }).Debug("requeueing provider") 229 if err := a.csvQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { 230 logger.WithError(err).Warn("could not requeue provider") 231 } 232 } 233 234 a.pruneProvidedAPIs(op, groupProvidedAPIs, providedAPIsForCSVs, logger) 235 return nil 236 } 237 238 func (a *Operator) operatorGroupDeleted(obj interface{}) { 239 op, ok := obj.(*operatorsv1.OperatorGroup) 240 if !ok { 241 a.logger.Debugf("casting OperatorGroup failed, wrong type: %#v\n", obj) 242 return 243 } 244 245 logger := a.logger.WithFields(logrus.Fields{ 246 "operatorGroup": op.GetName(), 247 "namespace": op.GetNamespace(), 248 }) 249 250 clusterRoles, err := a.lister.RbacV1().ClusterRoleLister().List(labels.SelectorFromSet(ownerutil.OwnerLabel(op, "OperatorGroup"))) 251 if err != nil { 252 logger.WithError(err).Error("failed to list ClusterRoles for garbage collection") 253 return 254 } 255 for _, clusterRole := range clusterRoles { 256 err = a.opClient.KubernetesInterface().RbacV1().ClusterRoles().Delete(context.TODO(), clusterRole.GetName(), metav1.DeleteOptions{}) 257 if err != nil { 258 logger.WithError(err).Error("failed to delete ClusterRole during garbage collection") 259 } 260 } 261 262 // Trigger a sync on namespaces 263 logger.Debug("OperatorGroup deleted, requeueing out of sync namespaces") 264 for _, ns := range op.Status.Namespaces { 265 logger.WithField("namespace", ns).Debug("requeueing") 266 a.nsQueueSet.Add(ns) 267 } 268 } 269 270 func (a *Operator) annotateCSVs(group *operatorsv1.OperatorGroup, targetNamespaces []string, logger *logrus.Entry) error { 271 updateErrs := []error{} 272 targetNamespaceSet := NewNamespaceSet(targetNamespaces) 273 274 for _, csv := range a.csvSet(group.GetNamespace(), v1alpha1.CSVPhaseAny) { 275 if csv.IsCopied() { 276 continue 277 } 278 logger := logger.WithField("csv", csv.GetName()) 279 280 originalNamespacesAnnotation := a.copyOperatorGroupAnnotations(&csv.ObjectMeta)[operatorsv1.OperatorGroupTargetsAnnotationKey] 281 originalNamespaceSet := NewNamespaceSetFromString(originalNamespacesAnnotation) 282 283 if a.operatorGroupAnnotationsDiffer(&csv.ObjectMeta, group) { 284 a.setOperatorGroupAnnotations(&csv.ObjectMeta, group, true) 285 // CRDs don't support strategic merge patching, but in the future if they do this should be updated to patch 286 if _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Update(context.TODO(), csv, metav1.UpdateOptions{}); err != nil && !apierrors.IsNotFound(err) { 287 logger.WithError(err).Warnf("error adding operatorgroup annotations") 288 updateErrs = append(updateErrs, err) 289 continue 290 } 291 } 292 293 // requeue csvs in original namespaces or in new target namespaces (to capture removed/added namespaces) 294 requeueNamespaces := originalNamespaceSet.Union(targetNamespaceSet) 295 if !requeueNamespaces.IsAllNamespaces() { 296 for ns := range requeueNamespaces { 297 if err := a.csvQueueSet.Requeue(ns, csv.GetName()); err != nil { 298 logger.WithError(err).Warn("could not requeue csv") 299 } 300 } 301 } 302 // have to requeue in all namespaces, previous or new targets were AllNamespaces 303 if namespaces, err := a.lister.CoreV1().NamespaceLister().List(labels.Everything()); err != nil { 304 for _, ns := range namespaces { 305 if err := a.csvQueueSet.Requeue(ns.GetName(), csv.GetName()); err != nil { 306 logger.WithError(err).Warn("could not requeue csv") 307 } 308 } 309 } 310 } 311 return errors.NewAggregate(updateErrs) 312 } 313 314 func (a *Operator) providedAPIsFromCSVs(group *operatorsv1.OperatorGroup, logger *logrus.Entry) map[opregistry.APIKey]*v1alpha1.ClusterServiceVersion { 315 set := a.csvSet(group.Namespace, v1alpha1.CSVPhaseAny) 316 providedAPIsFromCSVs := make(map[opregistry.APIKey]*v1alpha1.ClusterServiceVersion) 317 for _, csv := range set { 318 // Don't union providedAPIsFromCSVs if the CSV is copied (member of another OperatorGroup) 319 if csv.IsCopied() { 320 logger.Debug("csv is copied. not updating annotations or including in operatorgroup's provided api set") 321 continue 322 } 323 324 // TODO: Throw out CSVs that aren't members of the group due to group related failures? 325 326 // Union the providedAPIsFromCSVs from existing members of the group 327 operatorSurface, err := apiSurfaceOfCSV(csv) 328 if err != nil { 329 logger.WithError(err).Warn("could not create OperatorSurface from csv") 330 continue 331 } 332 for providedAPI := range operatorSurface.ProvidedAPIs.StripPlural() { 333 providedAPIsFromCSVs[providedAPI] = csv 334 } 335 } 336 return providedAPIsFromCSVs 337 } 338 339 func (a *Operator) pruneProvidedAPIs(group *operatorsv1.OperatorGroup, groupProvidedAPIs cache.APISet, providedAPIsFromCSVs map[opregistry.APIKey]*v1alpha1.ClusterServiceVersion, logger *logrus.Entry) { 340 // Don't prune providedAPIsFromCSVs if static 341 if group.Spec.StaticProvidedAPIs { 342 a.logger.Debug("group has static provided apis. skipping provided api pruning") 343 return 344 } 345 346 intersection := make(cache.APISet) 347 for api := range providedAPIsFromCSVs { 348 if _, ok := groupProvidedAPIs[api]; ok { 349 intersection[api] = struct{}{} 350 } else { 351 csv := providedAPIsFromCSVs[api] 352 _, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(csv.GetNamespace()).Get(csv.GetName()) 353 if apierrors.IsNotFound(err) { 354 continue 355 } 356 if csv.DeletionTimestamp == nil && (csv.Status.Phase == v1alpha1.CSVPhaseNone || csv.Status.Phase == v1alpha1.CSVPhasePending) { 357 logger.Debugf("aborting operator group provided API update due to CSV %v in phase %v", csv.GetName(), csv.Status.Phase) 358 return 359 } 360 } 361 } 362 363 // Prune providedAPIs annotation if the cluster has fewer providedAPIs (handles CSV deletion) 364 //if intersection := groupProvidedAPIs.Intersection(providedAPIsFromCSVs); len(intersection) < len(groupProvidedAPIs) { 365 if len(intersection) < len(groupProvidedAPIs) { 366 difference := groupProvidedAPIs.Difference(intersection) 367 logger := logger.WithFields(logrus.Fields{ 368 "providedAPIsOnCluster": providedAPIsFromCSVs, 369 "providedAPIsAnnotation": groupProvidedAPIs, 370 "providedAPIDifference": difference, 371 "intersection": intersection, 372 }) 373 374 // Don't need to check for nil annotations since we already know |annotations| > 0 375 annotations := group.GetAnnotations() 376 annotations[operatorsv1.OperatorGroupProvidedAPIsAnnotationKey] = intersection.String() 377 group.SetAnnotations(annotations) 378 logger.Debug("removing provided apis from annotation to match cluster state") 379 if _, err := a.client.OperatorsV1().OperatorGroups(group.GetNamespace()).Update(context.TODO(), group, metav1.UpdateOptions{}); err != nil && !apierrors.IsNotFound(err) { 380 logger.WithError(err).Warn("could not update provided api annotations") 381 } 382 } 383 } 384 385 // ensureProvidedAPIClusterRole ensures that a clusterrole exists (admin, edit, or view) for a single provided API Type 386 func (a *Operator) ensureProvidedAPIClusterRole(namePrefix, suffix string, verbs []string, group, resource string, resourceNames []string, api ownerutil.Owner, key opregistry.APIKey) error { 387 aggregationLabel, err := aggregationLabelFromAPIKey(key, suffix) 388 if err != nil { 389 return err 390 } 391 clusterRole := &rbacv1.ClusterRole{ 392 ObjectMeta: metav1.ObjectMeta{ 393 Name: namePrefix + suffix, 394 Labels: map[string]string{ 395 // Matches aggregation rules on the bootstrap ClusterRoles. 396 // https://github.com/kubernetes/kubernetes/blob/61847eab61788fb0543b4cf147773c9da646ed2f/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go#L232 397 fmt.Sprintf("rbac.authorization.k8s.io/aggregate-to-%s", suffix): "true", 398 aggregationLabel: "true", 399 install.OLMManagedLabelKey: install.OLMManagedLabelValue, 400 }, 401 OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(api)}, 402 }, 403 Rules: []rbacv1.PolicyRule{{Verbs: verbs, APIGroups: []string{group}, Resources: []string{resource}, ResourceNames: resourceNames}}, 404 } 405 406 existingCR, err := a.lister.RbacV1().ClusterRoleLister().Get(clusterRole.Name) 407 if err != nil && !apierrors.IsNotFound(err) { 408 return err 409 } 410 if apierrors.IsNotFound(err) { 411 existingCR, err = a.opClient.CreateClusterRole(clusterRole) 412 if err != nil { 413 a.logger.WithError(err).Errorf("Create cluster role failed: %v", clusterRole) 414 return err 415 } 416 } 417 418 if existingCR != nil && reflect.DeepEqual(existingCR.Rules, clusterRole.Rules) && ownerutil.IsOwnedBy(existingCR, api) && labels.Equals(existingCR.Labels, clusterRole.Labels) { 419 return nil 420 } 421 422 if _, err := a.opClient.UpdateClusterRole(clusterRole); err != nil { 423 a.logger.WithError(err).Errorf("Update existing cluster role failed: %v", clusterRole) 424 return err 425 } 426 return nil 427 } 428 429 // ensureClusterRolesForCSV ensures that ClusterRoles for writing and reading provided APIs exist for each operator 430 func (a *Operator) ensureClusterRolesForCSV(csv *v1alpha1.ClusterServiceVersion) error { 431 for _, owned := range csv.Spec.CustomResourceDefinitions.Owned { 432 crd, err := a.lister.APIExtensionsV1().CustomResourceDefinitionLister().Get(owned.Name) 433 if err != nil { 434 return fmt.Errorf("crd %q not found: %s", owned.Name, err.Error()) 435 } 436 crd.SetGroupVersionKind(apiextensionsv1.SchemeGroupVersion.WithKind("customresourcedefinition")) 437 nameGroupPair := strings.SplitN(owned.Name, ".", 2) // -> etcdclusters etcd.database.coreos.com 438 if len(nameGroupPair) != 2 { 439 return fmt.Errorf("invalid parsing of name '%v', got %v", owned.Name, nameGroupPair) 440 } 441 plural := nameGroupPair[0] 442 group := nameGroupPair[1] 443 namePrefix := fmt.Sprintf("%s-%s-", owned.Name, owned.Version) 444 key := opregistry.APIKey{Group: group, Version: owned.Version, Kind: owned.Kind, Plural: plural} 445 for suffix, verbs := range VerbsForSuffix { 446 if err := a.ensureProvidedAPIClusterRole(namePrefix, suffix, verbs, group, plural, nil, crd, key); err != nil { 447 return err 448 } 449 } 450 if err := a.ensureProvidedAPIClusterRole(namePrefix+"crd", ViewSuffix, []string{"get"}, "apiextensions.k8s.io", "customresourcedefinitions", []string{owned.Name}, crd, key); err != nil { 451 return err 452 } 453 } 454 for _, owned := range csv.Spec.APIServiceDefinitions.Owned { 455 svcName := strings.Join([]string{owned.Version, owned.Group}, ".") 456 svc, err := a.lister.APIRegistrationV1().APIServiceLister().Get(svcName) 457 if err != nil { 458 return fmt.Errorf("apiservice %q not found: %s", svcName, err.Error()) 459 } 460 namePrefix := fmt.Sprintf("%s-%s-", owned.Name, owned.Version) 461 key := opregistry.APIKey{Group: owned.Group, Version: owned.Version, Kind: owned.Kind} 462 for suffix, verbs := range VerbsForSuffix { 463 if err := a.ensureProvidedAPIClusterRole(namePrefix, suffix, verbs, owned.Group, owned.Name, nil, svc, key); err != nil { 464 return err 465 } 466 } 467 } 468 return nil 469 } 470 471 func (a *Operator) ensureRBACInTargetNamespace(csv *v1alpha1.ClusterServiceVersion, operatorGroup *operatorsv1.OperatorGroup) error { 472 targetNamespaces := operatorGroup.Status.Namespaces 473 if targetNamespaces == nil { 474 return nil 475 } 476 477 strategyResolver := install.StrategyResolver{} 478 strategy, err := strategyResolver.UnmarshalStrategy(csv.Spec.InstallStrategy) 479 if err != nil { 480 return err 481 } 482 strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) 483 if !ok { 484 return fmt.Errorf("could not cast install strategy as type %T", strategyDetailsDeployment) 485 } 486 487 logger := a.logger.WithField("opgroup", operatorGroup.GetName()).WithField("csv", csv.GetName()) 488 489 // if OperatorGroup is global (all namespaces) we generate cluster roles / cluster role bindings instead 490 if len(targetNamespaces) == 1 && targetNamespaces[0] == corev1.NamespaceAll { 491 logger.Debug("opgroup is global") 492 493 // synthesize cluster permissions to verify rbac 494 strategyDetailsDeployment.ClusterPermissions = append(strategyDetailsDeployment.ClusterPermissions, strategyDetailsDeployment.Permissions...) 495 strategyDetailsDeployment.Permissions = nil 496 permMet, _, err := a.permissionStatus(strategyDetailsDeployment, corev1.NamespaceAll, csv) 497 if err != nil { 498 return err 499 } 500 501 // operator already has access at the cluster scope 502 if permMet { 503 logger.Debug("global operator has correct global permissions") 504 return nil 505 } 506 logger.Debug("lift roles/rolebindings to clusterroles/rolebindings") 507 if err := a.ensureSingletonRBAC(operatorGroup.GetNamespace(), csv); err != nil { 508 return err 509 } 510 511 return nil 512 } 513 514 return nil 515 } 516 517 func (a *Operator) ensureSingletonRBAC(operatorNamespace string, csv *v1alpha1.ClusterServiceVersion) error { 518 ownerSelector := ownerutil.CSVOwnerSelector(csv) 519 ownedRoles, err := a.lister.RbacV1().RoleLister().Roles(operatorNamespace).List(ownerSelector) 520 if err != nil { 521 return err 522 } 523 if len(ownedRoles) == 0 { 524 return fmt.Errorf("no owned roles found") 525 } 526 527 for _, r := range ownedRoles { 528 a.logger.Debug("processing role") 529 _, err := a.lister.RbacV1().ClusterRoleLister().Get(r.GetName()) 530 if err != nil { 531 clusterRole := &rbacv1.ClusterRole{ 532 TypeMeta: metav1.TypeMeta{ 533 Kind: "ClusterRole", 534 APIVersion: r.APIVersion, 535 }, 536 ObjectMeta: metav1.ObjectMeta{ 537 Name: r.GetName(), 538 Labels: r.GetLabels(), 539 }, 540 Rules: append(r.Rules, rbacv1.PolicyRule{ 541 Verbs: ViewVerbs, 542 APIGroups: []string{corev1.GroupName}, 543 Resources: []string{"namespaces"}, 544 }), 545 } 546 if _, err := a.opClient.CreateClusterRole(clusterRole); err != nil { 547 return err 548 } 549 a.logger.Debug("created cluster role") 550 } 551 } 552 553 ownedRoleBindings, err := a.lister.RbacV1().RoleBindingLister().RoleBindings(operatorNamespace).List(ownerSelector) 554 if err != nil { 555 return err 556 } 557 if len(ownedRoleBindings) == 0 { 558 return fmt.Errorf("no owned rolebindings found") 559 } 560 561 for _, r := range ownedRoleBindings { 562 _, err := a.lister.RbacV1().ClusterRoleBindingLister().Get(r.GetName()) 563 if err != nil { 564 clusterRoleBinding := &rbacv1.ClusterRoleBinding{ 565 TypeMeta: metav1.TypeMeta{ 566 Kind: "ClusterRoleBinding", 567 APIVersion: r.APIVersion, 568 }, 569 ObjectMeta: metav1.ObjectMeta{ 570 Name: r.GetName(), 571 Labels: r.GetLabels(), 572 }, 573 Subjects: r.Subjects, 574 RoleRef: rbacv1.RoleRef{ 575 APIGroup: r.RoleRef.APIGroup, 576 Kind: "ClusterRole", 577 Name: r.RoleRef.Name, 578 }, 579 } 580 if _, err := a.opClient.CreateClusterRoleBinding(clusterRoleBinding); err != nil { 581 return err 582 } 583 } 584 } 585 return nil 586 } 587 588 func (a *Operator) ensureTenantRBAC(operatorNamespace, targetNamespace string, csv *v1alpha1.ClusterServiceVersion, targetCSV *v1alpha1.ClusterServiceVersion) error { 589 if operatorNamespace == targetNamespace { 590 return nil 591 } 592 593 ownerSelector := ownerutil.CSVOwnerSelector(csv) 594 ownedRoles, err := a.lister.RbacV1().RoleLister().Roles(operatorNamespace).List(ownerSelector) 595 if err != nil { 596 return err 597 } 598 599 if len(ownedRoles) == 0 { 600 return fmt.Errorf("owned roles not found in cache") 601 } 602 603 targetRoles, err := a.lister.RbacV1().RoleLister().Roles(targetNamespace).List(ownerutil.CSVOwnerSelector(targetCSV)) 604 if err != nil { 605 return err 606 } 607 608 targetRolesByName := map[string]*rbacv1.Role{} 609 for _, r := range targetRoles { 610 targetRolesByName[r.GetName()] = r 611 } 612 613 for _, ownedRole := range ownedRoles { 614 // don't trust the owner label 615 // TODO: this can skip objects that have owner labels but different ownerreferences 616 if !ownerutil.IsOwnedBy(ownedRole, csv) { 617 continue 618 } 619 620 existing, ok := targetRolesByName[ownedRole.GetName()] 621 622 // role already exists, update the rules 623 if ok { 624 existing.Rules = ownedRole.Rules 625 if _, err := a.opClient.UpdateRole(existing); err != nil { 626 return err 627 } 628 continue 629 } 630 631 // role doesn't exist, create it 632 // TODO: we can work around error cases here; if there's an un-owned role with a matching name we should generate instead 633 targetRole := ownedRole.DeepCopy() 634 targetRole.SetResourceVersion("0") 635 targetRole.SetNamespace(targetNamespace) 636 targetRole.SetOwnerReferences([]metav1.OwnerReference{ownerutil.NonBlockingOwner(targetCSV)}) 637 if err := ownerutil.AddOwnerLabels(targetRole, targetCSV); err != nil { 638 return err 639 } 640 targetRole.SetLabels(utillabels.AddLabel(targetRole.GetLabels(), v1alpha1.CopiedLabelKey, operatorNamespace)) 641 targetRole.Labels[install.OLMManagedLabelKey] = install.OLMManagedLabelValue 642 if _, err := a.opClient.CreateRole(targetRole); err != nil { 643 return err 644 } 645 } 646 647 ownedRoleBindings, err := a.lister.RbacV1().RoleBindingLister().RoleBindings(operatorNamespace).List(ownerSelector) 648 if err != nil { 649 return err 650 } 651 652 targetRoleBindings, err := a.lister.RbacV1().RoleBindingLister().RoleBindings(targetNamespace).List(ownerutil.CSVOwnerSelector(targetCSV)) 653 if err != nil { 654 return err 655 } 656 657 targetRoleBindingsByName := map[string]*rbacv1.RoleBinding{} 658 for _, r := range targetRoleBindings { 659 targetRoleBindingsByName[r.GetName()] = r 660 } 661 662 // role bindings 663 for _, ownedRoleBinding := range ownedRoleBindings { 664 // don't trust the owner label 665 if !ownerutil.IsOwnedBy(ownedRoleBinding, csv) { 666 continue 667 } 668 _, ok := targetRoleBindingsByName[ownedRoleBinding.GetName()] 669 670 // role binding exists 671 if ok { 672 // TODO: we should check if SA/role has changed 673 continue 674 } 675 676 // role binding doesn't exist 677 // TODO: we can work around error cases here; if there's an un-owned role with a matching name we should generate instead 678 ownedRoleBinding = ownedRoleBinding.DeepCopy() 679 ownedRoleBinding.SetNamespace(targetNamespace) 680 ownedRoleBinding.SetResourceVersion("0") 681 ownedRoleBinding.SetOwnerReferences([]metav1.OwnerReference{ownerutil.NonBlockingOwner(targetCSV)}) 682 if err := ownerutil.AddOwnerLabels(ownedRoleBinding, targetCSV); err != nil { 683 return err 684 } 685 ownedRoleBinding.SetLabels(utillabels.AddLabel(ownedRoleBinding.GetLabels(), v1alpha1.CopiedLabelKey, operatorNamespace)) 686 ownedRoleBinding.Labels[install.OLMManagedLabelKey] = install.OLMManagedLabelValue 687 if _, err := a.opClient.CreateRoleBinding(ownedRoleBinding); err != nil { 688 return err 689 } 690 } 691 return nil 692 } 693 694 func (a *Operator) ensureCSVsInNamespaces(csv *v1alpha1.ClusterServiceVersion, operatorGroup *operatorsv1.OperatorGroup, targets NamespaceSet) error { 695 namespaces, err := a.lister.CoreV1().NamespaceLister().List(labels.Everything()) 696 if err != nil { 697 return err 698 } 699 700 strategyDetailsDeployment := &csv.Spec.InstallStrategy.StrategySpec 701 logger := a.logger.WithField("opgroup", operatorGroup.GetName()).WithField("csv", csv.GetName()) 702 703 targetCSVs := make(map[string]*v1alpha1.ClusterServiceVersion) 704 705 var copyPrototype v1alpha1.ClusterServiceVersion 706 csvCopyPrototype(csv, ©Prototype) 707 nonstatus, status, err := copyableCSVHash(©Prototype) 708 if err != nil { 709 return err 710 } 711 712 for _, ns := range namespaces { 713 if ns.GetName() == operatorGroup.Namespace { 714 continue 715 } 716 if targets.Contains(ns.GetName()) { 717 var targetCSV *v1alpha1.ClusterServiceVersion 718 if targetCSV, err = a.copyToNamespace(©Prototype, csv.GetNamespace(), ns.GetName(), nonstatus, status); err != nil { 719 logger.WithError(err).Debug("error copying to target") 720 continue 721 } 722 targetCSVs[ns.GetName()] = targetCSV 723 } else { 724 if err := a.pruneFromNamespace(operatorGroup.GetName(), ns.GetName()); err != nil { 725 logger.WithError(err).Debug("error pruning from old target") 726 } 727 } 728 } 729 730 targetNamespaces := operatorGroup.Status.Namespaces 731 if targetNamespaces == nil { 732 logger.Errorf("operatorgroup '%v' should have non-nil status", operatorGroup.GetName()) 733 return nil 734 } 735 if len(targetNamespaces) == 1 && targetNamespaces[0] == corev1.NamespaceAll { 736 // global operator group handled by ensureRBACInTargetNamespace 737 return nil 738 } 739 for _, ns := range targetNamespaces { 740 // create roles/rolebindings for each target namespace 741 permMet, _, err := a.permissionStatus(strategyDetailsDeployment, ns, csv) 742 if err != nil { 743 logger.WithError(err).Debug("permission status") 744 return err 745 } 746 logger.WithField("target", ns).WithField("permMet", permMet).Debug("permission status") 747 748 // operator already has access in the target namespace 749 if permMet { 750 logger.Debug("operator has access") 751 continue 752 } else { 753 logger.Debug("operator needs access, going to create permissions") 754 } 755 756 targetCSV, ok := targetCSVs[ns] 757 if !ok { 758 return fmt.Errorf("bug: no target CSV for namespace %v", ns) 759 } 760 if err := a.ensureTenantRBAC(operatorGroup.GetNamespace(), ns, csv, targetCSV); err != nil { 761 logger.WithError(err).Debug("ensuring tenant rbac") 762 return err 763 } 764 logger.Debug("permissions created") 765 } 766 767 return nil 768 } 769 770 // copyableCSVHash returns a hash of the parts of the given CSV that 771 // are relevant to copied CSV projection. 772 func copyableCSVHash(original *v1alpha1.ClusterServiceVersion) (string, string, error) { 773 shallow := v1alpha1.ClusterServiceVersion{ 774 ObjectMeta: metav1.ObjectMeta{ 775 Name: original.Name, 776 Labels: original.Labels, 777 Annotations: original.Annotations, 778 }, 779 Spec: original.Spec, 780 } 781 782 newHash, err := hashutil.DeepHashObject(&shallow) 783 if err != nil { 784 return "", "", err 785 } 786 originalHash, err := hashutil.DeepHashObject(&original.Status) 787 if err != nil { 788 return "", "", err 789 } 790 791 return newHash, originalHash, nil 792 } 793 794 // If returned error is not nil, the returned ClusterServiceVersion 795 // has only the Name, Namespace, and UID fields set. 796 func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, nsFrom, nsTo, nonstatus, status string) (*v1alpha1.ClusterServiceVersion, error) { 797 if nsFrom == nsTo { 798 return nil, fmt.Errorf("bug: can not copy to active namespace %v", nsFrom) 799 } 800 801 prototype.Namespace = nsTo 802 prototype.ResourceVersion = "" 803 prototype.UID = "" 804 805 existing, err := a.copiedCSVLister.Namespace(nsTo).Get(prototype.GetName()) 806 if apierrors.IsNotFound(err) { 807 created, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).Create(context.TODO(), prototype, metav1.CreateOptions{}) 808 if err != nil { 809 return nil, fmt.Errorf("failed to create new CSV: %w", err) 810 } 811 created.Status = prototype.Status 812 if _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).UpdateStatus(context.TODO(), created, metav1.UpdateOptions{}); err != nil { 813 return nil, fmt.Errorf("failed to update status on new CSV: %w", err) 814 } 815 return &v1alpha1.ClusterServiceVersion{ 816 ObjectMeta: metav1.ObjectMeta{ 817 Name: created.Name, 818 Namespace: created.Namespace, 819 UID: created.UID, 820 }, 821 }, nil 822 } else if err != nil { 823 return nil, err 824 } 825 826 prototype.Namespace = existing.Namespace 827 prototype.ResourceVersion = existing.ResourceVersion 828 prototype.UID = existing.UID 829 existingNonStatus := existing.Annotations["$copyhash-nonstatus"] 830 existingStatus := existing.Annotations["$copyhash-status"] 831 832 var updated *v1alpha1.ClusterServiceVersion 833 if existingNonStatus != nonstatus { 834 if updated, err = a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).Update(context.TODO(), prototype, metav1.UpdateOptions{}); err != nil { 835 return nil, fmt.Errorf("failed to update: %w", err) 836 } 837 } else { 838 // Avoid mutating cached copied CSV. 839 updated = prototype 840 } 841 842 if existingStatus != status { 843 updated.Status = prototype.Status 844 if _, err = a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{}); err != nil { 845 return nil, fmt.Errorf("failed to update status: %w", err) 846 } 847 } 848 return &v1alpha1.ClusterServiceVersion{ 849 ObjectMeta: metav1.ObjectMeta{ 850 Name: updated.Name, 851 Namespace: updated.Namespace, 852 UID: updated.UID, 853 }, 854 }, nil 855 } 856 857 func (a *Operator) pruneFromNamespace(operatorGroupName, namespace string) error { 858 fetchedCSVs, err := a.copiedCSVLister.Namespace(namespace).List(labels.Everything()) 859 if err != nil { 860 return err 861 } 862 863 for _, csv := range fetchedCSVs { 864 if v1alpha1.IsCopied(csv) && csv.GetAnnotations()[operatorsv1.OperatorGroupAnnotationKey] == operatorGroupName { 865 a.logger.Debugf("Found CSV '%v' in namespace %v to delete", csv.GetName(), namespace) 866 if err := a.copiedCSVGCQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { 867 return err 868 } 869 } 870 } 871 return nil 872 } 873 874 func (a *Operator) setOperatorGroupAnnotations(obj *metav1.ObjectMeta, op *operatorsv1.OperatorGroup, addTargets bool) { 875 metav1.SetMetaDataAnnotation(obj, operatorsv1.OperatorGroupNamespaceAnnotationKey, op.GetNamespace()) 876 metav1.SetMetaDataAnnotation(obj, operatorsv1.OperatorGroupAnnotationKey, op.GetName()) 877 878 if addTargets && op.Status.Namespaces != nil { 879 metav1.SetMetaDataAnnotation(obj, operatorsv1.OperatorGroupTargetsAnnotationKey, op.BuildTargetNamespaces()) 880 } 881 } 882 883 func (a *Operator) operatorGroupAnnotationsDiffer(obj *metav1.ObjectMeta, op *operatorsv1.OperatorGroup) bool { 884 annotations := obj.GetAnnotations() 885 if annotations == nil { 886 return true 887 } 888 if operatorGroupNamespace, ok := annotations[operatorsv1.OperatorGroupNamespaceAnnotationKey]; !ok || operatorGroupNamespace != op.GetNamespace() { 889 return true 890 } 891 if operatorGroup, ok := annotations[operatorsv1.OperatorGroupAnnotationKey]; !ok || operatorGroup != op.GetName() { 892 return true 893 } 894 if targets, ok := annotations[operatorsv1.OperatorGroupTargetsAnnotationKey]; !ok || targets != op.BuildTargetNamespaces() { 895 a.logger.WithFields(logrus.Fields{ 896 "annotationTargets": annotations[operatorsv1.OperatorGroupTargetsAnnotationKey], 897 "opgroupTargets": op.BuildTargetNamespaces(), 898 }).Debug("annotations different") 899 return true 900 } 901 902 a.logger.WithFields(logrus.Fields{ 903 "annotationTargets": annotations[operatorsv1.OperatorGroupTargetsAnnotationKey], 904 "opgroupTargets": op.BuildTargetNamespaces(), 905 }).Debug("annotations correct") 906 return false 907 } 908 909 func (a *Operator) copyOperatorGroupAnnotations(obj *metav1.ObjectMeta) map[string]string { 910 copiedAnnotations := make(map[string]string) 911 for k, v := range obj.GetAnnotations() { 912 switch k { 913 case operatorsv1.OperatorGroupNamespaceAnnotationKey: 914 fallthrough 915 case operatorsv1.OperatorGroupAnnotationKey: 916 fallthrough 917 case operatorsv1.OperatorGroupTargetsAnnotationKey: 918 copiedAnnotations[k] = v 919 } 920 } 921 return copiedAnnotations 922 } 923 924 func namespacesChanged(clusterNamespaces []string, statusNamespaces []string) bool { 925 if len(clusterNamespaces) != len(statusNamespaces) { 926 return true 927 } 928 929 nsMap := map[string]struct{}{} 930 for _, v := range clusterNamespaces { 931 nsMap[v] = struct{}{} 932 } 933 for _, v := range statusNamespaces { 934 if _, ok := nsMap[v]; !ok { 935 return true 936 } 937 } 938 return false 939 } 940 941 func (a *Operator) getOperatorGroupTargets(op *operatorsv1.OperatorGroup) (map[string]struct{}, error) { 942 selector, err := metav1.LabelSelectorAsSelector(op.Spec.Selector) 943 944 if err != nil { 945 return nil, err 946 } 947 948 namespaceSet := make(map[string]struct{}) 949 if len(op.Spec.TargetNamespaces) > 0 { 950 for _, ns := range op.Spec.TargetNamespaces { 951 if ns == corev1.NamespaceAll { 952 return nil, fmt.Errorf("TargetNamespaces cannot contain NamespaceAll: %v", op.Spec.TargetNamespaces) 953 } 954 namespaceSet[ns] = struct{}{} 955 } 956 } else if selector == nil || selector.Empty() || selector == labels.Nothing() { 957 namespaceSet[corev1.NamespaceAll] = struct{}{} 958 } else { 959 matchedNamespaces, err := a.lister.CoreV1().NamespaceLister().List(selector) 960 if err != nil { 961 return nil, err 962 } else if len(matchedNamespaces) == 0 { 963 a.logger.Debugf("No matched TargetNamespaces are found for given selector: %#v\n", selector) 964 } 965 966 for _, ns := range matchedNamespaces { 967 namespaceSet[ns.GetName()] = struct{}{} 968 } 969 } 970 return namespaceSet, nil 971 } 972 973 func (a *Operator) updateNamespaceList(op *operatorsv1.OperatorGroup) ([]string, error) { 974 namespaceSet, err := a.getOperatorGroupTargets(op) 975 if err != nil { 976 return nil, err 977 } 978 namespaceList := []string{} 979 for ns := range namespaceSet { 980 namespaceList = append(namespaceList, ns) 981 } 982 983 return namespaceList, nil 984 } 985 986 func (a *Operator) getClusterRoleName(op *operatorsv1.OperatorGroup, roleType string) (string, error) { 987 roleSuffix := hash(fmt.Sprintf("%s/%s/%s", op.GetNamespace(), op.GetName(), roleType)) 988 // calculate how many characters are left for the operator group name 989 nameLimit := kubeResourceNameLimit - len(strings.Replace(operatorGroupClusterRoleNameFmt, "%s", "", -1)) - len(roleType) - len(roleSuffix) 990 if len(op.GetName()) < nameLimit { 991 return fmt.Sprintf(operatorGroupClusterRoleNameFmt, op.GetName(), roleType, roleSuffix), nil 992 } 993 return fmt.Sprintf(operatorGroupClusterRoleNameFmt, op.GetName()[:nameLimit], roleType, roleSuffix), nil 994 } 995 996 func (a *Operator) ensureOpGroupClusterRole(op *operatorsv1.OperatorGroup, suffix string, apis cache.APISet) error { 997 // create target cluster role spec 998 var clusterRole *rbacv1.ClusterRole 999 clusterRoleName, err := a.getClusterRoleName(op, suffix) 1000 if err != nil { 1001 return err 1002 } 1003 aggregationRule, err := a.getClusterRoleAggregationRule(apis, suffix) 1004 if err != nil { 1005 return err 1006 } 1007 1008 // get existing cluster role for this level (suffix: admin, edit, view)) 1009 existingRole, err := a.lister.RbacV1().ClusterRoleLister().Get(clusterRoleName) 1010 if err != nil && !apierrors.IsNotFound(err) { 1011 return err 1012 } 1013 1014 if existingRole != nil { 1015 // if the existing role conforms to the naming convention, check for skew 1016 cp := existingRole.DeepCopy() 1017 if err := ownerutil.AddOwnerLabels(cp, op); err != nil { 1018 return err 1019 } 1020 1021 // ensure that the labels and aggregation rules are correct 1022 if labels.Equals(existingRole.Labels, cp.Labels) && equality.Semantic.DeepEqual(existingRole.AggregationRule, aggregationRule) { 1023 return nil 1024 } 1025 1026 cp.AggregationRule = aggregationRule 1027 if _, err := a.opClient.UpdateClusterRole(cp); err != nil { 1028 a.logger.WithError(err).Errorf("update existing cluster role failed: %v", clusterRole) 1029 } 1030 return err 1031 } 1032 1033 clusterRole = &rbacv1.ClusterRole{ 1034 ObjectMeta: metav1.ObjectMeta{ 1035 Name: clusterRoleName, 1036 }, 1037 AggregationRule: aggregationRule, 1038 } 1039 1040 if err := ownerutil.AddOwnerLabels(clusterRole, op); err != nil { 1041 return err 1042 } 1043 clusterRole.Labels[install.OLMManagedLabelKey] = install.OLMManagedLabelValue 1044 1045 a.logger.Infof("creating cluster role: %s owned by operator group: %s/%s", clusterRole.GetName(), op.GetNamespace(), op.GetName()) 1046 _, err = a.opClient.CreateClusterRole(clusterRole) 1047 return err 1048 } 1049 1050 func (a *Operator) getClusterRoleAggregationRule(apis cache.APISet, suffix string) (*rbacv1.AggregationRule, error) { 1051 var selectors []metav1.LabelSelector 1052 for api := range apis { 1053 aggregationLabel, err := aggregationLabelFromAPIKey(api, suffix) 1054 if err != nil { 1055 return nil, err 1056 } 1057 selectors = append(selectors, metav1.LabelSelector{ 1058 MatchLabels: map[string]string{ 1059 aggregationLabel: "true", 1060 }, 1061 }) 1062 } 1063 if len(selectors) > 0 { 1064 return &rbacv1.AggregationRule{ 1065 ClusterRoleSelectors: selectors, 1066 }, nil 1067 } 1068 return nil, nil 1069 } 1070 1071 func (a *Operator) ensureOpGroupClusterRoles(op *operatorsv1.OperatorGroup, apis cache.APISet) error { 1072 for _, suffix := range Suffices { 1073 if err := a.ensureOpGroupClusterRole(op, suffix, apis); err != nil { 1074 return err 1075 } 1076 } 1077 return nil 1078 } 1079 1080 func (a *Operator) findCSVsThatProvideAnyOf(provide cache.APISet) ([]*v1alpha1.ClusterServiceVersion, error) { 1081 csvs, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(metav1.NamespaceAll).List(labels.Everything()) 1082 if err != nil { 1083 return nil, err 1084 } 1085 1086 providers := []*v1alpha1.ClusterServiceVersion{} 1087 for i := 0; i < len(csvs); i++ { 1088 csv := csvs[i] 1089 if csv.IsCopied() { 1090 continue 1091 } 1092 1093 operatorSurface, err := apiSurfaceOfCSV(csv) 1094 if err != nil { 1095 continue 1096 } 1097 1098 if len(operatorSurface.ProvidedAPIs.StripPlural().Intersection(provide)) > 0 { 1099 providers = append(providers, csv) 1100 } 1101 } 1102 1103 return providers, nil 1104 } 1105 1106 // namespacesAddedOrRemoved returns the union of: 1107 // - the set of elements in A but not in B 1108 // - the set of elements in B but not in A 1109 func namespacesAddedOrRemoved(a, b []string) []string { 1110 check := make(map[string]struct{}) 1111 1112 for _, namespace := range a { 1113 check[namespace] = struct{}{} 1114 } 1115 1116 for _, namespace := range b { 1117 if _, ok := check[namespace]; !ok { 1118 check[namespace] = struct{}{} 1119 } else { 1120 delete(check, namespace) 1121 } 1122 } 1123 1124 // Remove global namespace name if added 1125 delete(check, "") 1126 1127 var keys []string 1128 for key := range check { 1129 keys = append(keys, key) 1130 } 1131 1132 return keys 1133 } 1134 1135 func csvCopyPrototype(src, dst *v1alpha1.ClusterServiceVersion) { 1136 *dst = v1alpha1.ClusterServiceVersion{ 1137 TypeMeta: src.TypeMeta, 1138 ObjectMeta: metav1.ObjectMeta{ 1139 Name: src.Name, 1140 Annotations: map[string]string{}, 1141 Labels: map[string]string{}, 1142 }, 1143 Spec: src.Spec, 1144 Status: src.Status, 1145 } 1146 for k, v := range src.Annotations { 1147 if k == operatorsv1.OperatorGroupTargetsAnnotationKey { 1148 continue 1149 } 1150 if k == "kubectl.kubernetes.io/last-applied-configuration" { 1151 continue // big 1152 } 1153 dst.Annotations[k] = v 1154 } 1155 for k, v := range src.Labels { 1156 if strings.HasPrefix(k, decorators.ComponentLabelKeyPrefix) { 1157 continue 1158 } 1159 dst.Labels[k] = v 1160 } 1161 dst.Labels[v1alpha1.CopiedLabelKey] = src.Namespace 1162 dst.Status.Reason = v1alpha1.CSVReasonCopied 1163 dst.Status.Message = fmt.Sprintf("The operator is running in %s but is managing this namespace", src.GetNamespace()) 1164 } 1165 1166 func hash(s string) string { 1167 return toBase62(sha256.Sum224([]byte(s))) 1168 } 1169 1170 func toBase62(hash [28]byte) string { 1171 var i big.Int 1172 i.SetBytes(hash[:]) 1173 return i.Text(62) 1174 }