github.com/operator-framework/operator-lifecycle-manager@v0.30.0/test/e2e/operator_groups_e2e_test.go (about) 1 package e2e 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strings" 8 "time" 9 10 "github.com/blang/semver/v4" 11 "github.com/google/go-cmp/cmp" 12 . "github.com/onsi/ginkgo/v2" 13 . "github.com/onsi/gomega" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 authorizationv1 "k8s.io/api/authorization/v1" 17 corev1 "k8s.io/api/core/v1" 18 rbacv1 "k8s.io/api/rbac/v1" 19 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 20 apierrors "k8s.io/apimachinery/pkg/api/errors" 21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 "k8s.io/apimachinery/pkg/labels" 23 "k8s.io/apimachinery/pkg/util/wait" 24 "k8s.io/client-go/informers" 25 "k8s.io/client-go/tools/cache" 26 "k8s.io/client-go/util/retry" 27 28 v1 "github.com/operator-framework/api/pkg/operators/v1" 29 "github.com/operator-framework/api/pkg/operators/v1alpha1" 30 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" 31 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 32 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" 33 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" 34 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 35 "github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx" 36 ) 37 38 var _ = Describe("Operator Group", func() { 39 var ( 40 c operatorclient.ClientInterface 41 crc versioned.Interface 42 generatedNamespace corev1.Namespace 43 ) 44 45 BeforeEach(func() { 46 c = ctx.Ctx().KubeClient() 47 crc = ctx.Ctx().OperatorClient() 48 49 generatedNamespace = SetupGeneratedTestNamespace(genName("operator-group-e2e-")) 50 51 }) 52 53 AfterEach(func() { 54 TearDown(generatedNamespace.GetName()) 55 }) 56 57 It("e2e functionality", func() { 58 59 By(`Create namespace with specific label`) 60 By(`Create CRD`) 61 By(`Create CSV in operator namespace`) 62 By(`Create operator group that watches namespace and uses specific label`) 63 By(`Verify operator group status contains correct status`) 64 By(`Verify csv in target namespace exists, has copied status, has annotations`) 65 By(`Verify deployments have correct namespace annotation`) 66 By(`(Verify that the operator can operate in the target namespace)`) 67 By(`Update CSV to support no InstallModes`) 68 By(`Verify the CSV transitions to FAILED`) 69 By(`Verify the copied CSV transitions to FAILED`) 70 By(`Delete CSV`) 71 By(`Verify copied CVS is deleted`) 72 73 log := func(s string) { 74 GinkgoT().Logf("%s: %s", time.Now().Format("15:04:05.9999"), s) 75 } 76 77 csvName := genName("another-csv-") // must be lowercase for DNS-1123 validation 78 79 opGroupNamespace := genName(generatedNamespace.GetName() + "-") 80 matchingLabel := map[string]string{"inGroup": opGroupNamespace} 81 otherNamespaceName := genName(opGroupNamespace + "-") 82 bothNamespaceNames := opGroupNamespace + "," + otherNamespaceName 83 84 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 85 ObjectMeta: metav1.ObjectMeta{ 86 Name: opGroupNamespace, 87 Labels: matchingLabel, 88 }, 89 }, metav1.CreateOptions{}) 90 require.NoError(GinkgoT(), err) 91 defer func() { 92 err = c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), opGroupNamespace, metav1.DeleteOptions{}) 93 require.NoError(GinkgoT(), err) 94 }() 95 96 otherNamespace := corev1.Namespace{ 97 ObjectMeta: metav1.ObjectMeta{ 98 Name: otherNamespaceName, 99 Labels: matchingLabel, 100 }, 101 } 102 createdOtherNamespace, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &otherNamespace, metav1.CreateOptions{}) 103 require.NoError(GinkgoT(), err) 104 defer func() { 105 err = c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), otherNamespaceName, metav1.DeleteOptions{}) 106 require.NoError(GinkgoT(), err) 107 }() 108 109 log("Creating CRD") 110 mainCRDPlural := genName("opgroup") 111 mainCRD := newCRD(mainCRDPlural) 112 cleanupCRD, err := createCRD(c, mainCRD) 113 require.NoError(GinkgoT(), err) 114 defer cleanupCRD() 115 116 log("Creating operator group") 117 operatorGroup := v1.OperatorGroup{ 118 ObjectMeta: metav1.ObjectMeta{ 119 Name: genName("e2e-operator-group-"), 120 Namespace: opGroupNamespace, 121 }, 122 Spec: v1.OperatorGroupSpec{ 123 Selector: &metav1.LabelSelector{ 124 MatchLabels: matchingLabel, 125 }, 126 }, 127 } 128 _, err = crc.OperatorsV1().OperatorGroups(opGroupNamespace).Create(context.TODO(), &operatorGroup, metav1.CreateOptions{}) 129 require.NoError(GinkgoT(), err) 130 131 By(`fetched namespaces might be in any order`) 132 namespaces := map[string]bool{} 133 namespaces[opGroupNamespace] = true 134 namespaces[createdOtherNamespace.GetName()] = true 135 136 log("Waiting on operator group to have correct status") 137 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 138 fetched, fetchErr := crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get(context.TODO(), operatorGroup.Name, metav1.GetOptions{}) 139 if fetchErr != nil { 140 log(fmt.Sprintf("error getting operatorgroup %s/%s: %v", opGroupNamespace, operatorGroup.Name, err)) 141 return false, nil 142 } 143 if len(namespaces) != len(fetched.Status.Namespaces) { 144 log(fmt.Sprintf("element length mismatch: %v vs %v", namespaces, fetched.Status.Namespaces)) 145 return false, nil 146 } 147 for _, v := range fetched.Status.Namespaces { 148 if !namespaces[v] { 149 log(fmt.Sprintf("element values mismatch: %v vs %v", namespaces, fetched.Status.Namespaces)) 150 return false, nil 151 } 152 } 153 return true, nil 154 }) 155 require.NoError(GinkgoT(), err) 156 157 log("Creating CSV") 158 159 By(`Generate permissions`) 160 serviceAccountName := genName("nginx-sa") 161 permissions := []v1alpha1.StrategyDeploymentPermissions{ 162 { 163 ServiceAccountName: serviceAccountName, 164 Rules: []rbacv1.PolicyRule{ 165 { 166 Verbs: []string{rbacv1.VerbAll}, 167 APIGroups: []string{mainCRD.Spec.Group}, 168 Resources: []string{mainCRDPlural}, 169 }, 170 }, 171 }, 172 } 173 174 By(`Create a new NamedInstallStrategy`) 175 deploymentName := genName("operator-deployment") 176 namedStrategy := newNginxInstallStrategy(deploymentName, permissions, nil) 177 178 aCSV := newCSV(csvName, opGroupNamespace, "", semver.MustParse("0.0.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, &namedStrategy) 179 createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Create(context.TODO(), &aCSV, metav1.CreateOptions{}) 180 require.NoError(GinkgoT(), err) 181 182 serviceAccount := &corev1.ServiceAccount{ 183 ObjectMeta: metav1.ObjectMeta{ 184 Namespace: opGroupNamespace, 185 Name: serviceAccountName, 186 }, 187 } 188 ownerutil.AddNonBlockingOwner(serviceAccount, createdCSV) 189 err = ownerutil.AddOwnerLabels(serviceAccount, createdCSV) 190 require.NoError(GinkgoT(), err) 191 192 role := &rbacv1.Role{ 193 ObjectMeta: metav1.ObjectMeta{ 194 Namespace: opGroupNamespace, 195 Name: serviceAccountName + "-role", 196 }, 197 Rules: permissions[0].Rules, 198 } 199 ownerutil.AddNonBlockingOwner(role, createdCSV) 200 err = ownerutil.AddOwnerLabels(role, createdCSV) 201 require.NoError(GinkgoT(), err) 202 203 roleBinding := &rbacv1.RoleBinding{ 204 ObjectMeta: metav1.ObjectMeta{ 205 Namespace: opGroupNamespace, 206 Name: serviceAccountName + "-rb", 207 }, 208 Subjects: []rbacv1.Subject{ 209 { 210 Kind: "ServiceAccount", 211 Name: serviceAccountName, 212 Namespace: opGroupNamespace, 213 }, 214 }, 215 RoleRef: rbacv1.RoleRef{ 216 Kind: "Role", 217 Name: role.GetName(), 218 }, 219 } 220 ownerutil.AddNonBlockingOwner(roleBinding, createdCSV) 221 err = ownerutil.AddOwnerLabels(roleBinding, createdCSV) 222 require.NoError(GinkgoT(), err) 223 224 _, err = c.CreateServiceAccount(serviceAccount) 225 require.NoError(GinkgoT(), err) 226 _, err = c.CreateRole(role) 227 require.NoError(GinkgoT(), err) 228 _, err = c.CreateRoleBinding(roleBinding) 229 require.NoError(GinkgoT(), err) 230 231 log("wait for CSV to succeed") 232 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 233 fetched, err := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Get(context.TODO(), createdCSV.GetName(), metav1.GetOptions{}) 234 if err != nil { 235 return false, err 236 } 237 log(fmt.Sprintf("%s (%s): %s", fetched.Status.Phase, fetched.Status.Reason, fetched.Status.Message)) 238 return csvSucceededChecker(fetched), nil 239 }) 240 require.NoError(GinkgoT(), err) 241 242 log("Waiting for operator namespace csv to have annotations") 243 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 244 fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Get(context.TODO(), csvName, metav1.GetOptions{}) 245 if fetchErr != nil { 246 if apierrors.IsNotFound(fetchErr) { 247 return false, nil 248 } 249 log(fmt.Sprintf("Error (in %v): %v", generatedNamespace.GetName(), fetchErr.Error())) 250 return false, fetchErr 251 } 252 if checkOperatorGroupAnnotations(fetchedCSV, &operatorGroup, true, bothNamespaceNames) == nil { 253 return true, nil 254 } 255 return false, nil 256 }) 257 require.NoError(GinkgoT(), err) 258 259 log("Waiting for target namespace csv to have annotations (but not target namespaces)") 260 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 261 fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(context.TODO(), csvName, metav1.GetOptions{}) 262 if fetchErr != nil { 263 if apierrors.IsNotFound(fetchErr) { 264 return false, nil 265 } 266 log(fmt.Sprintf("Error (in %v): %v", otherNamespaceName, fetchErr.Error())) 267 return false, fetchErr 268 } 269 if checkOperatorGroupAnnotations(fetchedCSV, &operatorGroup, false, "") == nil { 270 return true, nil 271 } 272 273 return false, nil 274 }) 275 276 log("Checking status on csv in target namespace") 277 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 278 fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(context.TODO(), csvName, metav1.GetOptions{}) 279 if fetchErr != nil { 280 if apierrors.IsNotFound(fetchErr) { 281 return false, nil 282 } 283 GinkgoT().Logf("Error (in %v): %v", otherNamespaceName, fetchErr.Error()) 284 return false, fetchErr 285 } 286 if fetchedCSV.Status.Reason == v1alpha1.CSVReasonCopied { 287 return true, nil 288 } 289 return false, nil 290 }) 291 require.NoError(GinkgoT(), err) 292 293 log("Waiting on deployment to have correct annotations") 294 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 295 createdDeployment, err := c.GetDeployment(opGroupNamespace, deploymentName) 296 if err != nil { 297 if apierrors.IsNotFound(err) { 298 return false, nil 299 } 300 return false, err 301 } 302 if checkOperatorGroupAnnotations(&createdDeployment.Spec.Template, &operatorGroup, true, bothNamespaceNames) == nil { 303 return true, nil 304 } 305 return false, nil 306 }) 307 require.NoError(GinkgoT(), err) 308 309 By(`check rbac in target namespace`) 310 informerFactory := informers.NewSharedInformerFactory(c.KubernetesInterface(), 1*time.Second) 311 roleInformer := informerFactory.Rbac().V1().Roles() 312 roleBindingInformer := informerFactory.Rbac().V1().RoleBindings() 313 clusterRoleInformer := informerFactory.Rbac().V1().ClusterRoles() 314 clusterRoleBindingInformer := informerFactory.Rbac().V1().ClusterRoleBindings() 315 316 By(`kick off informers`) 317 stopCh := make(chan struct{}) 318 defer func() { 319 stopCh <- struct{}{} 320 }() 321 322 for _, informer := range []cache.SharedIndexInformer{roleInformer.Informer(), roleBindingInformer.Informer(), clusterRoleInformer.Informer(), clusterRoleBindingInformer.Informer()} { 323 go func() { 324 defer GinkgoRecover() 325 informer.Run(stopCh) 326 }() 327 328 synced := func() (bool, error) { 329 return informer.HasSynced(), nil 330 } 331 332 By(`wait until the informer has synced to continue`) 333 err := wait.PollUntil(500*time.Millisecond, synced, stopCh) 334 require.NoError(GinkgoT(), err) 335 } 336 337 ruleChecker := install.NewCSVRuleChecker(roleInformer.Lister(), roleBindingInformer.Lister(), clusterRoleInformer.Lister(), clusterRoleBindingInformer.Lister(), &aCSV) 338 339 log("Waiting for operator to have rbac in target namespace") 340 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 341 for _, perm := range permissions { 342 sa, err := c.GetServiceAccount(opGroupNamespace, perm.ServiceAccountName) 343 require.NoError(GinkgoT(), err) 344 for _, rule := range perm.Rules { 345 satisfied, err := ruleChecker.RuleSatisfied(sa, otherNamespaceName, rule) 346 if err != nil { 347 GinkgoT().Log(err.Error()) 348 return false, nil 349 } 350 if !satisfied { 351 return false, nil 352 } 353 } 354 } 355 return true, nil 356 }) 357 358 By(`validate provided API clusterroles for the operatorgroup`) 359 existingClusterRoleList, err := c.KubernetesInterface().RbacV1().ClusterRoles().List(context.TODO(), metav1.ListOptions{ 360 LabelSelector: labels.SelectorFromSet(ownerutil.OwnerLabel(&operatorGroup, "OperatorGroup")).String(), 361 }) 362 require.NoError(GinkgoT(), err) 363 require.Len(GinkgoT(), existingClusterRoleList.Items, 3) 364 365 for _, role := range existingClusterRoleList.Items { 366 if strings.HasSuffix(role.Name, "admin") { 367 adminPolicyRules := []rbacv1.PolicyRule{ 368 {Verbs: []string{"*"}, APIGroups: []string{mainCRD.Spec.Group}, Resources: []string{mainCRDPlural}}, 369 } 370 if assert.Equal(GinkgoT(), adminPolicyRules, role.Rules) == false { 371 fmt.Println(cmp.Diff(adminPolicyRules, role.Rules)) 372 GinkgoT().Fail() 373 } 374 375 } else if strings.HasSuffix(role.Name, "edit") { 376 editPolicyRules := []rbacv1.PolicyRule{ 377 {Verbs: []string{"create", "update", "patch", "delete"}, APIGroups: []string{mainCRD.Spec.Group}, Resources: []string{mainCRDPlural}}, 378 } 379 if assert.Equal(GinkgoT(), editPolicyRules, role.Rules) == false { 380 fmt.Println(cmp.Diff(editPolicyRules, role.Rules)) 381 GinkgoT().Fail() 382 } 383 } else if strings.HasSuffix(role.Name, "view") { 384 viewPolicyRules := []rbacv1.PolicyRule{ 385 {Verbs: []string{"get"}, APIGroups: []string{"apiextensions.k8s.io"}, Resources: []string{"customresourcedefinitions"}, ResourceNames: []string{mainCRD.Name}}, 386 {Verbs: []string{"get", "list", "watch"}, APIGroups: []string{mainCRD.Spec.Group}, Resources: []string{mainCRDPlural}}, 387 } 388 if assert.Equal(GinkgoT(), viewPolicyRules, role.Rules) == false { 389 fmt.Println(cmp.Diff(viewPolicyRules, role.Rules)) 390 GinkgoT().Fail() 391 } 392 } 393 } 394 395 By(`Unsupport all InstallModes`) 396 log("unsupporting all csv installmodes") 397 fetchedCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Get(context.TODO(), csvName, metav1.GetOptions{}) 398 require.NoError(GinkgoT(), err, "could not fetch csv") 399 fetchedCSV.Spec.InstallModes = []v1alpha1.InstallMode{} 400 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(fetchedCSV.GetNamespace()).Update(context.TODO(), fetchedCSV, metav1.UpdateOptions{}) 401 require.NoError(GinkgoT(), err, "could not update csv installmodes") 402 403 By(`Ensure CSV fails`) 404 _, err = fetchCSV(crc, opGroupNamespace, csvName, csvFailedChecker) 405 require.NoError(GinkgoT(), err, "csv did not transition to failed as expected") 406 407 By(`ensure deletion cleans up copied CSV`) 408 log("deleting parent csv") 409 err = crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Delete(context.TODO(), csvName, metav1.DeleteOptions{}) 410 require.NoError(GinkgoT(), err) 411 412 log("waiting for orphaned csv to be deleted") 413 err = waitForDelete(func() error { 414 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(context.TODO(), csvName, metav1.GetOptions{}) 415 return err 416 }) 417 require.NoError(GinkgoT(), err) 418 419 err = crc.OperatorsV1().OperatorGroups(opGroupNamespace).Delete(context.TODO(), operatorGroup.Name, metav1.DeleteOptions{}) 420 require.NoError(GinkgoT(), err) 421 GinkgoT().Log("Waiting for OperatorGroup RBAC to be garbage collected") 422 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 423 _, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), operatorGroup.Name+"-admin", metav1.GetOptions{}) 424 if err == nil { 425 return false, nil 426 } 427 return true, err 428 }) 429 require.True(GinkgoT(), apierrors.IsNotFound(err)) 430 431 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 432 _, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), operatorGroup.Name+"-edit", metav1.GetOptions{}) 433 if err == nil { 434 return false, nil 435 } 436 return true, err 437 }) 438 require.True(GinkgoT(), apierrors.IsNotFound(err)) 439 440 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 441 _, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), operatorGroup.Name+"-view", metav1.GetOptions{}) 442 if err == nil { 443 return false, nil 444 } 445 return true, err 446 }) 447 require.True(GinkgoT(), apierrors.IsNotFound(err)) 448 }) 449 It("role aggregation", func() { 450 451 By(`kubectl -n a8v4sw auth can-i create alp999.cluster.com --as system:serviceaccount:a8v4sw:padmin-xqdfz`) 452 453 By(`Generate namespaceA`) 454 By(`Generate operatorGroupA - OwnNamespace`) 455 By(`Generate csvA in namespaceA with all installmodes supported`) 456 By(`Create crd so csv succeeds`) 457 By(`Ensure clusterroles created and aggregated for access provided APIs`) 458 459 nsA := genName("a") 460 GinkgoT().Logf("generating namespaceA: %s", nsA) 461 c := newKubeClient() 462 for _, ns := range []string{nsA} { 463 namespace := &corev1.Namespace{ 464 ObjectMeta: metav1.ObjectMeta{ 465 Name: ns, 466 }, 467 } 468 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) 469 require.NoError(GinkgoT(), err) 470 defer func(name string) { 471 require.NoError(GinkgoT(), c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{})) 472 }(ns) 473 } 474 475 groupAName := genName("a") 476 GinkgoT().Logf("Generate operatorGroupA - OwnNamespace: %s", groupAName) 477 groupA := newOperatorGroup(nsA, groupAName, nil, nil, []string{nsA}, false) 478 _, err := crc.OperatorsV1().OperatorGroups(nsA).Create(context.TODO(), groupA, metav1.CreateOptions{}) 479 require.NoError(GinkgoT(), err) 480 defer func() { 481 require.NoError(GinkgoT(), crc.OperatorsV1().OperatorGroups(nsA).Delete(context.TODO(), groupA.GetName(), metav1.DeleteOptions{})) 482 }() 483 484 crdAName := genName("a") 485 strategyName := genName("dep-") 486 csvAName := "nginx-a" 487 GinkgoT().Logf("Generate csv (%s/%s) with crd %s and with all installmodes supported: %s", nsA, csvAName, crdAName, strategyName) 488 crd := newCRD(crdAName) 489 namedStrategy := newNginxInstallStrategy(strategyName, nil, nil) 490 csvA := newCSV(csvAName, nsA, "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, &namedStrategy) 491 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Create(context.TODO(), &csvA, metav1.CreateOptions{}) 492 require.NoError(GinkgoT(), err) 493 defer func() { 494 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Delete(context.TODO(), csvA.GetName(), metav1.DeleteOptions{})) 495 }() 496 497 GinkgoT().Logf("Create crd %s so csv %s/%s succeeds", crdAName, nsA, csvAName) 498 cleanupCRD, err := createCRD(c, crd) 499 require.NoError(GinkgoT(), err) 500 defer cleanupCRD() 501 502 _, err = fetchCSV(crc, nsA, csvA.GetName(), csvSucceededChecker) 503 require.NoError(GinkgoT(), err) 504 505 depName := genName("hat-server") 506 GinkgoT().Logf("Create csv %s/%s for an apiserver", nsA, depName) 507 mockGroup := fmt.Sprintf("hats.%s.redhat.com", genName("")) 508 version := "v1alpha1" 509 mockGroupVersion := strings.Join([]string{mockGroup, version}, "/") 510 mockKinds := []string{"fez", "fedora"} 511 mockNames := []string{"fezs", "fedoras"} 512 depSpec := newMockExtServerDeployment(depName, []mockGroupVersionKind{{depName, mockGroupVersion, mockKinds, 5443}}) 513 strategy := v1alpha1.StrategyDetailsDeployment{ 514 DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ 515 { 516 Name: depName, 517 Spec: depSpec, 518 }, 519 }, 520 } 521 522 owned := make([]v1alpha1.APIServiceDescription, len(mockKinds)) 523 for i, kind := range mockKinds { 524 owned[i] = v1alpha1.APIServiceDescription{ 525 Name: mockNames[i], 526 Group: mockGroup, 527 Version: version, 528 Kind: kind, 529 DeploymentName: depName, 530 ContainerPort: int32(5443), 531 DisplayName: kind, 532 Description: fmt.Sprintf("A %s", kind), 533 } 534 } 535 536 csvB := v1alpha1.ClusterServiceVersion{ 537 Spec: v1alpha1.ClusterServiceVersionSpec{ 538 MinKubeVersion: "0.0.0", 539 InstallModes: []v1alpha1.InstallMode{ 540 { 541 Type: v1alpha1.InstallModeTypeOwnNamespace, 542 Supported: true, 543 }, 544 { 545 Type: v1alpha1.InstallModeTypeSingleNamespace, 546 Supported: true, 547 }, 548 { 549 Type: v1alpha1.InstallModeTypeMultiNamespace, 550 Supported: true, 551 }, 552 { 553 Type: v1alpha1.InstallModeTypeAllNamespaces, 554 Supported: true, 555 }, 556 }, 557 InstallStrategy: v1alpha1.NamedInstallStrategy{ 558 StrategyName: v1alpha1.InstallStrategyNameDeployment, 559 StrategySpec: strategy, 560 }, 561 APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ 562 Owned: owned, 563 }, 564 }, 565 } 566 csvB.SetName(depName) 567 568 GinkgoT().Logf("Create the APIService CSV %s/%s", nsA, depName) 569 cleanupCSV, err := createCSV(c, crc, csvB, nsA, false, true) 570 require.NoError(GinkgoT(), err) 571 defer cleanupCSV() 572 573 GinkgoT().Logf("Fetch the APIService CSV %s/%s", nsA, depName) 574 _, err = fetchCSV(crc, nsA, csvB.GetName(), csvSucceededChecker) 575 require.NoError(GinkgoT(), err) 576 577 GinkgoT().Logf("Ensure clusterroles created and aggregated for access provided APIs") 578 padmin, cleanupPadmin := createProjectAdmin(GinkgoT(), c, nsA) 579 defer cleanupPadmin() 580 581 GinkgoT().Logf("Check CRD access aggregated") 582 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 583 res, err := c.KubernetesInterface().AuthorizationV1().SubjectAccessReviews().Create(context.TODO(), &authorizationv1.SubjectAccessReview{ 584 Spec: authorizationv1.SubjectAccessReviewSpec{ 585 User: padmin, 586 ResourceAttributes: &authorizationv1.ResourceAttributes{ 587 Namespace: nsA, 588 Group: crd.Spec.Group, 589 Version: crd.Spec.Versions[0].Name, 590 Resource: crd.Spec.Names.Plural, 591 Verb: "create", 592 }, 593 }, 594 }, metav1.CreateOptions{}) 595 if err != nil { 596 return false, err 597 } 598 if res == nil { 599 return false, nil 600 } 601 GinkgoT().Logf("checking padmin for permission: %#v", res) 602 return res.Status.Allowed, nil 603 }) 604 require.NoError(GinkgoT(), err) 605 606 GinkgoT().Logf("Check apiserver access aggregated") 607 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 608 res, err := c.KubernetesInterface().AuthorizationV1().SubjectAccessReviews().Create(context.TODO(), &authorizationv1.SubjectAccessReview{ 609 Spec: authorizationv1.SubjectAccessReviewSpec{ 610 User: padmin, 611 ResourceAttributes: &authorizationv1.ResourceAttributes{ 612 Namespace: nsA, 613 Group: mockGroup, 614 Version: version, 615 Resource: mockNames[1], 616 Verb: "create", 617 }, 618 }, 619 }, metav1.CreateOptions{}) 620 if err != nil { 621 return false, err 622 } 623 if res == nil { 624 return false, nil 625 } 626 GinkgoT().Logf("checking padmin for permission: %#v", res) 627 return res.Status.Allowed, nil 628 }) 629 require.NoError(GinkgoT(), err) 630 }) 631 It("install mode support", func() { 632 633 By(`Generate namespaceA`) 634 By(`Generate namespaceB`) 635 By(`Create operatorGroupA in namespaceA that selects namespaceA`) 636 By(`Generate csvA with an unfulfilled required CRD and no supported InstallModes in namespaceA`) 637 By(`Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"`) 638 By(`Update csvA to have OwnNamespace supported=true`) 639 By(`Ensure csvA transitions to Succeeded`) 640 By(`Update operatorGroupA's target namespaces to select namespaceB`) 641 By(`Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"`) 642 By(`Update csvA to have SingleNamespace supported=true`) 643 By(`Ensure csvA transitions to Pending`) 644 By(`Update operatorGroupA's target namespaces to select namespaceA and namespaceB`) 645 By(`Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"`) 646 By(`Update csvA to have MultiNamespace supported=true`) 647 By(`Ensure csvA transitions to Pending`) 648 By(`Update operatorGroupA to select all namespaces`) 649 By(`Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"`) 650 By(`Update csvA to have AllNamespaces supported=true`) 651 By(`Ensure csvA transitions to Pending`) 652 653 By(`Generate namespaceA and namespaceB`) 654 nsA := genName("a") 655 nsB := genName("b") 656 657 c := newKubeClient() 658 for _, ns := range []string{nsA, nsB} { 659 namespace := &corev1.Namespace{ 660 ObjectMeta: metav1.ObjectMeta{ 661 Name: ns, 662 }, 663 } 664 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) 665 require.NoError(GinkgoT(), err) 666 defer func(name string) { 667 require.NoError(GinkgoT(), c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{})) 668 }(ns) 669 } 670 671 By(`Generate operatorGroupA`) 672 groupA := newOperatorGroup(nsA, genName("a"), nil, nil, []string{nsA}, false) 673 _, err := crc.OperatorsV1().OperatorGroups(nsA).Create(context.TODO(), groupA, metav1.CreateOptions{}) 674 require.NoError(GinkgoT(), err) 675 defer func() { 676 require.NoError(GinkgoT(), crc.OperatorsV1().OperatorGroups(nsA).Delete(context.TODO(), groupA.GetName(), metav1.DeleteOptions{})) 677 }() 678 679 By(`Generate csvA in namespaceA with no supported InstallModes`) 680 crd := newCRD(genName("b")) 681 namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) 682 csv := newCSV("nginx-a", nsA, "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{crd}, &namedStrategy) 683 csvA := &csv 684 csvA.Spec.InstallModes = []v1alpha1.InstallMode{ 685 { 686 Type: v1alpha1.InstallModeTypeOwnNamespace, 687 Supported: false, 688 }, 689 { 690 Type: v1alpha1.InstallModeTypeSingleNamespace, 691 Supported: false, 692 }, 693 { 694 Type: v1alpha1.InstallModeTypeMultiNamespace, 695 Supported: false, 696 }, 697 { 698 Type: v1alpha1.InstallModeTypeAllNamespaces, 699 Supported: false, 700 }, 701 } 702 csvA, err = crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Create(context.TODO(), csvA, metav1.CreateOptions{}) 703 require.NoError(GinkgoT(), err) 704 defer func() { 705 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Delete(context.TODO(), csvA.GetName(), metav1.DeleteOptions{})) 706 }() 707 708 By(`Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"`) 709 failedWithUnsupportedOperatorGroup := func(csv *v1alpha1.ClusterServiceVersion) bool { 710 return csvFailedChecker(csv) && csv.Status.Reason == v1alpha1.CSVReasonUnsupportedOperatorGroup 711 } 712 csvA, err = fetchCSV(crc, nsA, csvA.GetName(), failedWithUnsupportedOperatorGroup) 713 require.NoError(GinkgoT(), err) 714 715 By(`Update csvA to have OwnNamespace supported=true`) 716 csvA.Spec.InstallModes = []v1alpha1.InstallMode{ 717 { 718 Type: v1alpha1.InstallModeTypeOwnNamespace, 719 Supported: true, 720 }, 721 { 722 Type: v1alpha1.InstallModeTypeSingleNamespace, 723 Supported: false, 724 }, 725 { 726 Type: v1alpha1.InstallModeTypeMultiNamespace, 727 Supported: false, 728 }, 729 { 730 Type: v1alpha1.InstallModeTypeAllNamespaces, 731 Supported: false, 732 }, 733 } 734 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Update(context.TODO(), csvA, metav1.UpdateOptions{}) 735 require.NoError(GinkgoT(), err) 736 737 By(`Create crd so csv succeeds`) 738 cleanupCRD, err := createCRD(c, crd) 739 require.NoError(GinkgoT(), err) 740 defer cleanupCRD() 741 742 By(`Ensure csvA transitions to Succeeded`) 743 csvA, err = fetchCSV(crc, nsA, csvA.GetName(), csvSucceededChecker) 744 require.NoError(GinkgoT(), err) 745 746 By(`Update operatorGroupA's target namespaces to select namespaceB`) 747 groupA, err = crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 748 require.NoError(GinkgoT(), err) 749 groupA.Spec.TargetNamespaces = []string{nsB} 750 _, err = crc.OperatorsV1().OperatorGroups(nsA).Update(context.TODO(), groupA, metav1.UpdateOptions{}) 751 require.NoError(GinkgoT(), err) 752 753 By(`Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"`) 754 csvA, err = fetchCSV(crc, nsA, csvA.GetName(), failedWithUnsupportedOperatorGroup) 755 require.NoError(GinkgoT(), err) 756 757 By(`Update csvA to have SingleNamespace supported=true`) 758 csvA.Spec.InstallModes = []v1alpha1.InstallMode{ 759 { 760 Type: v1alpha1.InstallModeTypeOwnNamespace, 761 Supported: true, 762 }, 763 { 764 Type: v1alpha1.InstallModeTypeSingleNamespace, 765 Supported: true, 766 }, 767 { 768 Type: v1alpha1.InstallModeTypeMultiNamespace, 769 Supported: false, 770 }, 771 { 772 Type: v1alpha1.InstallModeTypeAllNamespaces, 773 Supported: false, 774 }, 775 } 776 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Update(context.TODO(), csvA, metav1.UpdateOptions{}) 777 require.NoError(GinkgoT(), err) 778 779 By(`Ensure csvA transitions to Succeeded`) 780 csvA, err = fetchCSV(crc, nsA, csvA.GetName(), csvSucceededChecker) 781 require.NoError(GinkgoT(), err) 782 783 By(`Update operatorGroupA's target namespaces to select namespaceA and namespaceB`) 784 groupA, err = crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 785 require.NoError(GinkgoT(), err) 786 groupA.Spec.TargetNamespaces = []string{nsA, nsB} 787 _, err = crc.OperatorsV1().OperatorGroups(nsA).Update(context.TODO(), groupA, metav1.UpdateOptions{}) 788 require.NoError(GinkgoT(), err) 789 790 By(`Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"`) 791 csvA, err = fetchCSV(crc, nsA, csvA.GetName(), failedWithUnsupportedOperatorGroup) 792 require.NoError(GinkgoT(), err) 793 794 By(`Update csvA to have MultiNamespace supported=true`) 795 csvA.Spec.InstallModes = []v1alpha1.InstallMode{ 796 { 797 Type: v1alpha1.InstallModeTypeOwnNamespace, 798 Supported: true, 799 }, 800 { 801 Type: v1alpha1.InstallModeTypeSingleNamespace, 802 Supported: true, 803 }, 804 { 805 Type: v1alpha1.InstallModeTypeMultiNamespace, 806 Supported: true, 807 }, 808 { 809 Type: v1alpha1.InstallModeTypeAllNamespaces, 810 Supported: false, 811 }, 812 } 813 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Update(context.TODO(), csvA, metav1.UpdateOptions{}) 814 require.NoError(GinkgoT(), err) 815 816 By(`Ensure csvA transitions to Succeeded`) 817 csvA, err = fetchCSV(crc, nsA, csvA.GetName(), csvSucceededChecker) 818 require.NoError(GinkgoT(), err) 819 820 By(`Update operatorGroupA's target namespaces to select all namespaces`) 821 groupA, err = crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 822 require.NoError(GinkgoT(), err) 823 groupA.Spec.TargetNamespaces = []string{} 824 _, err = crc.OperatorsV1().OperatorGroups(nsA).Update(context.TODO(), groupA, metav1.UpdateOptions{}) 825 require.NoError(GinkgoT(), err) 826 827 By(`Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"`) 828 csvA, err = fetchCSV(crc, nsA, csvA.GetName(), failedWithUnsupportedOperatorGroup) 829 require.NoError(GinkgoT(), err) 830 831 By(`Update csvA to have AllNamespaces supported=true`) 832 csvA.Spec.InstallModes = []v1alpha1.InstallMode{ 833 { 834 Type: v1alpha1.InstallModeTypeOwnNamespace, 835 Supported: true, 836 }, 837 { 838 Type: v1alpha1.InstallModeTypeSingleNamespace, 839 Supported: true, 840 }, 841 { 842 Type: v1alpha1.InstallModeTypeMultiNamespace, 843 Supported: true, 844 }, 845 { 846 Type: v1alpha1.InstallModeTypeAllNamespaces, 847 Supported: true, 848 }, 849 } 850 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Update(context.TODO(), csvA, metav1.UpdateOptions{}) 851 require.NoError(GinkgoT(), err) 852 853 By(`Ensure csvA transitions to Pending`) 854 csvA, err = fetchCSV(crc, nsA, csvA.GetName(), csvSucceededChecker) 855 require.NoError(GinkgoT(), err) 856 }) 857 It("[FLAKE] intersection", func() { 858 859 By(`Generate namespaceA`) 860 By(`Generate namespaceB`) 861 By(`Generate namespaceC`) 862 By(`Generate namespaceD`) 863 By(`Generate namespaceE`) 864 By(`Generate operatorGroupD in namespaceD that selects namespace D and E`) 865 By(`Generate csvD in namespaceD`) 866 By(`Wait for csvD to be successful`) 867 By(`Wait for csvD to have a CSV with copied status in namespace E`) 868 By(`Wait for operatorGroupD to have providedAPI annotation with crdD's Kind.version.group`) 869 By(`Generate operatorGroupA in namespaceA that selects AllNamespaces`) 870 By(`Generate csvD in namespaceA`) 871 By(`Wait for csvD to fail with status "InterOperatorGroupOwnerConflict"`) 872 By(`Ensure operatorGroupA's providedAPIs are empty`) 873 By(`Ensure csvD in namespaceD is still successful`) 874 By(`Generate csvA in namespaceA that owns crdA`) 875 By(`Wait for csvA to be successful`) 876 By(`Ensure clusterroles created and aggregated for accessing provided APIs`) 877 By(`Wait for operatorGroupA to have providedAPI annotation with crdA's Kind.version.group in its providedAPIs annotation`) 878 By(`Wait for csvA to have a CSV with copied status in namespace D`) 879 By(`Ensure csvA retains the operatorgroup annotations for operatorgroupA`) 880 By(`Wait for csvA to have a CSV with copied status in namespace C`) 881 By(`Generate operatorGroupB in namespaceB that selects namespace C`) 882 By(`Generate csvB in namespaceB that owns crdA`) 883 By(`Wait for csvB to fail with status "InterOperatorGroupOwnerConflict"`) 884 By(`Delete csvA`) 885 By(`Wait for crdA's Kind.version.group to be removed from operatorGroupA's providedAPIs annotation`) 886 By(`Ensure csvA's deployments are deleted`) 887 By(`Wait for csvB to be successful`) 888 By(`Wait for operatorGroupB to have providedAPI annotation with crdB's Kind.version.group`) 889 By(`Wait for csvB to have a CSV with a copied status in namespace C`) 890 891 By(`Create a catalog for csvA, csvB, and csvD`) 892 pkgA := genName("a-") 893 pkgB := genName("b-") 894 pkgD := genName("d-") 895 pkgAStable := pkgA + "-stable" 896 pkgBStable := pkgB + "-stable" 897 pkgDStable := pkgD + "-stable" 898 stableChannel := "stable" 899 strategyA := newNginxInstallStrategy(pkgAStable, nil, nil) 900 strategyB := newNginxInstallStrategy(pkgBStable, nil, nil) 901 strategyD := newNginxInstallStrategy(pkgDStable, nil, nil) 902 crdA := newCRD(genName(pkgA)) 903 crdB := newCRD(genName(pkgB)) 904 crdD := newCRD(genName(pkgD)) 905 kvgA := fmt.Sprintf("%s.%s.%s", crdA.Spec.Names.Kind, crdA.Spec.Versions[0].Name, crdA.Spec.Group) 906 kvgB := fmt.Sprintf("%s.%s.%s", crdB.Spec.Names.Kind, crdB.Spec.Versions[0].Name, crdB.Spec.Group) 907 kvgD := fmt.Sprintf("%s.%s.%s", crdD.Spec.Names.Kind, crdD.Spec.Versions[0].Name, crdD.Spec.Group) 908 csvA := newCSV(pkgAStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crdA}, nil, &strategyA) 909 csvB := newCSV(pkgBStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crdA, crdB}, nil, &strategyB) 910 csvD := newCSV(pkgDStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crdD}, nil, &strategyD) 911 912 By(`Create namespaces`) 913 nsA, nsB, nsC, nsD, nsE := genName("a-"), genName("b-"), genName("c-"), genName("d-"), genName("e-") 914 for _, ns := range []string{nsA, nsB, nsC, nsD, nsE} { 915 namespace := &corev1.Namespace{ 916 ObjectMeta: metav1.ObjectMeta{ 917 Name: ns, 918 }, 919 } 920 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) 921 require.NoError(GinkgoT(), err) 922 defer func(name string) { 923 require.NoError(GinkgoT(), c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{})) 924 }(ns) 925 } 926 927 By(`Create the initial catalogsources`) 928 manifests := []registry.PackageManifest{ 929 { 930 PackageName: pkgA, 931 Channels: []registry.PackageChannel{ 932 {Name: stableChannel, CurrentCSVName: pkgAStable}, 933 }, 934 DefaultChannelName: stableChannel, 935 }, 936 { 937 PackageName: pkgB, 938 Channels: []registry.PackageChannel{ 939 {Name: stableChannel, CurrentCSVName: pkgBStable}, 940 }, 941 DefaultChannelName: stableChannel, 942 }, 943 { 944 PackageName: pkgD, 945 Channels: []registry.PackageChannel{ 946 {Name: stableChannel, CurrentCSVName: pkgDStable}, 947 }, 948 DefaultChannelName: stableChannel, 949 }, 950 } 951 952 catalog := genName("catalog-") 953 _, cleanupCatalogSource := createInternalCatalogSource(c, crc, catalog, nsA, manifests, []apiextensionsv1.CustomResourceDefinition{crdA, crdD, crdB}, []v1alpha1.ClusterServiceVersion{csvA, csvB, csvD}) 954 defer cleanupCatalogSource() 955 _, err := fetchCatalogSourceOnStatus(crc, catalog, nsA, catalogSourceRegistryPodSynced()) 956 require.NoError(GinkgoT(), err) 957 _, cleanupCatalogSource = createInternalCatalogSource(c, crc, catalog, nsB, manifests, []apiextensionsv1.CustomResourceDefinition{crdA, crdD, crdB}, []v1alpha1.ClusterServiceVersion{csvA, csvB, csvD}) 958 defer cleanupCatalogSource() 959 _, err = fetchCatalogSourceOnStatus(crc, catalog, nsB, catalogSourceRegistryPodSynced()) 960 require.NoError(GinkgoT(), err) 961 _, cleanupCatalogSource = createInternalCatalogSource(c, crc, catalog, nsD, manifests, []apiextensionsv1.CustomResourceDefinition{crdA, crdD, crdB}, []v1alpha1.ClusterServiceVersion{csvA, csvB, csvD}) 962 defer cleanupCatalogSource() 963 _, err = fetchCatalogSourceOnStatus(crc, catalog, nsD, catalogSourceRegistryPodSynced()) 964 require.NoError(GinkgoT(), err) 965 966 By(`Create operatorgroups`) 967 groupA := newOperatorGroup(nsA, genName("a-"), nil, nil, nil, false) 968 groupB := newOperatorGroup(nsB, genName("b-"), nil, nil, []string{nsC}, false) 969 groupD := newOperatorGroup(nsD, genName("d-"), nil, nil, []string{nsD, nsE}, false) 970 for _, group := range []*v1.OperatorGroup{groupA, groupB, groupD} { 971 _, err := crc.OperatorsV1().OperatorGroups(group.GetNamespace()).Create(context.TODO(), group, metav1.CreateOptions{}) 972 require.NoError(GinkgoT(), err) 973 defer func(namespace, name string) { 974 require.NoError(GinkgoT(), crc.OperatorsV1().OperatorGroups(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})) 975 }(group.GetNamespace(), group.GetName()) 976 } 977 978 By(`Create subscription for csvD in namespaceD`) 979 subDName := genName("d-") 980 cleanupSubD := createSubscriptionForCatalog(crc, nsD, subDName, catalog, pkgD, stableChannel, pkgDStable, v1alpha1.ApprovalAutomatic) 981 defer cleanupSubD() 982 subD, err := fetchSubscription(crc, nsD, subDName, subscriptionHasInstallPlanChecker()) 983 require.NoError(GinkgoT(), err) 984 require.NotNil(GinkgoT(), subD) 985 986 By(`Await csvD's success`) 987 _, err = fetchCSV(crc, nsD, csvD.GetName(), csvSucceededChecker) 988 require.NoError(GinkgoT(), err) 989 990 By(`Await csvD's copy in namespaceE`) 991 _, err = fetchCSV(crc, nsE, csvD.GetName(), csvCopiedChecker) 992 require.NoError(GinkgoT(), err) 993 994 By(`Await annotation on groupD`) 995 q := func() (metav1.ObjectMeta, error) { 996 g, err := crc.OperatorsV1().OperatorGroups(nsD).Get(context.TODO(), groupD.GetName(), metav1.GetOptions{}) 997 return g.ObjectMeta, err 998 } 999 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgD})) 1000 1001 By(`Create subscription for csvD2 in namespaceA`) 1002 subD2Name := genName("d2-") 1003 cleanupSubD2 := createSubscriptionForCatalog(crc, nsA, subD2Name, catalog, pkgD, stableChannel, pkgDStable, v1alpha1.ApprovalAutomatic) 1004 defer cleanupSubD2() 1005 subD2, err := fetchSubscription(crc, nsA, subD2Name, subscriptionHasInstallPlanChecker()) 1006 require.NoError(GinkgoT(), err) 1007 require.NotNil(GinkgoT(), subD2) 1008 1009 By(`Await csvD2's failure`) 1010 csvD2, err := fetchCSV(crc, nsA, csvD.GetName(), csvFailedChecker) 1011 require.NoError(GinkgoT(), err) 1012 require.Equal(GinkgoT(), v1alpha1.CSVReasonInterOperatorGroupOwnerConflict, csvD2.Status.Reason) 1013 1014 By(`Ensure groupA's annotations are blank`) 1015 q = func() (metav1.ObjectMeta, error) { 1016 g, err := crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 1017 return g.ObjectMeta, err 1018 } 1019 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{})) 1020 1021 By(`Ensure csvD is still successful`) 1022 _, err = fetchCSV(crc, nsD, csvD.GetName(), csvSucceededChecker) 1023 require.NoError(GinkgoT(), err) 1024 1025 By(`Create subscription for csvA in namespaceA`) 1026 subAName := genName("a-") 1027 cleanupSubA := createSubscriptionForCatalog(crc, nsA, subAName, catalog, pkgA, stableChannel, pkgAStable, v1alpha1.ApprovalAutomatic) 1028 defer cleanupSubA() 1029 subA, err := fetchSubscription(crc, nsA, subAName, subscriptionHasInstallPlanChecker()) 1030 require.NoError(GinkgoT(), err) 1031 require.NotNil(GinkgoT(), subA) 1032 1033 By(`Await csvA's success`) 1034 _, err = fetchCSV(crc, nsA, csvA.GetName(), csvSucceededChecker) 1035 require.NoError(GinkgoT(), err) 1036 1037 By(`Ensure clusterroles created and aggregated for access provided APIs`) 1038 padmin, cleanupPadmin := createProjectAdmin(GinkgoT(), c, nsA) 1039 defer cleanupPadmin() 1040 1041 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1042 res, err := c.KubernetesInterface().AuthorizationV1().SubjectAccessReviews().Create(context.TODO(), &authorizationv1.SubjectAccessReview{ 1043 Spec: authorizationv1.SubjectAccessReviewSpec{ 1044 User: padmin, 1045 ResourceAttributes: &authorizationv1.ResourceAttributes{ 1046 Namespace: nsA, 1047 Group: crdA.Spec.Group, 1048 Version: crdA.Spec.Versions[0].Name, 1049 Resource: crdA.Spec.Names.Plural, 1050 Verb: "create", 1051 }, 1052 }, 1053 }, metav1.CreateOptions{}) 1054 if err != nil { 1055 return false, err 1056 } 1057 if res == nil { 1058 return false, nil 1059 } 1060 GinkgoT().Log("checking padmin for permission") 1061 return res.Status.Allowed, nil 1062 }) 1063 require.NoError(GinkgoT(), err) 1064 1065 By(`Await annotation on groupA`) 1066 q = func() (metav1.ObjectMeta, error) { 1067 g, err := crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 1068 return g.ObjectMeta, err 1069 } 1070 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgA})) 1071 1072 By(`Wait for csvA to have a CSV with copied status in namespace D`) 1073 csvAinNsD, err := fetchCSV(crc, nsD, csvA.GetName(), csvCopiedChecker) 1074 require.NoError(GinkgoT(), err) 1075 1076 By(`trigger a resync of operatorgropuD`) 1077 fetchedGroupD, err := crc.OperatorsV1().OperatorGroups(nsD).Get(context.TODO(), groupD.GetName(), metav1.GetOptions{}) 1078 require.NoError(GinkgoT(), err) 1079 1080 fetchedGroupD.Annotations["bump"] = "update" 1081 _, err = crc.OperatorsV1().OperatorGroups(nsD).Update(context.TODO(), fetchedGroupD, metav1.UpdateOptions{}) 1082 require.NoError(GinkgoT(), err) 1083 1084 By(`Ensure csvA retains the operatorgroup annotations for operatorgroupA`) 1085 csvAinNsD, err = fetchCSV(crc, nsD, csvA.GetName(), csvCopiedChecker) 1086 require.NoError(GinkgoT(), err) 1087 1088 require.Equal(GinkgoT(), groupA.GetName(), csvAinNsD.Annotations[v1.OperatorGroupAnnotationKey]) 1089 require.Equal(GinkgoT(), nsA, csvAinNsD.Annotations[v1.OperatorGroupNamespaceAnnotationKey]) 1090 require.Equal(GinkgoT(), nsA, csvAinNsD.Labels[v1alpha1.CopiedLabelKey]) 1091 1092 By(`Await csvA's copy in namespaceC`) 1093 _, err = fetchCSV(crc, nsC, csvA.GetName(), csvCopiedChecker) 1094 require.NoError(GinkgoT(), err) 1095 1096 By(`Create subscription for csvB in namespaceB`) 1097 subBName := genName("b-") 1098 cleanupSubB := createSubscriptionForCatalog(crc, nsB, subBName, catalog, pkgB, stableChannel, pkgBStable, v1alpha1.ApprovalAutomatic) 1099 defer cleanupSubB() 1100 subB, err := fetchSubscription(crc, nsB, subBName, subscriptionHasInstallPlanChecker()) 1101 require.NoError(GinkgoT(), err) 1102 require.NotNil(GinkgoT(), subB) 1103 1104 By(`Await csvB's failure`) 1105 fetchedB, err := fetchCSV(crc, nsB, csvB.GetName(), csvFailedChecker) 1106 require.NoError(GinkgoT(), err) 1107 require.Equal(GinkgoT(), v1alpha1.CSVReasonInterOperatorGroupOwnerConflict, fetchedB.Status.Reason) 1108 1109 By(`Ensure no annotation on groupB`) 1110 q = func() (metav1.ObjectMeta, error) { 1111 g, err := crc.OperatorsV1().OperatorGroups(nsB).Get(context.TODO(), groupB.GetName(), metav1.GetOptions{}) 1112 return g.ObjectMeta, err 1113 } 1114 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{})) 1115 1116 By(`Delete csvA`) 1117 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Delete(context.TODO(), csvA.GetName(), metav1.DeleteOptions{})) 1118 1119 By(`Ensure annotations are removed from groupA`) 1120 q = func() (metav1.ObjectMeta, error) { 1121 g, err := crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 1122 return g.ObjectMeta, err 1123 } 1124 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: ""})) 1125 1126 By(`Ensure csvA's deployment is deleted`) 1127 require.NoError(GinkgoT(), waitForDeploymentToDelete(generatedNamespace.GetName(), pkgAStable, c)) 1128 1129 By(`Await csvB's success`) 1130 _, err = fetchCSV(crc, nsB, csvB.GetName(), csvSucceededChecker) 1131 require.NoError(GinkgoT(), err) 1132 1133 By(`Await csvB's copy in namespace C`) 1134 _, err = fetchCSV(crc, nsC, csvB.GetName(), csvCopiedChecker) 1135 require.NoError(GinkgoT(), err) 1136 1137 By(`Ensure annotations exist on group B`) 1138 q = func() (metav1.ObjectMeta, error) { 1139 g, err := crc.OperatorsV1().OperatorGroups(nsB).Get(context.TODO(), groupB.GetName(), metav1.GetOptions{}) 1140 return g.ObjectMeta, err 1141 } 1142 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: strings.Join([]string{kvgA, kvgB}, ",")})) 1143 }) 1144 It("static provider", func() { 1145 1146 By(`Generate namespaceA`) 1147 By(`Generate namespaceB`) 1148 By(`Generate namespaceC`) 1149 By(`Generate namespaceD`) 1150 By(`Create static operatorGroupA in namespaceA that targets namespaceD with providedAPIs annotation containing KindA.version.group`) 1151 By(`Create operatorGroupB in namespaceB that targets all namespaces`) 1152 By(`Create operatorGroupC in namespaceC that targets namespaceC`) 1153 By(`Create csvA in namespaceB that provides KindA.version.group`) 1154 By(`Wait for csvA in namespaceB to fail`) 1155 By(`Ensure no providedAPI annotations on operatorGroupB`) 1156 By(`Ensure providedAPI annotations are unchanged on operatorGroupA`) 1157 By(`Create csvA in namespaceC`) 1158 By(`Wait for csvA in namespaceC to succeed`) 1159 By(`Ensure KindA.version.group providedAPI annotation on operatorGroupC`) 1160 By(`Create csvB in namespaceB that provides KindB.version.group`) 1161 By(`Wait for csvB to succeed`) 1162 By(`Wait for csvB to be copied to namespaceA, namespaceC, and namespaceD`) 1163 By(`Wait for KindB.version.group to exist in operatorGroupB's providedAPIs annotation`) 1164 By(`Add namespaceD to operatorGroupC's targetNamespaces`) 1165 By(`Wait for csvA in namespaceC to FAIL with status "InterOperatorGroupOwnerConflict"`) 1166 By(`Wait for KindA.version.group providedAPI annotation to be removed from operatorGroupC's providedAPIs annotation`) 1167 By(`Ensure KindA.version.group providedAPI annotation on operatorGroupA`) 1168 1169 By(`Create a catalog for csvA, csvB`) 1170 pkgA := genName("a-") 1171 pkgB := genName("b-") 1172 pkgAStable := pkgA + "-stable" 1173 pkgBStable := pkgB + "-stable" 1174 stableChannel := "stable" 1175 strategyA := newNginxInstallStrategy(pkgAStable, nil, nil) 1176 strategyB := newNginxInstallStrategy(pkgBStable, nil, nil) 1177 crdA := newCRD(genName(pkgA)) 1178 crdB := newCRD(genName(pkgB)) 1179 kvgA := fmt.Sprintf("%s.%s.%s", crdA.Spec.Names.Kind, crdA.Spec.Versions[0].Name, crdA.Spec.Group) 1180 kvgB := fmt.Sprintf("%s.%s.%s", crdB.Spec.Names.Kind, crdB.Spec.Versions[0].Name, crdB.Spec.Group) 1181 csvA := newCSV(pkgAStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crdA}, nil, &strategyA) 1182 csvB := newCSV(pkgBStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crdB}, nil, &strategyB) 1183 1184 By(`Create namespaces`) 1185 nsA, nsB, nsC, nsD := genName("a-"), genName("b-"), genName("c-"), genName("d-") 1186 1187 for _, ns := range []string{nsA, nsB, nsC, nsD} { 1188 namespace := &corev1.Namespace{ 1189 ObjectMeta: metav1.ObjectMeta{ 1190 Name: ns, 1191 }, 1192 } 1193 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) 1194 require.NoError(GinkgoT(), err) 1195 defer func(name string) { 1196 require.NoError(GinkgoT(), c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{})) 1197 }(ns) 1198 } 1199 1200 By(`Create the initial catalogsources`) 1201 manifests := []registry.PackageManifest{ 1202 { 1203 PackageName: pkgA, 1204 Channels: []registry.PackageChannel{ 1205 {Name: stableChannel, CurrentCSVName: pkgAStable}, 1206 }, 1207 DefaultChannelName: stableChannel, 1208 }, 1209 { 1210 PackageName: pkgB, 1211 Channels: []registry.PackageChannel{ 1212 {Name: stableChannel, CurrentCSVName: pkgBStable}, 1213 }, 1214 DefaultChannelName: stableChannel, 1215 }, 1216 } 1217 1218 By(`Create catalog in namespaceB and namespaceC`) 1219 catalog := genName("catalog-") 1220 _, cleanupCatalogSource := createInternalCatalogSource(c, crc, catalog, nsB, manifests, []apiextensionsv1.CustomResourceDefinition{crdA, crdB}, []v1alpha1.ClusterServiceVersion{csvA, csvB}) 1221 defer cleanupCatalogSource() 1222 _, err := fetchCatalogSourceOnStatus(crc, catalog, nsB, catalogSourceRegistryPodSynced()) 1223 require.NoError(GinkgoT(), err) 1224 _, cleanupCatalogSource = createInternalCatalogSource(c, crc, catalog, nsC, manifests, []apiextensionsv1.CustomResourceDefinition{crdA, crdB}, []v1alpha1.ClusterServiceVersion{csvA, csvB}) 1225 defer cleanupCatalogSource() 1226 _, err = fetchCatalogSourceOnStatus(crc, catalog, nsC, catalogSourceRegistryPodSynced()) 1227 require.NoError(GinkgoT(), err) 1228 1229 By(`Create OperatorGroups`) 1230 groupA := newOperatorGroup(nsA, genName("a-"), map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgA}, nil, []string{nsD}, true) 1231 groupB := newOperatorGroup(nsB, genName("b-"), nil, nil, nil, false) 1232 groupC := newOperatorGroup(nsC, genName("d-"), nil, nil, []string{nsC}, false) 1233 for _, group := range []*v1.OperatorGroup{groupA, groupB, groupC} { 1234 _, err := crc.OperatorsV1().OperatorGroups(group.GetNamespace()).Create(context.TODO(), group, metav1.CreateOptions{}) 1235 require.NoError(GinkgoT(), err) 1236 defer func(namespace, name string) { 1237 require.NoError(GinkgoT(), crc.OperatorsV1().OperatorGroups(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})) 1238 }(group.GetNamespace(), group.GetName()) 1239 } 1240 1241 By(`Create subscription for csvA in namespaceB`) 1242 subAName := genName("a-") 1243 cleanupSubA := createSubscriptionForCatalog(crc, nsB, subAName, catalog, pkgA, stableChannel, pkgAStable, v1alpha1.ApprovalAutomatic) 1244 defer cleanupSubA() 1245 subA, err := fetchSubscription(crc, nsB, subAName, subscriptionHasInstallPlanChecker()) 1246 require.NoError(GinkgoT(), err) 1247 require.NotNil(GinkgoT(), subA) 1248 1249 By(`Await csvA's failure`) 1250 fetchedCSVA, err := fetchCSV(crc, nsB, csvA.GetName(), csvFailedChecker) 1251 require.NoError(GinkgoT(), err) 1252 require.Equal(GinkgoT(), v1alpha1.CSVReasonInterOperatorGroupOwnerConflict, fetchedCSVA.Status.Reason) 1253 1254 By(`Ensure operatorGroupB doesn't have providedAPI annotation`) 1255 q := func() (metav1.ObjectMeta, error) { 1256 g, err := crc.OperatorsV1().OperatorGroups(nsB).Get(context.TODO(), groupB.GetName(), metav1.GetOptions{}) 1257 return g.ObjectMeta, err 1258 } 1259 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{})) 1260 1261 By(`Ensure operatorGroupA still has KindA.version.group in its providedAPIs annotation`) 1262 q = func() (metav1.ObjectMeta, error) { 1263 g, err := crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 1264 return g.ObjectMeta, err 1265 } 1266 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgA})) 1267 1268 By(`Create subscription for csvA in namespaceC`) 1269 cleanupSubAC := createSubscriptionForCatalog(crc, nsC, subAName, catalog, pkgA, stableChannel, pkgAStable, v1alpha1.ApprovalAutomatic) 1270 defer cleanupSubAC() 1271 subAC, err := fetchSubscription(crc, nsC, subAName, subscriptionHasInstallPlanChecker()) 1272 require.NoError(GinkgoT(), err) 1273 require.NotNil(GinkgoT(), subAC) 1274 1275 By(`Await csvA's success`) 1276 _, err = fetchCSV(crc, nsC, csvA.GetName(), csvSucceededChecker) 1277 require.NoError(GinkgoT(), err) 1278 1279 By(`Ensure operatorGroupC has KindA.version.group in its providedAPIs annotation`) 1280 q = func() (metav1.ObjectMeta, error) { 1281 g, err := crc.OperatorsV1().OperatorGroups(nsC).Get(context.TODO(), groupC.GetName(), metav1.GetOptions{}) 1282 return g.ObjectMeta, err 1283 } 1284 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgA})) 1285 1286 By(`Ensure operatorGroupA still has KindA.version.group in its providedAPIs annotation`) 1287 q = func() (metav1.ObjectMeta, error) { 1288 g, err := crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 1289 return g.ObjectMeta, err 1290 } 1291 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgA})) 1292 1293 By(`Create subscription for csvB in namespaceB`) 1294 subBName := genName("b-") 1295 cleanupSubB := createSubscriptionForCatalog(crc, nsB, subBName, catalog, pkgB, stableChannel, pkgBStable, v1alpha1.ApprovalAutomatic) 1296 defer cleanupSubB() 1297 subB, err := fetchSubscription(crc, nsB, subBName, subscriptionHasInstallPlanChecker()) 1298 require.NoError(GinkgoT(), err) 1299 require.NotNil(GinkgoT(), subB) 1300 1301 By(`Await csvB's success`) 1302 _, err = fetchCSV(crc, nsB, csvB.GetName(), csvSucceededChecker) 1303 require.NoError(GinkgoT(), err) 1304 1305 By(`Await copied csvBs`) 1306 _, err = fetchCSV(crc, nsA, csvB.GetName(), csvCopiedChecker) 1307 require.NoError(GinkgoT(), err) 1308 _, err = fetchCSV(crc, nsC, csvB.GetName(), csvCopiedChecker) 1309 require.NoError(GinkgoT(), err) 1310 _, err = fetchCSV(crc, nsD, csvB.GetName(), csvCopiedChecker) 1311 require.NoError(GinkgoT(), err) 1312 1313 By(`Ensure operatorGroupB has KindB.version.group in its providedAPIs annotation`) 1314 q = func() (metav1.ObjectMeta, error) { 1315 g, err := crc.OperatorsV1().OperatorGroups(nsB).Get(context.TODO(), groupB.GetName(), metav1.GetOptions{}) 1316 return g.ObjectMeta, err 1317 } 1318 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgB})) 1319 1320 By(`Ensure operatorGroupA still has KindA.version.group in its providedAPIs annotation`) 1321 q = func() (metav1.ObjectMeta, error) { 1322 g, err := crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 1323 return g.ObjectMeta, err 1324 } 1325 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgA})) 1326 1327 By(`Add namespaceD to operatorGroupC's targetNamespaces`) 1328 groupC, err = crc.OperatorsV1().OperatorGroups(groupC.GetNamespace()).Get(context.TODO(), groupC.GetName(), metav1.GetOptions{}) 1329 require.NoError(GinkgoT(), err) 1330 groupC.Spec.TargetNamespaces = []string{nsC, nsD} 1331 _, err = crc.OperatorsV1().OperatorGroups(groupC.GetNamespace()).Update(context.TODO(), groupC, metav1.UpdateOptions{}) 1332 require.NoError(GinkgoT(), err) 1333 1334 By(`Wait for csvA in namespaceC to fail with status "InterOperatorGroupOwnerConflict"`) 1335 fetchedCSVA, err = fetchCSV(crc, nsC, csvA.GetName(), csvFailedChecker) 1336 require.NoError(GinkgoT(), err) 1337 require.Equal(GinkgoT(), v1alpha1.CSVReasonInterOperatorGroupOwnerConflict, fetchedCSVA.Status.Reason) 1338 1339 By(`Wait for crdA's providedAPIs to be removed from operatorGroupC's providedAPIs annotation`) 1340 q = func() (metav1.ObjectMeta, error) { 1341 g, err := crc.OperatorsV1().OperatorGroups(nsC).Get(context.TODO(), groupC.GetName(), metav1.GetOptions{}) 1342 return g.ObjectMeta, err 1343 } 1344 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: ""})) 1345 1346 By(`Ensure operatorGroupA still has KindA.version.group in its providedAPIs annotation`) 1347 q = func() (metav1.ObjectMeta, error) { 1348 g, err := crc.OperatorsV1().OperatorGroups(nsA).Get(context.TODO(), groupA.GetName(), metav1.GetOptions{}) 1349 return g.ObjectMeta, err 1350 } 1351 require.NoError(GinkgoT(), awaitAnnotations(GinkgoT(), q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgA})) 1352 }) 1353 1354 // TODO: Test OperatorGroup resizing collisions 1355 // TODO: Test Subscriptions with depedencies and transitive dependencies in intersecting OperatorGroups 1356 // TODO: Test Subscription upgrade paths with + and - providedAPIs 1357 It("CSV copy watching all namespaces", func() { 1358 1359 csvName := genName("another-csv-") // must be lowercase for DNS-1123 validation 1360 1361 opGroupNamespace := generatedNamespace.GetName() 1362 matchingLabel := map[string]string{"inGroup": opGroupNamespace} 1363 otherNamespaceName := genName(opGroupNamespace + "-") 1364 GinkgoT().Log("Creating CRD") 1365 mainCRDPlural := genName("opgroup-") 1366 mainCRD := newCRD(mainCRDPlural) 1367 cleanupCRD, err := createCRD(c, mainCRD) 1368 require.NoError(GinkgoT(), err) 1369 defer cleanupCRD() 1370 operatorGroup, err := crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get(context.TODO(), fmt.Sprintf("%v-operatorgroup", opGroupNamespace), metav1.GetOptions{}) 1371 require.NoError(GinkgoT(), err) 1372 1373 expectedOperatorGroupStatus := v1.OperatorGroupStatus{ 1374 Namespaces: []string{metav1.NamespaceAll}, 1375 } 1376 GinkgoT().Log("Waiting on operator group to have correct status") 1377 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1378 fetched, fetchErr := crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get(context.TODO(), operatorGroup.Name, metav1.GetOptions{}) 1379 if fetchErr != nil { 1380 return false, fetchErr 1381 } 1382 if len(fetched.Status.Namespaces) > 0 { 1383 require.ElementsMatch(GinkgoT(), expectedOperatorGroupStatus.Namespaces, fetched.Status.Namespaces) 1384 fmt.Println(fetched.Status.Namespaces) 1385 return true, nil 1386 } 1387 return false, nil 1388 }) 1389 require.NoError(GinkgoT(), err) 1390 GinkgoT().Log("Creating CSV") 1391 By(`Generate permissions`) 1392 serviceAccountName := genName("nginx-sa") 1393 permissions := []v1alpha1.StrategyDeploymentPermissions{ 1394 { 1395 ServiceAccountName: serviceAccountName, 1396 Rules: []rbacv1.PolicyRule{ 1397 { 1398 Verbs: []string{rbacv1.VerbAll}, 1399 APIGroups: []string{mainCRD.Spec.Group}, 1400 Resources: []string{mainCRDPlural}, 1401 }, 1402 }, 1403 }, 1404 } 1405 1406 serviceAccount := &corev1.ServiceAccount{ 1407 ObjectMeta: metav1.ObjectMeta{ 1408 Namespace: opGroupNamespace, 1409 Name: serviceAccountName, 1410 }, 1411 } 1412 role := &rbacv1.Role{ 1413 ObjectMeta: metav1.ObjectMeta{ 1414 Namespace: opGroupNamespace, 1415 Name: serviceAccountName + "-role", 1416 }, 1417 Rules: permissions[0].Rules, 1418 } 1419 roleBinding := &rbacv1.RoleBinding{ 1420 ObjectMeta: metav1.ObjectMeta{ 1421 Namespace: opGroupNamespace, 1422 Name: serviceAccountName + "-rb", 1423 }, 1424 Subjects: []rbacv1.Subject{ 1425 { 1426 Kind: "ServiceAccount", 1427 Name: serviceAccountName, 1428 Namespace: opGroupNamespace, 1429 }, 1430 }, 1431 RoleRef: rbacv1.RoleRef{ 1432 Kind: "Role", 1433 Name: role.GetName(), 1434 }, 1435 } 1436 serviceAccount, err = c.CreateServiceAccount(serviceAccount) 1437 require.NoError(GinkgoT(), err) 1438 defer func() { 1439 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 1440 fmt.Printf("Skipping cleanup of serviceaccount %s/%s...\n", serviceAccount.GetNamespace(), serviceAccount.GetName()) 1441 return 1442 } 1443 c.DeleteServiceAccount(serviceAccount.GetNamespace(), serviceAccount.GetName(), metav1.NewDeleteOptions(0)) 1444 }() 1445 createdRole, err := c.CreateRole(role) 1446 require.NoError(GinkgoT(), err) 1447 defer func() { 1448 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 1449 fmt.Printf("Skipping cleanup of role %s/%s...\n", role.GetNamespace(), role.GetName()) 1450 return 1451 } 1452 c.DeleteRole(role.GetNamespace(), role.GetName(), metav1.NewDeleteOptions(0)) 1453 }() 1454 createdRoleBinding, err := c.CreateRoleBinding(roleBinding) 1455 require.NoError(GinkgoT(), err) 1456 defer func() { 1457 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 1458 fmt.Printf("Skipping cleanup of role binding %s/%s...\n", roleBinding.GetNamespace(), roleBinding.GetName()) 1459 return 1460 } 1461 c.DeleteRoleBinding(roleBinding.GetNamespace(), roleBinding.GetName(), metav1.NewDeleteOptions(0)) 1462 }() 1463 By(`Create a new NamedInstallStrategy`) 1464 deploymentName := genName("operator-deployment") 1465 namedStrategy := newNginxInstallStrategy(deploymentName, permissions, nil) 1466 1467 aCSV := newCSV(csvName, opGroupNamespace, "", semver.MustParse("0.0.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, &namedStrategy) 1468 1469 By(`Use the It spec name as label after stripping whitespaces`) 1470 aCSV.Labels = map[string]string{"label": K8sSafeCurrentTestDescription()} 1471 createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Create(context.TODO(), &aCSV, metav1.CreateOptions{}) 1472 require.NoError(GinkgoT(), err) 1473 1474 err = ownerutil.AddOwnerLabels(createdRole, createdCSV) 1475 require.NoError(GinkgoT(), err) 1476 createdRole.Labels[install.OLMManagedLabelKey] = install.OLMManagedLabelValue 1477 _, err = c.UpdateRole(createdRole) 1478 require.NoError(GinkgoT(), err) 1479 1480 err = ownerutil.AddOwnerLabels(createdRoleBinding, createdCSV) 1481 require.NoError(GinkgoT(), err) 1482 createdRoleBinding.Labels[install.OLMManagedLabelKey] = install.OLMManagedLabelValue 1483 _, err = c.UpdateRoleBinding(createdRoleBinding) 1484 require.NoError(GinkgoT(), err) 1485 GinkgoT().Log("wait for CSV to succeed") 1486 _, err = fetchCSV(crc, opGroupNamespace, createdCSV.GetName(), csvSucceededChecker) 1487 require.NoError(GinkgoT(), err) 1488 GinkgoT().Log("wait for roles to be promoted to clusterroles") 1489 var fetchedRole *rbacv1.ClusterRole 1490 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1491 fetchedRole, err = c.GetClusterRole(role.GetName()) 1492 if err != nil { 1493 if apierrors.IsNotFound(err) { 1494 return false, nil 1495 } 1496 return false, err 1497 } 1498 return true, nil 1499 }) 1500 require.EqualValues(GinkgoT(), append(role.Rules, rbacv1.PolicyRule{ 1501 Verbs: []string{"get", "list", "watch"}, 1502 APIGroups: []string{""}, 1503 Resources: []string{"namespaces"}, 1504 }), fetchedRole.Rules) 1505 var fetchedRoleBinding *rbacv1.ClusterRoleBinding 1506 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1507 fetchedRoleBinding, err = c.GetClusterRoleBinding(roleBinding.GetName()) 1508 if err != nil { 1509 if apierrors.IsNotFound(err) { 1510 return false, nil 1511 } 1512 return false, err 1513 } 1514 return true, nil 1515 }) 1516 require.EqualValues(GinkgoT(), roleBinding.Subjects, fetchedRoleBinding.Subjects) 1517 require.EqualValues(GinkgoT(), roleBinding.RoleRef.Name, fetchedRoleBinding.RoleRef.Name) 1518 require.EqualValues(GinkgoT(), "rbac.authorization.k8s.io", fetchedRoleBinding.RoleRef.APIGroup) 1519 require.EqualValues(GinkgoT(), "ClusterRole", fetchedRoleBinding.RoleRef.Kind) 1520 GinkgoT().Log("ensure operator was granted namespace list permission") 1521 res, err := c.KubernetesInterface().AuthorizationV1().SubjectAccessReviews().Create(context.TODO(), &authorizationv1.SubjectAccessReview{ 1522 Spec: authorizationv1.SubjectAccessReviewSpec{ 1523 User: "system:serviceaccount:" + opGroupNamespace + ":" + serviceAccountName, 1524 ResourceAttributes: &authorizationv1.ResourceAttributes{ 1525 Group: corev1.GroupName, 1526 Version: "v1", 1527 Resource: "namespaces", 1528 Verb: "list", 1529 }, 1530 }, 1531 }, metav1.CreateOptions{}) 1532 require.NoError(GinkgoT(), err) 1533 require.True(GinkgoT(), res.Status.Allowed, "got %#v", res.Status) 1534 GinkgoT().Log("Waiting for operator namespace csv to have annotations") 1535 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1536 fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Get(context.TODO(), csvName, metav1.GetOptions{}) 1537 if fetchErr != nil { 1538 if apierrors.IsNotFound(fetchErr) { 1539 return false, nil 1540 } 1541 GinkgoT().Logf("Error (in %v): %v", generatedNamespace.GetName(), fetchErr.Error()) 1542 return false, fetchErr 1543 } 1544 if checkOperatorGroupAnnotations(fetchedCSV, operatorGroup, true, corev1.NamespaceAll) == nil { 1545 return true, nil 1546 } 1547 return false, nil 1548 }) 1549 require.NoError(GinkgoT(), err) 1550 1551 csvList, err := crc.OperatorsV1alpha1().ClusterServiceVersions(corev1.NamespaceAll).List(context.TODO(), metav1.ListOptions{LabelSelector: fmt.Sprintf("label=%s", K8sSafeCurrentTestDescription())}) 1552 require.NoError(GinkgoT(), err) 1553 GinkgoT().Logf("Found CSV count of %v", len(csvList.Items)) 1554 GinkgoT().Logf("Create other namespace %s", otherNamespaceName) 1555 otherNamespace := corev1.Namespace{ 1556 ObjectMeta: metav1.ObjectMeta{ 1557 Name: otherNamespaceName, 1558 Labels: matchingLabel, 1559 }, 1560 } 1561 _, err = c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &otherNamespace, metav1.CreateOptions{}) 1562 require.NoError(GinkgoT(), err) 1563 defer func() { 1564 err = c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), otherNamespaceName, metav1.DeleteOptions{}) 1565 require.NoError(GinkgoT(), err) 1566 }() 1567 GinkgoT().Log("Waiting to ensure copied CSV shows up in other namespace") 1568 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1569 fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(context.TODO(), csvName, metav1.GetOptions{}) 1570 if fetchErr != nil { 1571 if apierrors.IsNotFound(fetchErr) { 1572 return false, nil 1573 } 1574 GinkgoT().Logf("Error (in %v): %v", otherNamespaceName, fetchErr.Error()) 1575 return false, fetchErr 1576 } 1577 if checkOperatorGroupAnnotations(fetchedCSV, operatorGroup, false, "") == nil { 1578 return true, nil 1579 } 1580 return false, nil 1581 }) 1582 require.NoError(GinkgoT(), err) 1583 GinkgoT( // verify created CSV is cleaned up after operator group is "contracted" 1584 ).Log("Modifying operator group to no longer watch all namespaces") 1585 currentOperatorGroup, err := crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get(context.TODO(), operatorGroup.Name, metav1.GetOptions{}) 1586 require.NoError(GinkgoT(), err) 1587 currentOperatorGroup.Spec.TargetNamespaces = []string{opGroupNamespace} 1588 _, err = crc.OperatorsV1().OperatorGroups(opGroupNamespace).Update(context.TODO(), currentOperatorGroup, metav1.UpdateOptions{}) 1589 require.NoError(GinkgoT(), err) 1590 defer func() { 1591 GinkgoT().Log("Re-modifying operator group to be watching all namespaces") 1592 currentOperatorGroup, err = crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get(context.TODO(), operatorGroup.Name, metav1.GetOptions{}) 1593 require.NoError(GinkgoT(), err) 1594 currentOperatorGroup.Spec = v1.OperatorGroupSpec{} 1595 _, err = crc.OperatorsV1().OperatorGroups(opGroupNamespace).Update(context.TODO(), currentOperatorGroup, metav1.UpdateOptions{}) 1596 require.NoError(GinkgoT(), err) 1597 }() 1598 1599 err = wait.Poll(pollInterval, 2*pollDuration, func() (bool, error) { 1600 _, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(context.TODO(), csvName, metav1.GetOptions{}) 1601 if fetchErr != nil { 1602 if apierrors.IsNotFound(fetchErr) { 1603 return true, nil 1604 } 1605 GinkgoT().Logf("Error (in %v): %v", opGroupNamespace, fetchErr.Error()) 1606 return false, fetchErr 1607 } 1608 return false, nil 1609 }) 1610 require.NoError(GinkgoT(), err) 1611 }) 1612 It("insufficient permissions resolve via RBAC", func() { 1613 1614 log := func(s string) { 1615 GinkgoT().Logf("%s: %s", time.Now().Format("15:04:05.9999"), s) 1616 } 1617 1618 csvName := genName("another-csv-") 1619 1620 newNamespaceName := genName(generatedNamespace.GetName() + "-") 1621 1622 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 1623 ObjectMeta: metav1.ObjectMeta{ 1624 Name: newNamespaceName, 1625 }, 1626 }, metav1.CreateOptions{}) 1627 require.NoError(GinkgoT(), err) 1628 defer func() { 1629 err = c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), newNamespaceName, metav1.DeleteOptions{}) 1630 require.NoError(GinkgoT(), err) 1631 }() 1632 1633 log("Creating CRD") 1634 mainCRDPlural := genName("opgroup") 1635 mainCRD := newCRD(mainCRDPlural) 1636 cleanupCRD, err := createCRD(c, mainCRD) 1637 require.NoError(GinkgoT(), err) 1638 defer cleanupCRD() 1639 1640 log("Creating operator group") 1641 serviceAccountName := genName("nginx-sa") 1642 By(`intentionally creating an operator group without a service account already existing`) 1643 operatorGroup := v1.OperatorGroup{ 1644 ObjectMeta: metav1.ObjectMeta{ 1645 Name: genName("e2e-operator-group-"), 1646 Namespace: newNamespaceName, 1647 }, 1648 Spec: v1.OperatorGroupSpec{ 1649 ServiceAccountName: serviceAccountName, 1650 TargetNamespaces: []string{newNamespaceName}, 1651 }, 1652 } 1653 _, err = crc.OperatorsV1().OperatorGroups(newNamespaceName).Create(context.TODO(), &operatorGroup, metav1.CreateOptions{}) 1654 require.NoError(GinkgoT(), err) 1655 1656 log("Creating CSV") 1657 1658 By(`Create a new NamedInstallStrategy`) 1659 deploymentName := genName("operator-deployment") 1660 namedStrategy := newNginxInstallStrategy(deploymentName, nil, nil) 1661 1662 aCSV := newCSV(csvName, newNamespaceName, "", semver.MustParse("0.0.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, &namedStrategy) 1663 createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(newNamespaceName).Create(context.TODO(), &aCSV, metav1.CreateOptions{}) 1664 require.NoError(GinkgoT(), err) 1665 1666 serviceAccount := &corev1.ServiceAccount{ 1667 ObjectMeta: metav1.ObjectMeta{ 1668 Namespace: newNamespaceName, 1669 Name: serviceAccountName, 1670 }, 1671 } 1672 ownerutil.AddNonBlockingOwner(serviceAccount, createdCSV) 1673 err = ownerutil.AddOwnerLabels(serviceAccount, createdCSV) 1674 require.NoError(GinkgoT(), err) 1675 1676 _, err = c.CreateServiceAccount(serviceAccount) 1677 require.NoError(GinkgoT(), err) 1678 By(`Create token secret for the serviceaccount`) 1679 _, cleanupSE := newTokenSecret(c, newNamespaceName, serviceAccount.GetName()) 1680 defer cleanupSE() 1681 1682 log("wait for CSV to fail") 1683 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1684 fetched, err := crc.OperatorsV1alpha1().ClusterServiceVersions(newNamespaceName).Get(context.TODO(), createdCSV.GetName(), metav1.GetOptions{}) 1685 if err != nil { 1686 return false, err 1687 } 1688 log(fmt.Sprintf("%s (%s): %s", fetched.Status.Phase, fetched.Status.Reason, fetched.Status.Message)) 1689 return csvFailedChecker(fetched), nil 1690 }) 1691 require.NoError(GinkgoT(), err) 1692 1693 By(`now add cluster admin permissions to service account`) 1694 role := &rbacv1.Role{ 1695 ObjectMeta: metav1.ObjectMeta{ 1696 Namespace: newNamespaceName, 1697 Name: serviceAccountName + "-role", 1698 }, 1699 Rules: []rbacv1.PolicyRule{ 1700 { 1701 Verbs: []string{"*"}, 1702 APIGroups: []string{"*"}, 1703 Resources: []string{"*"}, 1704 }, 1705 }, 1706 } 1707 ownerutil.AddNonBlockingOwner(role, createdCSV) 1708 err = ownerutil.AddOwnerLabels(role, createdCSV) 1709 require.NoError(GinkgoT(), err) 1710 1711 roleBinding := &rbacv1.RoleBinding{ 1712 ObjectMeta: metav1.ObjectMeta{ 1713 Namespace: newNamespaceName, 1714 Name: serviceAccountName + "-rb", 1715 }, 1716 Subjects: []rbacv1.Subject{ 1717 { 1718 Kind: "ServiceAccount", 1719 Name: serviceAccountName, 1720 Namespace: newNamespaceName, 1721 }, 1722 }, 1723 RoleRef: rbacv1.RoleRef{ 1724 Kind: "Role", 1725 Name: role.GetName(), 1726 }, 1727 } 1728 ownerutil.AddNonBlockingOwner(roleBinding, createdCSV) 1729 err = ownerutil.AddOwnerLabels(roleBinding, createdCSV) 1730 require.NoError(GinkgoT(), err) 1731 1732 _, err = c.CreateRole(role) 1733 require.NoError(GinkgoT(), err) 1734 _, err = c.CreateRoleBinding(roleBinding) 1735 require.NoError(GinkgoT(), err) 1736 1737 log("wait for CSV to succeeed") 1738 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1739 fetched, err := crc.OperatorsV1alpha1().ClusterServiceVersions(newNamespaceName).Get(context.TODO(), createdCSV.GetName(), metav1.GetOptions{}) 1740 if err != nil { 1741 return false, err 1742 } 1743 log(fmt.Sprintf("%s (%s): %s", fetched.Status.Phase, fetched.Status.Reason, fetched.Status.Message)) 1744 return csvSucceededChecker(fetched), nil 1745 }) 1746 require.NoError(GinkgoT(), err) 1747 }) 1748 It("insufficient permissions resolve via service account removal", func() { 1749 1750 log := func(s string) { 1751 GinkgoT().Logf("%s: %s", time.Now().Format("15:04:05.9999"), s) 1752 } 1753 1754 csvName := genName("another-csv-") 1755 1756 newNamespaceName := genName(generatedNamespace.GetName() + "-") 1757 1758 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 1759 ObjectMeta: metav1.ObjectMeta{ 1760 Name: newNamespaceName, 1761 }, 1762 }, metav1.CreateOptions{}) 1763 require.NoError(GinkgoT(), err) 1764 defer func() { 1765 err = c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), newNamespaceName, metav1.DeleteOptions{}) 1766 require.NoError(GinkgoT(), err) 1767 }() 1768 1769 log("Creating CRD") 1770 mainCRDPlural := genName("opgroup") 1771 mainCRD := newCRD(mainCRDPlural) 1772 cleanupCRD, err := createCRD(c, mainCRD) 1773 require.NoError(GinkgoT(), err) 1774 defer cleanupCRD() 1775 1776 log("Creating operator group") 1777 serviceAccountName := genName("nginx-sa") 1778 By(`intentionally creating an operator group without a service account already existing`) 1779 operatorGroup := v1.OperatorGroup{ 1780 ObjectMeta: metav1.ObjectMeta{ 1781 Name: genName("e2e-operator-group-"), 1782 Namespace: newNamespaceName, 1783 }, 1784 Spec: v1.OperatorGroupSpec{ 1785 ServiceAccountName: serviceAccountName, 1786 TargetNamespaces: []string{newNamespaceName}, 1787 }, 1788 } 1789 _, err = crc.OperatorsV1().OperatorGroups(newNamespaceName).Create(context.TODO(), &operatorGroup, metav1.CreateOptions{}) 1790 require.NoError(GinkgoT(), err) 1791 1792 log("Creating CSV") 1793 1794 By(`Create a new NamedInstallStrategy`) 1795 deploymentName := genName("operator-deployment") 1796 namedStrategy := newNginxInstallStrategy(deploymentName, nil, nil) 1797 1798 aCSV := newCSV(csvName, newNamespaceName, "", semver.MustParse("0.0.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, &namedStrategy) 1799 createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(newNamespaceName).Create(context.TODO(), &aCSV, metav1.CreateOptions{}) 1800 require.NoError(GinkgoT(), err) 1801 1802 serviceAccount := &corev1.ServiceAccount{ 1803 ObjectMeta: metav1.ObjectMeta{ 1804 Namespace: newNamespaceName, 1805 Name: serviceAccountName, 1806 }, 1807 } 1808 ownerutil.AddNonBlockingOwner(serviceAccount, createdCSV) 1809 err = ownerutil.AddOwnerLabels(serviceAccount, createdCSV) 1810 require.NoError(GinkgoT(), err) 1811 1812 _, err = c.CreateServiceAccount(serviceAccount) 1813 require.NoError(GinkgoT(), err) 1814 By(`Create token secret for the serviceaccount`) 1815 _, cleanupSE := newTokenSecret(c, newNamespaceName, serviceAccount.GetName()) 1816 defer cleanupSE() 1817 1818 log("wait for CSV to fail") 1819 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1820 fetched, err := crc.OperatorsV1alpha1().ClusterServiceVersions(newNamespaceName).Get(context.TODO(), createdCSV.GetName(), metav1.GetOptions{}) 1821 if err != nil { 1822 return false, err 1823 } 1824 log(fmt.Sprintf("%s (%s): %s", fetched.Status.Phase, fetched.Status.Reason, fetched.Status.Message)) 1825 return csvFailedChecker(fetched), nil 1826 }) 1827 require.NoError(GinkgoT(), err) 1828 1829 By(`now remove operator group specified service account`) 1830 createdOpGroup, err := crc.OperatorsV1().OperatorGroups(newNamespaceName).Get(context.TODO(), operatorGroup.GetName(), metav1.GetOptions{}) 1831 require.NoError(GinkgoT(), err) 1832 createdOpGroup.Spec.ServiceAccountName = "" 1833 _, err = crc.OperatorsV1().OperatorGroups(newNamespaceName).Update(context.TODO(), createdOpGroup, metav1.UpdateOptions{}) 1834 require.NoError(GinkgoT(), err) 1835 1836 log("wait for CSV to succeeed") 1837 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1838 fetched, err := crc.OperatorsV1alpha1().ClusterServiceVersions(newNamespaceName).Get(context.TODO(), createdCSV.GetName(), metav1.GetOptions{}) 1839 if err != nil { 1840 return false, err 1841 } 1842 log(fmt.Sprintf("%s (%s): %s", fetched.Status.Phase, fetched.Status.Reason, fetched.Status.Message)) 1843 return csvSucceededChecker(fetched), nil 1844 }) 1845 require.NoError(GinkgoT(), err) 1846 }) 1847 1848 It("cleanup csvs with bad namespace annotation", func() { 1849 By(`Versions of OLM at 0.14.1 and older had a bug that would place the wrong namespace annotation on copied CSVs,`) 1850 By(`preventing them from being GCd. This ensures that any leftover CSVs in that state are properly cleared up.`) 1851 1852 csvName := genName("another-csv-") // must be lowercase for DNS-1123 validation 1853 1854 opGroupNamespace := generatedNamespace.GetName() 1855 matchingLabel := map[string]string{"inGroup": opGroupNamespace} 1856 otherNamespaceName := genName(opGroupNamespace + "-") 1857 GinkgoT().Log("Creating CRD") 1858 mainCRDPlural := genName("opgroup-") 1859 mainCRD := newCRD(mainCRDPlural) 1860 cleanupCRD, err := createCRD(c, mainCRD) 1861 require.NoError(GinkgoT(), err) 1862 defer cleanupCRD() 1863 operatorGroup, err := crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get(context.TODO(), fmt.Sprintf("%v-operatorgroup", opGroupNamespace), metav1.GetOptions{}) 1864 require.NoError(GinkgoT(), err) 1865 1866 expectedOperatorGroupStatus := v1.OperatorGroupStatus{ 1867 Namespaces: []string{metav1.NamespaceAll}, 1868 } 1869 GinkgoT().Log("Waiting on operator group to have correct status") 1870 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1871 fetched, fetchErr := crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get(context.TODO(), operatorGroup.Name, metav1.GetOptions{}) 1872 if fetchErr != nil { 1873 return false, fetchErr 1874 } 1875 if len(fetched.Status.Namespaces) > 0 { 1876 require.ElementsMatch(GinkgoT(), expectedOperatorGroupStatus.Namespaces, fetched.Status.Namespaces) 1877 fmt.Println(fetched.Status.Namespaces) 1878 return true, nil 1879 } 1880 return false, nil 1881 }) 1882 require.NoError(GinkgoT(), err) 1883 GinkgoT().Log("Creating CSV") 1884 By(`Generate permissions`) 1885 serviceAccountName := genName("nginx-sa") 1886 permissions := []v1alpha1.StrategyDeploymentPermissions{ 1887 { 1888 ServiceAccountName: serviceAccountName, 1889 Rules: []rbacv1.PolicyRule{ 1890 { 1891 Verbs: []string{rbacv1.VerbAll}, 1892 APIGroups: []string{mainCRD.Spec.Group}, 1893 Resources: []string{mainCRDPlural}, 1894 }, 1895 }, 1896 }, 1897 } 1898 1899 serviceAccount := &corev1.ServiceAccount{ 1900 ObjectMeta: metav1.ObjectMeta{ 1901 Namespace: opGroupNamespace, 1902 Name: serviceAccountName, 1903 }, 1904 } 1905 role := &rbacv1.Role{ 1906 ObjectMeta: metav1.ObjectMeta{ 1907 Namespace: opGroupNamespace, 1908 Name: serviceAccountName + "-role", 1909 }, 1910 Rules: permissions[0].Rules, 1911 } 1912 roleBinding := &rbacv1.RoleBinding{ 1913 ObjectMeta: metav1.ObjectMeta{ 1914 Namespace: opGroupNamespace, 1915 Name: serviceAccountName + "-rb", 1916 }, 1917 Subjects: []rbacv1.Subject{ 1918 { 1919 Kind: "ServiceAccount", 1920 Name: serviceAccountName, 1921 Namespace: opGroupNamespace, 1922 }, 1923 }, 1924 RoleRef: rbacv1.RoleRef{ 1925 Kind: "Role", 1926 Name: role.GetName(), 1927 }, 1928 } 1929 _, err = c.CreateServiceAccount(serviceAccount) 1930 require.NoError(GinkgoT(), err) 1931 defer func() { 1932 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 1933 fmt.Printf("Skipping cleanup of serviceaccount %s/%s...\n", serviceAccount.GetNamespace(), serviceAccount.GetName()) 1934 return 1935 } 1936 c.DeleteServiceAccount(serviceAccount.GetNamespace(), serviceAccount.GetName(), metav1.NewDeleteOptions(0)) 1937 }() 1938 createdRole, err := c.CreateRole(role) 1939 require.NoError(GinkgoT(), err) 1940 defer func() { 1941 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 1942 fmt.Printf("Skipping cleanup of role %s/%s...\n", role.GetNamespace(), role.GetName()) 1943 return 1944 } 1945 c.DeleteRole(role.GetNamespace(), role.GetName(), metav1.NewDeleteOptions(0)) 1946 }() 1947 createdRoleBinding, err := c.CreateRoleBinding(roleBinding) 1948 require.NoError(GinkgoT(), err) 1949 defer func() { 1950 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 1951 fmt.Printf("Skipping cleanup of role binding %s/%s...\n", roleBinding.GetNamespace(), roleBinding.GetName()) 1952 return 1953 } 1954 c.DeleteRoleBinding(roleBinding.GetNamespace(), roleBinding.GetName(), metav1.NewDeleteOptions(0)) 1955 }() 1956 By(`Create a new NamedInstallStrategy`) 1957 deploymentName := genName("operator-deployment") 1958 namedStrategy := newNginxInstallStrategy(deploymentName, permissions, nil) 1959 1960 aCSV := newCSV(csvName, opGroupNamespace, "", semver.MustParse("0.0.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, &namedStrategy) 1961 1962 By(`Use the It spec name as label after stripping whitespaces`) 1963 aCSV.Labels = map[string]string{"label": K8sSafeCurrentTestDescription()} 1964 createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Create(context.TODO(), &aCSV, metav1.CreateOptions{}) 1965 require.NoError(GinkgoT(), err) 1966 1967 err = ownerutil.AddOwnerLabels(createdRole, createdCSV) 1968 require.NoError(GinkgoT(), err) 1969 createdRole.Labels[install.OLMManagedLabelKey] = install.OLMManagedLabelValue 1970 _, err = c.UpdateRole(createdRole) 1971 require.NoError(GinkgoT(), err) 1972 1973 err = ownerutil.AddOwnerLabels(createdRoleBinding, createdCSV) 1974 require.NoError(GinkgoT(), err) 1975 createdRoleBinding.Labels[install.OLMManagedLabelKey] = install.OLMManagedLabelValue 1976 _, err = c.UpdateRoleBinding(createdRoleBinding) 1977 require.NoError(GinkgoT(), err) 1978 GinkgoT().Log("wait for CSV to succeed") 1979 _, err = fetchCSV(crc, opGroupNamespace, createdCSV.GetName(), csvSucceededChecker) 1980 require.NoError(GinkgoT(), err) 1981 GinkgoT().Log("wait for roles to be promoted to clusterroles") 1982 var fetchedRole *rbacv1.ClusterRole 1983 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 1984 fetchedRole, err = c.GetClusterRole(role.GetName()) 1985 if err != nil { 1986 if apierrors.IsNotFound(err) { 1987 return false, nil 1988 } 1989 return false, err 1990 } 1991 return true, nil 1992 }) 1993 require.NoError(GinkgoT(), err) 1994 require.EqualValues(GinkgoT(), append(role.Rules, rbacv1.PolicyRule{ 1995 Verbs: []string{"get", "list", "watch"}, 1996 APIGroups: []string{""}, 1997 Resources: []string{"namespaces"}, 1998 }), fetchedRole.Rules) 1999 var fetchedRoleBinding *rbacv1.ClusterRoleBinding 2000 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 2001 fetchedRoleBinding, err = c.GetClusterRoleBinding(roleBinding.GetName()) 2002 if err != nil { 2003 if apierrors.IsNotFound(err) { 2004 return false, nil 2005 } 2006 return false, err 2007 } 2008 return true, nil 2009 }) 2010 require.NoError(GinkgoT(), err) 2011 require.EqualValues(GinkgoT(), roleBinding.Subjects, fetchedRoleBinding.Subjects) 2012 require.EqualValues(GinkgoT(), roleBinding.RoleRef.Name, fetchedRoleBinding.RoleRef.Name) 2013 require.EqualValues(GinkgoT(), "rbac.authorization.k8s.io", fetchedRoleBinding.RoleRef.APIGroup) 2014 require.EqualValues(GinkgoT(), "ClusterRole", fetchedRoleBinding.RoleRef.Kind) 2015 GinkgoT().Log("ensure operator was granted namespace list permission") 2016 res, err := c.KubernetesInterface().AuthorizationV1().SubjectAccessReviews().Create(context.TODO(), &authorizationv1.SubjectAccessReview{ 2017 Spec: authorizationv1.SubjectAccessReviewSpec{ 2018 User: "system:serviceaccount:" + opGroupNamespace + ":" + serviceAccountName, 2019 ResourceAttributes: &authorizationv1.ResourceAttributes{ 2020 Group: corev1.GroupName, 2021 Version: "v1", 2022 Resource: "namespaces", 2023 Verb: "list", 2024 }, 2025 }, 2026 }, metav1.CreateOptions{}) 2027 require.NoError(GinkgoT(), err) 2028 require.True(GinkgoT(), res.Status.Allowed, "got %#v", res.Status) 2029 GinkgoT().Log("Waiting for operator namespace csv to have annotations") 2030 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 2031 fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Get(context.TODO(), csvName, metav1.GetOptions{}) 2032 if fetchErr != nil { 2033 if apierrors.IsNotFound(fetchErr) { 2034 return false, nil 2035 } 2036 GinkgoT().Logf("Error (in %v): %v", generatedNamespace.GetName(), fetchErr.Error()) 2037 return false, fetchErr 2038 } 2039 if checkOperatorGroupAnnotations(fetchedCSV, operatorGroup, true, corev1.NamespaceAll) == nil { 2040 return true, nil 2041 } 2042 return false, nil 2043 }) 2044 require.NoError(GinkgoT(), err) 2045 2046 csvList, err := crc.OperatorsV1alpha1().ClusterServiceVersions(corev1.NamespaceAll).List(context.TODO(), metav1.ListOptions{LabelSelector: fmt.Sprintf("label=%s", K8sSafeCurrentTestDescription())}) 2047 require.NoError(GinkgoT(), err) 2048 GinkgoT().Logf("Found CSV count of %v", len(csvList.Items)) 2049 GinkgoT().Logf("Create other namespace %s", otherNamespaceName) 2050 otherNamespace := corev1.Namespace{ 2051 ObjectMeta: metav1.ObjectMeta{ 2052 Name: otherNamespaceName, 2053 Labels: matchingLabel, 2054 }, 2055 } 2056 _, err = c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &otherNamespace, metav1.CreateOptions{}) 2057 require.NoError(GinkgoT(), err) 2058 defer func() { 2059 err = c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), otherNamespaceName, metav1.DeleteOptions{}) 2060 require.NoError(GinkgoT(), err) 2061 }() 2062 GinkgoT().Log("Waiting to ensure copied CSV shows up in other namespace") 2063 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 2064 fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(context.TODO(), csvName, metav1.GetOptions{}) 2065 if fetchErr != nil { 2066 if apierrors.IsNotFound(fetchErr) { 2067 return false, nil 2068 } 2069 GinkgoT().Logf("Error (in %v): %v", otherNamespaceName, fetchErr.Error()) 2070 return false, fetchErr 2071 } 2072 if checkOperatorGroupAnnotations(fetchedCSV, operatorGroup, false, "") == nil { 2073 return true, nil 2074 } 2075 return false, nil 2076 }) 2077 require.NoError(GinkgoT(), err) 2078 GinkgoT().Log("Copied CSV showed up in other namespace, giving copied CSV a bad OpertorGroup annotation") 2079 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { 2080 fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(context.TODO(), csvName, metav1.GetOptions{}) 2081 if fetchErr != nil { 2082 return false, fetchErr 2083 } 2084 fetchedCSV.Annotations[v1.OperatorGroupNamespaceAnnotationKey] = fetchedCSV.GetNamespace() 2085 _, updateErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Update(context.TODO(), fetchedCSV, metav1.UpdateOptions{}) 2086 if updateErr != nil { 2087 GinkgoT().Logf("Error updating copied CSV (in %v): %v", otherNamespaceName, updateErr.Error()) 2088 return false, updateErr 2089 } 2090 return true, nil 2091 }) 2092 require.NoError(GinkgoT(), err) 2093 GinkgoT().Log("Done updating copied CSV with bad annotation OperatorGroup, waiting for CSV to be gc'd") 2094 err = wait.Poll(pollInterval, 2*pollDuration, func() (bool, error) { 2095 csv, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(context.TODO(), csvName, metav1.GetOptions{}) 2096 if fetchErr != nil { 2097 if apierrors.IsNotFound(fetchErr) { 2098 return true, nil 2099 } 2100 GinkgoT().Logf("Error (in %v): %v", opGroupNamespace, fetchErr.Error()) 2101 return false, fetchErr 2102 } 2103 By(`The CSV with the wrong annotation could have been replaced with a new copied CSV by this time`) 2104 By(`If we find a CSV in the namespace, and it contains the correct annotation, it means the CSV`) 2105 By(`with the wrong annotation was GCed`) 2106 if csv.Annotations[v1.OperatorGroupNamespaceAnnotationKey] != csv.GetNamespace() { 2107 return true, nil 2108 } 2109 return false, nil 2110 }) 2111 require.NoError(GinkgoT(), err) 2112 }) 2113 It("OperatorGroupLabels", func() { 2114 2115 By(`Create the namespaces that will have an OperatorGroup Label applied.`) 2116 testNamespaceA := genName("namespace-a-") 2117 testNamespaceB := genName("namespace-b-") 2118 testNamespaceC := genName("namespace-c-") 2119 testNamespaces := []string{ 2120 testNamespaceA, testNamespaceB, testNamespaceC, 2121 } 2122 2123 By(`Create the namespaces`) 2124 for _, namespace := range testNamespaces { 2125 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 2126 ObjectMeta: metav1.ObjectMeta{ 2127 Name: namespace, 2128 }, 2129 }, metav1.CreateOptions{}) 2130 require.NoError(GinkgoT(), err) 2131 } 2132 2133 By(`Cleanup namespaces`) 2134 defer func() { 2135 for _, namespace := range testNamespaces { 2136 err := c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}) 2137 require.NoError(GinkgoT(), err) 2138 } 2139 }() 2140 2141 By(`Create an OperatorGroup`) 2142 operatorGroup := &v1.OperatorGroup{ 2143 ObjectMeta: metav1.ObjectMeta{ 2144 Name: genName("e2e-operator-group-"), 2145 Namespace: testNamespaceA, 2146 }, 2147 Spec: v1.OperatorGroupSpec{ 2148 TargetNamespaces: []string{}, 2149 }, 2150 } 2151 operatorGroup, err := crc.OperatorsV1().OperatorGroups(testNamespaceA).Create(context.TODO(), operatorGroup, metav1.CreateOptions{}) 2152 require.NoError(GinkgoT(), err) 2153 2154 By(`Cleanup OperatorGroup`) 2155 defer func() { 2156 err := crc.OperatorsV1().OperatorGroups(testNamespaceA).Delete(context.TODO(), operatorGroup.GetName(), metav1.DeleteOptions{}) 2157 require.NoError(GinkgoT(), err) 2158 }() 2159 2160 By(`Create the OperatorGroup Label`) 2161 ogLabel, err := getOGLabelKey(operatorGroup) 2162 require.NoError(GinkgoT(), err) 2163 2164 By(`Create list options`) 2165 listOptions := metav1.ListOptions{ 2166 LabelSelector: labels.Set(map[string]string{ogLabel: ""}).String(), 2167 } 2168 2169 namespaceList, err := pollForNamespaceListCount(c, listOptions, 0) 2170 require.NoError(GinkgoT(), err) 2171 2172 By(`Update the OperatorGroup to include a single namespace`) 2173 operatorGroup.Spec.TargetNamespaces = []string{testNamespaceA} 2174 updateOGSpecFunc := updateOperatorGroupSpecFunc(GinkgoT(), crc, testNamespaceA, operatorGroup.GetName()) 2175 require.NoError(GinkgoT(), retry.RetryOnConflict(retry.DefaultBackoff, updateOGSpecFunc(operatorGroup.Spec))) 2176 2177 namespaceList, err = pollForNamespaceListCount(c, listOptions, 1) 2178 require.NoError(GinkgoT(), err) 2179 require.True(GinkgoT(), checkForOperatorGroupLabels(operatorGroup, namespaceList.Items)) 2180 2181 By(`Update the OperatorGroup to include two namespaces`) 2182 operatorGroup.Spec.TargetNamespaces = []string{testNamespaceA, testNamespaceC} 2183 require.NoError(GinkgoT(), retry.RetryOnConflict(retry.DefaultBackoff, updateOGSpecFunc(operatorGroup.Spec))) 2184 2185 namespaceList, err = pollForNamespaceListCount(c, listOptions, 2) 2186 require.NoError(GinkgoT(), err) 2187 require.True(GinkgoT(), checkForOperatorGroupLabels(operatorGroup, namespaceList.Items)) 2188 2189 By(`Update the OperatorGroup to include three namespaces`) 2190 operatorGroup.Spec.TargetNamespaces = []string{testNamespaceA, testNamespaceB, testNamespaceC} 2191 require.NoError(GinkgoT(), retry.RetryOnConflict(retry.DefaultBackoff, updateOGSpecFunc(operatorGroup.Spec))) 2192 2193 namespaceList, err = pollForNamespaceListCount(c, listOptions, 3) 2194 require.NoError(GinkgoT(), err) 2195 require.True(GinkgoT(), checkForOperatorGroupLabels(operatorGroup, namespaceList.Items)) 2196 2197 By(`Update the OperatorGroup to include two namespaces`) 2198 operatorGroup.Spec.TargetNamespaces = []string{testNamespaceA, testNamespaceC} 2199 require.NoError(GinkgoT(), retry.RetryOnConflict(retry.DefaultBackoff, updateOGSpecFunc(operatorGroup.Spec))) 2200 2201 namespaceList, err = pollForNamespaceListCount(c, listOptions, 2) 2202 require.NoError(GinkgoT(), err) 2203 require.True(GinkgoT(), checkForOperatorGroupLabels(operatorGroup, namespaceList.Items)) 2204 2205 By(`Make the OperatorGroup a Cluster OperatorGroup.`) 2206 operatorGroup.Spec.TargetNamespaces = []string{} 2207 require.NoError(GinkgoT(), retry.RetryOnConflict(retry.DefaultBackoff, updateOGSpecFunc(operatorGroup.Spec))) 2208 2209 namespaceList, err = pollForNamespaceListCount(c, listOptions, 0) 2210 require.NoError(GinkgoT(), err) 2211 }) 2212 It("CleanupDeletedOperatorGroupLabels", func() { 2213 2214 By(`Create the namespaces that will have an OperatorGroup Label applied.`) 2215 testNamespaceA := genName("namespace-a-") 2216 testNamespaceB := genName("namespace-b-") 2217 testNamespaceC := genName("namespace-c-") 2218 testNamespaces := []string{ 2219 testNamespaceA, testNamespaceB, testNamespaceC, 2220 } 2221 2222 By(`Create the namespaces`) 2223 for _, namespace := range testNamespaces { 2224 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 2225 ObjectMeta: metav1.ObjectMeta{ 2226 Name: namespace, 2227 }, 2228 }, metav1.CreateOptions{}) 2229 require.NoError(GinkgoT(), err) 2230 } 2231 2232 By(`Cleanup namespaces`) 2233 defer func() { 2234 for _, namespace := range testNamespaces { 2235 err := c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}) 2236 require.NoError(GinkgoT(), err) 2237 } 2238 }() 2239 2240 By(`Create an OperatorGroup with three target namespaces.`) 2241 operatorGroup := &v1.OperatorGroup{ 2242 ObjectMeta: metav1.ObjectMeta{ 2243 Name: genName("e2e-operator-group-"), 2244 Namespace: testNamespaceA, 2245 }, 2246 Spec: v1.OperatorGroupSpec{ 2247 TargetNamespaces: testNamespaces, 2248 }, 2249 } 2250 operatorGroup, err := crc.OperatorsV1().OperatorGroups(testNamespaceA).Create(context.TODO(), operatorGroup, metav1.CreateOptions{}) 2251 require.NoError(GinkgoT(), err) 2252 2253 By(`Create the OperatorGroup Label`) 2254 ogLabel, err := getOGLabelKey(operatorGroup) 2255 require.NoError(GinkgoT(), err) 2256 2257 By(`Create list options`) 2258 listOptions := metav1.ListOptions{ 2259 LabelSelector: labels.Set(map[string]string{ogLabel: ""}).String(), 2260 } 2261 2262 namespaceList, err := pollForNamespaceListCount(c, listOptions, 3) 2263 require.NoError(GinkgoT(), err) 2264 require.True(GinkgoT(), checkForOperatorGroupLabels(operatorGroup, namespaceList.Items)) 2265 2266 By(`Delete the operatorGroup.`) 2267 err = crc.OperatorsV1().OperatorGroups(testNamespaceA).Delete(context.TODO(), operatorGroup.GetName(), metav1.DeleteOptions{}) 2268 require.NoError(GinkgoT(), err) 2269 2270 By(`Check that no namespaces have the OperatorGroup.`) 2271 namespaceList, err = pollForNamespaceListCount(c, listOptions, 0) 2272 require.NoError(GinkgoT(), err) 2273 }) 2274 2275 Context("Given a set of Namespaces", func() { 2276 var ( 2277 c operatorclient.ClientInterface 2278 crc versioned.Interface 2279 testNamespaces []string 2280 testNamespaceA string 2281 ) 2282 2283 BeforeEach(func() { 2284 c = newKubeClient() 2285 crc = newCRClient() 2286 2287 By(`Create the namespaces that will have an OperatorGroup Label applied.`) 2288 testNamespaceA = genName("namespace-a-") 2289 testNamespaceB := genName("namespace-b-") 2290 testNamespaceC := genName("namespace-c-") 2291 testNamespaces = []string{ 2292 testNamespaceA, testNamespaceB, testNamespaceC, 2293 } 2294 2295 By(`Create the namespaces`) 2296 for _, namespace := range testNamespaces { 2297 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 2298 ObjectMeta: metav1.ObjectMeta{ 2299 Name: namespace, 2300 }, 2301 }, metav1.CreateOptions{}) 2302 Expect(err).ToNot(HaveOccurred()) 2303 } 2304 }) 2305 2306 AfterEach(func() { 2307 By(`Cleanup namespaces`) 2308 for _, namespace := range testNamespaces { 2309 err := c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}) 2310 Expect(err).ToNot(HaveOccurred()) 2311 } 2312 }) 2313 2314 Context("Associating these Namespaces with a label", func() { 2315 var ( 2316 matchingLabel map[string]string 2317 ) 2318 2319 BeforeEach(func() { 2320 matchingLabel = map[string]string{"foo": "bar"} 2321 2322 By(`Updating Namespace with labels`) 2323 for _, namespace := range testNamespaces { 2324 _, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), &corev1.Namespace{ 2325 ObjectMeta: metav1.ObjectMeta{ 2326 Name: namespace, 2327 Labels: matchingLabel, 2328 }, 2329 }, metav1.UpdateOptions{}) 2330 Expect(err).ToNot(HaveOccurred()) 2331 } 2332 2333 }) 2334 2335 When("an OperatorGroup is created having matching label selector defined", func() { 2336 var operatorGroup *v1.OperatorGroup 2337 2338 BeforeEach(func() { 2339 By(`Creating operator group`) 2340 operatorGroup = &v1.OperatorGroup{ 2341 ObjectMeta: metav1.ObjectMeta{ 2342 Name: genName("e2e-operator-group-"), 2343 Namespace: testNamespaceA, 2344 }, 2345 Spec: v1.OperatorGroupSpec{ 2346 Selector: &metav1.LabelSelector{ 2347 MatchLabels: matchingLabel, 2348 }, 2349 }, 2350 } 2351 var err error 2352 operatorGroup, err = crc.OperatorsV1().OperatorGroups(testNamespaceA).Create(context.TODO(), operatorGroup, metav1.CreateOptions{}) 2353 Expect(err).ToNot(HaveOccurred()) 2354 }) 2355 2356 It("[FLAKE] OLM applies labels to Namespaces that are associated with an OperatorGroup", func() { 2357 By(`issue: https://github.com/operator-framework/operator-lifecycle-manager/issues/2637`) 2358 ogLabel, err := getOGLabelKey(operatorGroup) 2359 Expect(err).ToNot(HaveOccurred()) 2360 2361 By(`Create list options`) 2362 listOptions := metav1.ListOptions{ 2363 LabelSelector: labels.Set(map[string]string{ogLabel: ""}).String(), 2364 } 2365 2366 By(`Verify that all the namespaces listed in targetNamespaces field of OperatorGroup have labels applied on them`) 2367 namespaceList, err := pollForNamespaceListCount(c, listOptions, 3) 2368 Expect(err).ToNot(HaveOccurred()) 2369 Expect(checkForOperatorGroupLabels(operatorGroup, namespaceList.Items)).Should(BeTrue()) 2370 }) 2371 }) 2372 }) 2373 2374 When("an OperatorGroup is created having above Namespaces defined under targetNamespaces field", func() { 2375 var operatorGroup *v1.OperatorGroup 2376 2377 BeforeEach(func() { 2378 By(`Create an OperatorGroup with three target namespaces.`) 2379 operatorGroup = &v1.OperatorGroup{ 2380 ObjectMeta: metav1.ObjectMeta{ 2381 Name: genName("e2e-operator-group-"), 2382 Namespace: testNamespaceA, 2383 }, 2384 Spec: v1.OperatorGroupSpec{ 2385 TargetNamespaces: testNamespaces, 2386 }, 2387 } 2388 var err error 2389 operatorGroup, err = crc.OperatorsV1().OperatorGroups(testNamespaceA).Create(context.TODO(), operatorGroup, metav1.CreateOptions{}) 2390 Expect(err).ToNot(HaveOccurred()) 2391 }) 2392 2393 It("OLM applies labels to Namespaces that are associated with an OperatorGroup", func() { 2394 2395 ogLabel, err := getOGLabelKey(operatorGroup) 2396 Expect(err).ToNot(HaveOccurred()) 2397 2398 By(`Create list options`) 2399 listOptions := metav1.ListOptions{ 2400 LabelSelector: labels.Set(map[string]string{ogLabel: ""}).String(), 2401 } 2402 2403 By(`Verify that all the namespaces listed in targetNamespaces field of OperatorGroup have labels applied on them`) 2404 namespaceList, err := pollForNamespaceListCount(c, listOptions, 3) 2405 Expect(err).ToNot(HaveOccurred()) 2406 Expect(checkForOperatorGroupLabels(operatorGroup, namespaceList.Items)).Should(BeTrue()) 2407 2408 }) 2409 }) 2410 }) 2411 }) 2412 2413 func checkOperatorGroupAnnotations(obj metav1.Object, op *v1.OperatorGroup, checkTargetNamespaces bool, targetNamespaces string) error { 2414 if checkTargetNamespaces { 2415 if annotation, ok := obj.GetAnnotations()[v1.OperatorGroupTargetsAnnotationKey]; !ok || annotation != targetNamespaces { 2416 return fmt.Errorf("missing targetNamespaces annotation on %v", obj.GetName()) 2417 } 2418 } else { 2419 if _, found := obj.GetAnnotations()[v1.OperatorGroupTargetsAnnotationKey]; found { 2420 return fmt.Errorf("targetNamespaces annotation unexpectedly found on %v", obj.GetName()) 2421 } 2422 } 2423 2424 if annotation, ok := obj.GetAnnotations()[v1.OperatorGroupNamespaceAnnotationKey]; !ok || annotation != op.GetNamespace() { 2425 return fmt.Errorf("missing operatorNamespace on %v", obj.GetName()) 2426 } 2427 if annotation, ok := obj.GetAnnotations()[v1.OperatorGroupAnnotationKey]; !ok || annotation != op.GetName() { 2428 return fmt.Errorf("missing operatorGroup annotation on %v", obj.GetName()) 2429 } 2430 2431 return nil 2432 } 2433 2434 func newOperatorGroup(namespace, name string, annotations map[string]string, selector *metav1.LabelSelector, targetNamespaces []string, static bool) *v1.OperatorGroup { 2435 return &v1.OperatorGroup{ 2436 ObjectMeta: metav1.ObjectMeta{ 2437 Namespace: namespace, 2438 Name: name, 2439 Annotations: annotations, 2440 }, 2441 Spec: v1.OperatorGroupSpec{ 2442 TargetNamespaces: targetNamespaces, 2443 Selector: selector, 2444 StaticProvidedAPIs: static, 2445 }, 2446 } 2447 } 2448 2449 func createProjectAdmin(t GinkgoTInterface, c operatorclient.ClientInterface, namespace string) (string, cleanupFunc) { 2450 sa, err := c.CreateServiceAccount(&corev1.ServiceAccount{ 2451 ObjectMeta: metav1.ObjectMeta{ 2452 Namespace: namespace, 2453 Name: genName("padmin-"), 2454 }, 2455 }) 2456 require.NoError(t, err) 2457 2458 rb, err := c.CreateRoleBinding(&rbacv1.RoleBinding{ 2459 ObjectMeta: metav1.ObjectMeta{ 2460 Name: genName("padmin-"), 2461 Namespace: namespace, 2462 }, 2463 Subjects: []rbacv1.Subject{ 2464 { 2465 Kind: "ServiceAccount", 2466 Name: sa.GetName(), 2467 Namespace: sa.GetNamespace(), 2468 }, 2469 }, 2470 RoleRef: rbacv1.RoleRef{ 2471 APIGroup: "rbac.authorization.k8s.io", 2472 Kind: "ClusterRole", 2473 Name: "admin", 2474 }, 2475 }) 2476 require.NoError(t, err) 2477 2478 return "system:serviceaccount:" + namespace + ":" + sa.GetName(), func() { 2479 _ = c.DeleteServiceAccount(sa.GetNamespace(), sa.GetName(), metav1.NewDeleteOptions(0)) 2480 _ = c.DeleteRoleBinding(rb.GetNamespace(), rb.GetName(), metav1.NewDeleteOptions(0)) 2481 } 2482 } 2483 2484 func checkForOperatorGroupLabels(operatorGroup *v1.OperatorGroup, namespaces []corev1.Namespace) bool { 2485 for _, ns := range operatorGroup.Spec.TargetNamespaces { 2486 if !containsNamespace(namespaces, ns) { 2487 return false 2488 } 2489 } 2490 return true 2491 } 2492 2493 func updateOperatorGroupSpecFunc(t GinkgoTInterface, crc versioned.Interface, namespace, operatorGroupName string) func(v1.OperatorGroupSpec) func() error { 2494 return func(operatorGroupSpec v1.OperatorGroupSpec) func() error { 2495 return func() error { 2496 fetchedOG, err := crc.OperatorsV1().OperatorGroups(namespace).Get(context.TODO(), operatorGroupName, metav1.GetOptions{}) 2497 require.NoError(t, err) 2498 fetchedOG.Spec = operatorGroupSpec 2499 _, err = crc.OperatorsV1().OperatorGroups(namespace).Update(context.TODO(), fetchedOG, metav1.UpdateOptions{}) 2500 return err 2501 } 2502 } 2503 } 2504 2505 func pollForNamespaceListCount(c operatorclient.ClientInterface, listOptions metav1.ListOptions, expectedLength int) (list *corev1.NamespaceList, err error) { 2506 Eventually(func() (bool, error) { 2507 list, err = c.KubernetesInterface().CoreV1().Namespaces().List(context.TODO(), listOptions) 2508 if err != nil { 2509 return false, err 2510 } 2511 if len(list.Items) == expectedLength { 2512 return true, nil 2513 } 2514 return false, nil 2515 }).Should(BeTrue()) 2516 return 2517 } 2518 2519 func containsNamespace(namespaces []corev1.Namespace, namespaceName string) bool { 2520 for i := range namespaces { 2521 if namespaces[i].GetName() == namespaceName { 2522 return true 2523 } 2524 } 2525 return false 2526 } 2527 2528 func getOGLabelKey(og *v1.OperatorGroup) (string, error) { 2529 ogUID := string(og.GetUID()) 2530 if ogUID == "" { 2531 return "", fmt.Errorf("OperatorGroup UID is empty string") 2532 } 2533 return fmt.Sprintf("olm.operatorgroup.uid/%s", og.GetUID()), nil 2534 }