How to use GetName method of main Package

Best K6 code snippet using main.GetName

installplan_e2e_test.go

Source:installplan_e2e_test.go Github

copy

Full Screen

...65 c = ctx.Ctx().KubeClient()66 crc = ctx.Ctx().OperatorClient()67 })68 AfterEach(func() {69 TeardownNamespace(ns.GetName())70 })71 When("an InstallPlan step contains a deprecated resource version", func() {72 var (73 csv operatorsv1alpha1.ClusterServiceVersion74 plan operatorsv1alpha1.InstallPlan75 pdb policyv1beta1.PodDisruptionBudget76 manifest string77 counter float6478 )79 BeforeEach(func() {80 dc, err := discovery.NewDiscoveryClientForConfig(ctx.Ctx().RESTConfig())81 Expect(err).ToNot(HaveOccurred())82 v, err := dc.ServerVersion()83 Expect(err).ToNot(HaveOccurred())84 if minor, err := strconv.Atoi(v.Minor); err == nil && minor < 21 {85 // This is a tactical can-kick with86 // the expectation that the87 // event-emitting behavior being88 // tested in this context will have89 // moved by the time 1.25 arrives.90 Skip("hack: test is dependent on 1.21+ behavior")91 }92 })93 BeforeEach(func() {94 counter = 095 for _, metric := range getMetricsFromPod(ctx.Ctx().KubeClient(), getPodWithLabel(ctx.Ctx().KubeClient(), "app=catalog-operator")) {96 if metric.Family == "installplan_warnings_total" {97 counter = metric.Value98 }99 }100 csv = newCSV(genName("test-csv-"), ns.GetName(), "", semver.Version{}, nil, nil, nil)101 Expect(ctx.Ctx().Client().Create(context.Background(), &csv)).To(Succeed())102 pdb = policyv1beta1.PodDisruptionBudget{103 ObjectMeta: metav1.ObjectMeta{104 Name: genName("test-pdb-"),105 },106 TypeMeta: metav1.TypeMeta{107 Kind: "PodDisruptionBudget",108 APIVersion: policyv1beta1.SchemeGroupVersion.String(),109 },110 Spec: policyv1beta1.PodDisruptionBudgetSpec{},111 }112 scheme := runtime.NewScheme()113 Expect(policyv1beta1.AddToScheme(scheme)).To(Succeed())114 {115 var b bytes.Buffer116 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(&pdb, &b)).To(Succeed())117 manifest = b.String()118 }119 plan = operatorsv1alpha1.InstallPlan{120 ObjectMeta: metav1.ObjectMeta{121 Namespace: ns.GetName(),122 Name: genName("test-plan-"),123 },124 Spec: operatorsv1alpha1.InstallPlanSpec{125 Approval: operatorsv1alpha1.ApprovalAutomatic,126 Approved: true,127 ClusterServiceVersionNames: []string{},128 },129 }130 Expect(ctx.Ctx().Client().Create(context.Background(), &plan)).To(Succeed())131 plan.Status = operatorsv1alpha1.InstallPlanStatus{132 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,133 CatalogSources: []string{},134 Plan: []*operatorsv1alpha1.Step{135 {136 Resolving: csv.GetName(),137 Status: operatorsv1alpha1.StepStatusUnknown,138 Resource: operatorsv1alpha1.StepResource{139 Name: pdb.GetName(),140 Version: pdb.APIVersion,141 Kind: pdb.Kind,142 Manifest: manifest,143 },144 },145 },146 }147 Expect(ctx.Ctx().Client().Status().Update(context.Background(), &plan)).To(Succeed())148 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {149 return &plan, ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&plan), &plan)150 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseComplete))151 })152 AfterEach(func() {153 Eventually(func() error {154 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &csv))155 }).Should(Succeed())156 })157 It("creates an Event surfacing the deprecation warning", func() {158 Eventually(func() ([]corev1.Event, error) {159 var events corev1.EventList160 if err := ctx.Ctx().Client().List(context.Background(), &events, client.InNamespace(ns.GetName())); err != nil {161 return nil, err162 }163 var result []corev1.Event164 for _, item := range events.Items {165 result = append(result, corev1.Event{166 InvolvedObject: corev1.ObjectReference{167 APIVersion: item.InvolvedObject.APIVersion,168 Kind: item.InvolvedObject.Kind,169 Namespace: item.InvolvedObject.Namespace,170 Name: item.InvolvedObject.Name,171 FieldPath: item.InvolvedObject.FieldPath,172 },173 Reason: item.Reason,174 Message: item.Message,175 })176 }177 return result, nil178 }).Should(ContainElement(corev1.Event{179 InvolvedObject: corev1.ObjectReference{180 APIVersion: operatorsv1alpha1.InstallPlanAPIVersion,181 Kind: operatorsv1alpha1.InstallPlanKind,182 Namespace: ns.GetName(),183 Name: plan.GetName(),184 FieldPath: "status.plan[0]",185 },186 Reason: "AppliedWithWarnings",187 Message: fmt.Sprintf("1 warning(s) generated during installation of operator \"%s\" (PodDisruptionBudget \"%s\"): policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget", csv.GetName(), pdb.GetName()),188 }))189 })190 It("increments a metric counting the warning", func() {191 Eventually(func() []Metric {192 return getMetricsFromPod(ctx.Ctx().KubeClient(), getPodWithLabel(ctx.Ctx().KubeClient(), "app=catalog-operator"))193 }).Should(ContainElement(LikeMetric(194 WithFamily("installplan_warnings_total"),195 WithValueGreaterThan(counter),196 )))197 })198 })199 When("a CustomResourceDefinition step resolved from a bundle is applied", func() {200 var (201 crd apiextensionsv1.CustomResourceDefinition202 manifest string203 )204 BeforeEach(func() {205 csv := newCSV("test-csv", ns.GetName(), "", semver.Version{}, nil, nil, nil)206 Expect(ctx.Ctx().Client().Create(context.Background(), &csv)).To(Succeed())207 crd = apiextensionsv1.CustomResourceDefinition{208 ObjectMeta: metav1.ObjectMeta{209 Name: "tests.example.com",210 },211 TypeMeta: metav1.TypeMeta{212 Kind: "CustomResourceDefinition",213 APIVersion: apiextensionsv1.SchemeGroupVersion.String(),214 },215 Spec: apiextensionsv1.CustomResourceDefinitionSpec{216 Group: "example.com",217 Scope: apiextensionsv1.ClusterScoped,218 Names: apiextensionsv1.CustomResourceDefinitionNames{219 Plural: "tests",220 Singular: "test",221 Kind: "Test",222 ListKind: "TestList",223 },224 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{225 Name: "v1",226 Served: true,227 Storage: true,228 Schema: &apiextensionsv1.CustomResourceValidation{229 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{230 Type: "object",231 },232 },233 }},234 },235 }236 scheme := runtime.NewScheme()237 Expect(corev1.AddToScheme(scheme)).To(Succeed())238 {239 var b bytes.Buffer240 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(&crd, &b)).To(Succeed())241 manifest = b.String()242 }243 plan := operatorsv1alpha1.InstallPlan{244 ObjectMeta: metav1.ObjectMeta{245 Namespace: ns.GetName(),246 Name: "test-plan",247 },248 Spec: operatorsv1alpha1.InstallPlanSpec{249 Approval: operatorsv1alpha1.ApprovalAutomatic,250 Approved: true,251 ClusterServiceVersionNames: []string{},252 },253 }254 Expect(ctx.Ctx().Client().Create(context.Background(), &plan)).To(Succeed())255 plan.Status = operatorsv1alpha1.InstallPlanStatus{256 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,257 CatalogSources: []string{},258 Plan: []*operatorsv1alpha1.Step{259 {260 Resolving: "test-csv",261 Status: operatorsv1alpha1.StepStatusUnknown,262 Resource: operatorsv1alpha1.StepResource{263 Name: crd.GetName(),264 Version: apiextensionsv1.SchemeGroupVersion.String(),265 Kind: "CustomResourceDefinition",266 Manifest: manifest,267 },268 },269 },270 }271 Expect(ctx.Ctx().Client().Status().Update(context.Background(), &plan)).To(Succeed())272 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {273 return &plan, ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&plan), &plan)274 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseComplete))275 })276 AfterEach(func() {277 Eventually(func() error {278 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{}))279 }).Should(Succeed())280 })281 It("is annotated with a reference to its associated ClusterServiceVersion", func() {282 Eventually(func() (map[string]string, error) {283 if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&crd), &crd); err != nil {284 return nil, err285 }286 return crd.GetAnnotations(), nil287 }).Should(HaveKeyWithValue(288 HavePrefix("operatorframework.io/installed-alongside-"),289 fmt.Sprintf("%s/test-csv", ns.GetName()),290 ))291 })292 When("a second plan includes the same CustomResourceDefinition", func() {293 var (294 csv operatorsv1alpha1.ClusterServiceVersion295 plan operatorsv1alpha1.InstallPlan296 )297 BeforeEach(func() {298 csv = newCSV("test-csv-two", ns.GetName(), "", semver.Version{}, nil, nil, nil)299 Expect(ctx.Ctx().Client().Create(context.Background(), &csv)).To(Succeed())300 plan = operatorsv1alpha1.InstallPlan{301 ObjectMeta: metav1.ObjectMeta{302 Namespace: ns.GetName(),303 Name: "test-plan-two",304 },305 Spec: operatorsv1alpha1.InstallPlanSpec{306 Approval: operatorsv1alpha1.ApprovalAutomatic,307 Approved: true,308 ClusterServiceVersionNames: []string{},309 },310 }311 Expect(ctx.Ctx().Client().Create(context.Background(), &plan)).To(Succeed())312 plan.Status = operatorsv1alpha1.InstallPlanStatus{313 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,314 CatalogSources: []string{},315 Plan: []*operatorsv1alpha1.Step{316 {317 Resolving: "test-csv-two",318 Status: operatorsv1alpha1.StepStatusUnknown,319 Resource: operatorsv1alpha1.StepResource{320 Name: crd.GetName(),321 Version: apiextensionsv1.SchemeGroupVersion.String(),322 Kind: "CustomResourceDefinition",323 Manifest: manifest,324 },325 },326 },327 }328 Expect(ctx.Ctx().Client().Status().Update(context.Background(), &plan)).To(Succeed())329 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {330 return &plan, ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&plan), &plan)331 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseComplete))332 })333 AfterEach(func() {334 Eventually(func() error {335 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &csv))336 }).Should(Succeed())337 Eventually(func() error {338 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &plan))339 }).Should(Succeed())340 })341 It("has one annotation for each ClusterServiceVersion", func() {342 Eventually(func() ([]struct{ Key, Value string }, error) {343 if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&crd), &crd); err != nil {344 return nil, err345 }346 var pairs []struct{ Key, Value string }347 for k, v := range crd.GetAnnotations() {348 pairs = append(pairs, struct{ Key, Value string }{Key: k, Value: v})349 }350 return pairs, nil351 }).Should(ConsistOf(352 MatchFields(IgnoreExtras, Fields{353 "Key": HavePrefix("operatorframework.io/installed-alongside-"),354 "Value": Equal(fmt.Sprintf("%s/test-csv", ns.GetName())),355 }),356 MatchFields(IgnoreExtras, Fields{357 "Key": HavePrefix("operatorframework.io/installed-alongside-"),358 "Value": Equal(fmt.Sprintf("%s/test-csv-two", ns.GetName())),359 }),360 ))361 })362 })363 })364 When("an error is encountered during InstallPlan step execution", func() {365 var (366 plan *operatorsv1alpha1.InstallPlan367 owned *corev1.ConfigMap368 )369 BeforeEach(func() {370 // It's hard to reliably generate transient371 // errors in an uninstrumented end-to-end372 // test, so simulate it by constructing an373 // error state that can be easily corrected374 // during a test.375 owned = &corev1.ConfigMap{376 ObjectMeta: metav1.ObjectMeta{377 Namespace: ns.GetName(),378 Name: "test-owned",379 OwnerReferences: []metav1.OwnerReference{{380 APIVersion: "operators.coreos.com/v1alpha1",381 Kind: "ClusterServiceVersion",382 Name: "test-owner", // Does not exist!383 UID: "", // catalog-operator populates this if the named CSV exists.384 }},385 },386 }387 scheme := runtime.NewScheme()388 Expect(corev1.AddToScheme(scheme)).To(Succeed())389 var manifest bytes.Buffer390 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(owned, &manifest)).To(Succeed())391 plan = &operatorsv1alpha1.InstallPlan{392 ObjectMeta: metav1.ObjectMeta{393 Namespace: ns.GetName(),394 Name: "test-plan",395 },396 Spec: operatorsv1alpha1.InstallPlanSpec{397 Approval: operatorsv1alpha1.ApprovalAutomatic,398 Approved: true,399 ClusterServiceVersionNames: []string{},400 },401 }402 Expect(ctx.Ctx().Client().Create(context.Background(), plan)).To(Succeed())403 plan.Status = operatorsv1alpha1.InstallPlanStatus{404 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,405 CatalogSources: []string{},406 Plan: []*operatorsv1alpha1.Step{407 {408 Status: operatorsv1alpha1.StepStatusUnknown,409 Resource: operatorsv1alpha1.StepResource{410 Name: owned.GetName(),411 Version: "v1",412 Kind: "ConfigMap",413 Manifest: manifest.String(),414 },415 },416 },417 }418 Expect(ctx.Ctx().Client().Status().Update(context.Background(), plan)).To(Succeed())419 })420 AfterEach(func() {421 Expect(ctx.Ctx().Client().Delete(context.Background(), owned)).To(Or(422 Succeed(),423 WithTransform(apierrors.IsNotFound, BeTrue()),424 ))425 Expect(ctx.Ctx().Client().Delete(context.Background(), plan)).To(Or(426 Succeed(),427 WithTransform(apierrors.IsNotFound, BeTrue()),428 ))429 })430 It("times out if the error persists", func() {431 Eventually(432 func() (*operatorsv1alpha1.InstallPlan, error) {433 return plan, ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(plan), plan)434 },435 90*time.Second,436 ).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseFailed))437 })438 It("succeeds if there is no error on a later attempt", func() {439 owner := newCSV("test-owner", ns.GetName(), "", semver.Version{}, nil, nil, nil)440 Expect(ctx.Ctx().Client().Create(context.Background(), &owner)).To(Succeed())441 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {442 return plan, ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(plan), plan)443 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseComplete))444 })445 })446 When("an InstallPlan transfers ownership of a ServiceAccount to a new ClusterServiceVersion", func() {447 var (448 csv1, csv2 operatorsv1alpha1.ClusterServiceVersion449 sa corev1.ServiceAccount450 plan operatorsv1alpha1.InstallPlan451 )452 BeforeEach(func() {453 csv1 = newCSV("test-csv-old", ns.GetName(), "", semver.Version{}, nil, nil, nil)454 Expect(ctx.Ctx().Client().Create(context.Background(), &csv1)).To(Succeed())455 csv2 = newCSV("test-csv-new", ns.GetName(), "", semver.Version{}, nil, nil, nil)456 Expect(ctx.Ctx().Client().Create(context.Background(), &csv2)).To(Succeed())457 sa = corev1.ServiceAccount{458 ObjectMeta: metav1.ObjectMeta{459 Namespace: ns.GetName(),460 Name: "test-serviceaccount",461 OwnerReferences: []metav1.OwnerReference{462 {463 APIVersion: operatorsv1alpha1.SchemeGroupVersion.String(),464 Kind: operatorsv1alpha1.ClusterServiceVersionKind,465 Name: csv1.GetName(),466 UID: csv1.GetUID(),467 },468 },469 },470 }471 Expect(ctx.Ctx().Client().Create(context.Background(), &sa)).To(Succeed())472 scheme := runtime.NewScheme()473 Expect(corev1.AddToScheme(scheme)).To(Succeed())474 var manifest bytes.Buffer475 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(&corev1.ServiceAccount{476 ObjectMeta: metav1.ObjectMeta{477 Namespace: ns.GetName(),478 Name: "test-serviceaccount",479 OwnerReferences: []metav1.OwnerReference{480 {481 APIVersion: operatorsv1alpha1.SchemeGroupVersion.String(),482 Kind: operatorsv1alpha1.ClusterServiceVersionKind,483 Name: csv2.GetName(),484 },485 },486 },487 }, &manifest)).To(Succeed())488 plan = operatorsv1alpha1.InstallPlan{489 ObjectMeta: metav1.ObjectMeta{490 Namespace: ns.GetName(),491 Name: "test-plan",492 },493 Spec: operatorsv1alpha1.InstallPlanSpec{494 Approval: operatorsv1alpha1.ApprovalAutomatic,495 Approved: true,496 ClusterServiceVersionNames: []string{},497 },498 }499 Expect(ctx.Ctx().Client().Create(context.Background(), &plan)).To(Succeed())500 plan.Status = operatorsv1alpha1.InstallPlanStatus{501 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,502 CatalogSources: []string{},503 Plan: []*operatorsv1alpha1.Step{504 {505 Status: operatorsv1alpha1.StepStatusUnknown,506 Resource: operatorsv1alpha1.StepResource{507 Name: sa.GetName(),508 Version: "v1",509 Kind: "ServiceAccount",510 Manifest: manifest.String(),511 },512 },513 },514 }515 Expect(ctx.Ctx().Client().Status().Update(context.Background(), &plan)).To(Succeed())516 })517 AfterEach(func() {518 Expect(ctx.Ctx().Client().Delete(context.Background(), &sa)).To(Or(519 Succeed(),520 WithTransform(apierrors.IsNotFound, BeTrue()),521 ))522 Expect(ctx.Ctx().Client().Delete(context.Background(), &csv1)).To(Or(523 Succeed(),524 WithTransform(apierrors.IsNotFound, BeTrue()),525 ))526 Expect(ctx.Ctx().Client().Delete(context.Background(), &csv2)).To(Or(527 Succeed(),528 WithTransform(apierrors.IsNotFound, BeTrue()),529 ))530 Expect(ctx.Ctx().Client().Delete(context.Background(), &plan)).To(Or(531 Succeed(),532 WithTransform(apierrors.IsNotFound, BeTrue()),533 ))534 })535 It("preserves owner references to both the old and the new ClusterServiceVersion", func() {536 Eventually(func() ([]metav1.OwnerReference, error) {537 if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&sa), &sa); err != nil {538 return nil, err539 }540 return sa.GetOwnerReferences(), nil541 }).Should(ContainElements([]metav1.OwnerReference{542 {543 APIVersion: operatorsv1alpha1.SchemeGroupVersion.String(),544 Kind: operatorsv1alpha1.ClusterServiceVersionKind,545 Name: csv1.GetName(),546 UID: csv1.GetUID(),547 },548 {549 APIVersion: operatorsv1alpha1.SchemeGroupVersion.String(),550 Kind: operatorsv1alpha1.ClusterServiceVersionKind,551 Name: csv2.GetName(),552 UID: csv2.GetUID(),553 },554 }))555 })556 })557 When("a ClusterIP service exists", func() {558 var (559 service *corev1.Service560 )561 BeforeEach(func() {562 service = &corev1.Service{563 TypeMeta: metav1.TypeMeta{564 Kind: "Service",565 APIVersion: "v1",566 },567 ObjectMeta: metav1.ObjectMeta{568 Namespace: ns.GetName(),569 Name: "test-service",570 },571 Spec: corev1.ServiceSpec{572 Type: corev1.ServiceTypeClusterIP,573 Ports: []corev1.ServicePort{574 {575 Port: 12345,576 },577 },578 },579 }580 Expect(ctx.Ctx().Client().Create(context.Background(), service.DeepCopy())).To(Succeed())581 })582 AfterEach(func() {583 Expect(ctx.Ctx().Client().Delete(context.Background(), service)).To(Succeed())584 })585 It("it can be updated by an InstallPlan step", func() {586 scheme := runtime.NewScheme()587 Expect(corev1.AddToScheme(scheme)).To(Succeed())588 var manifest bytes.Buffer589 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(service, &manifest)).To(Succeed())590 plan := &operatorsv1alpha1.InstallPlan{591 ObjectMeta: metav1.ObjectMeta{592 Namespace: ns.GetName(),593 Name: "test-plan",594 },595 Spec: operatorsv1alpha1.InstallPlanSpec{596 Approval: operatorsv1alpha1.ApprovalAutomatic,597 Approved: true,598 ClusterServiceVersionNames: []string{},599 },600 }601 Expect(ctx.Ctx().Client().Create(context.Background(), plan)).To(Succeed())602 plan.Status = operatorsv1alpha1.InstallPlanStatus{603 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,604 CatalogSources: []string{},605 Plan: []*operatorsv1alpha1.Step{606 {607 Status: operatorsv1alpha1.StepStatusUnknown,608 Resource: operatorsv1alpha1.StepResource{609 Name: service.Name,610 Version: "v1",611 Kind: "Service",612 Manifest: manifest.String(),613 },614 },615 },616 }617 Expect(ctx.Ctx().Client().Status().Update(context.Background(), plan)).To(Succeed())618 key := client.ObjectKeyFromObject(plan)619 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {620 return plan, ctx.Ctx().Client().Get(context.Background(), key, plan)621 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseComplete))622 Expect(client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), plan))).To(Succeed())623 })624 })625 It("with CSVs across multiple catalog sources", func() {626 log := func(s string) {627 GinkgoT().Logf("%s: %s", time.Now().Format("15:04:05.9999"), s)628 }629 mainPackageName := genName("nginx-")630 dependentPackageName := genName("nginxdep-")631 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)632 dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName)633 stableChannel := "stable"634 dependentCRD := newCRD(genName("ins-"))635 mainCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, nil)636 dependentCSV := newCSV(dependentPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, nil)637 defer func() {638 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))639 }()640 dependentCatalogName := genName("mock-ocs-dependent-")641 mainCatalogName := genName("mock-ocs-main-")642 // Create separate manifests for each CatalogSource643 mainManifests := []registry.PackageManifest{644 {645 PackageName: mainPackageName,646 Channels: []registry.PackageChannel{647 {Name: stableChannel, CurrentCSVName: mainPackageStable},648 },649 DefaultChannelName: stableChannel,650 },651 }652 dependentManifests := []registry.PackageManifest{653 {654 PackageName: dependentPackageName,655 Channels: []registry.PackageChannel{656 {Name: stableChannel, CurrentCSVName: dependentPackageStable},657 },658 DefaultChannelName: stableChannel,659 },660 }661 // Defer CRD clean up662 defer func() {663 Eventually(func() error {664 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{}))665 }).Should(Succeed())666 }()667 // Create the catalog sources668 require.NotEqual(GinkgoT(), "", ns.GetName())669 _, cleanupDependentCatalogSource := createInternalCatalogSource(c, crc, dependentCatalogName, ns.GetName(), dependentManifests, []apiextensions.CustomResourceDefinition{dependentCRD}, []operatorsv1alpha1.ClusterServiceVersion{dependentCSV})670 defer cleanupDependentCatalogSource()671 // Attempt to get the catalog source before creating install plan672 _, err := fetchCatalogSourceOnStatus(crc, dependentCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)673 require.NoError(GinkgoT(), err)674 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, nil, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})675 defer cleanupMainCatalogSource()676 // Attempt to get the catalog source before creating install plan677 _, err = fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)678 require.NoError(GinkgoT(), err)679 // Create expected install plan step sources680 expectedStepSources := map[registry.ResourceKey]registry.ResourceKey{681 {Name: dependentCRD.Name, Kind: "CustomResourceDefinition"}: {Name: dependentCatalogName, Namespace: ns.GetName()},682 {Name: dependentPackageStable, Kind: operatorsv1alpha1.ClusterServiceVersionKind}: {Name: dependentCatalogName, Namespace: ns.GetName()},683 {Name: mainPackageStable, Kind: operatorsv1alpha1.ClusterServiceVersionKind}: {Name: mainCatalogName, Namespace: ns.GetName()},684 {Name: strings.Join([]string{dependentPackageStable, dependentCatalogName, ns.GetName()}, "-"), Kind: operatorsv1alpha1.SubscriptionKind}: {Name: dependentCatalogName, Namespace: ns.GetName()},685 }686 subscriptionName := genName("sub-nginx-")687 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)688 defer subscriptionCleanup()689 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)690 require.NoError(GinkgoT(), err)691 require.NotNil(GinkgoT(), subscription)692 installPlanName := subscription.Status.InstallPlanRef.Name693 // Wait for InstallPlan to be status: Complete before checking resource presence694 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))695 require.NoError(GinkgoT(), err)696 log(fmt.Sprintf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase))697 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)698 // Fetch installplan again to check for unnecessary control loops699 fetchedInstallPlan, err = fetchInstallPlan(GinkgoT(), crc, fetchedInstallPlan.GetName(), ns.GetName(), func(fip *operatorsv1alpha1.InstallPlan) bool {700 // Don't compare object meta as labels can be applied by the operator controller.701 Expect(equality.Semantic.DeepEqual(fetchedInstallPlan.Spec, fip.Spec)).Should(BeTrue(), diff.ObjectDiff(fetchedInstallPlan, fip))702 Expect(equality.Semantic.DeepEqual(fetchedInstallPlan.Status, fip.Status)).Should(BeTrue(), diff.ObjectDiff(fetchedInstallPlan, fip))703 return true704 })705 require.NoError(GinkgoT(), err)706 require.Equal(GinkgoT(), len(expectedStepSources), len(fetchedInstallPlan.Status.Plan), "Number of resolved steps matches the number of expected steps")707 // Ensure resolved step resources originate from the correct catalog sources708 log(fmt.Sprintf("%#v", expectedStepSources))709 for _, step := range fetchedInstallPlan.Status.Plan {710 log(fmt.Sprintf("checking %s", step.Resource))711 key := registry.ResourceKey{Name: step.Resource.Name, Kind: step.Resource.Kind}712 expectedSource, ok := expectedStepSources[key]713 require.True(GinkgoT(), ok, "didn't find %v", key)714 require.Equal(GinkgoT(), expectedSource.Name, step.Resource.CatalogSource)715 require.Equal(GinkgoT(), expectedSource.Namespace, step.Resource.CatalogSourceNamespace)716 // delete717 }718 EXPECTED:719 for key := range expectedStepSources {720 for _, step := range fetchedInstallPlan.Status.Plan {721 if step.Resource.Name == key.Name && step.Resource.Kind == key.Kind {722 continue EXPECTED723 }724 }725 GinkgoT().Fatalf("expected step %s not found in %#v", key, fetchedInstallPlan.Status.Plan)726 }727 log("All expected resources resolved")728 // Verify that the dependent subscription is in a good state729 dependentSubscription, err := fetchSubscription(crc, ns.GetName(), strings.Join([]string{dependentPackageStable, dependentCatalogName, ns.GetName()}, "-"), subscriptionStateAtLatestChecker)730 require.NoError(GinkgoT(), err)731 require.NotNil(GinkgoT(), dependentSubscription)732 require.NotNil(GinkgoT(), dependentSubscription.Status.InstallPlanRef)733 require.Equal(GinkgoT(), dependentCSV.GetName(), dependentSubscription.Status.CurrentCSV)734 // Verify CSV is created735 _, err = awaitCSV(crc, ns.GetName(), dependentCSV.GetName(), csvAnyChecker)736 require.NoError(GinkgoT(), err)737 // Update dependent subscription in catalog and wait for csv to update738 updatedDependentCSV := newCSV(dependentPackageStable+"-v2", ns.GetName(), dependentPackageStable, semver.MustParse("0.1.1"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, nil)739 dependentManifests = []registry.PackageManifest{740 {741 PackageName: dependentPackageName,742 Channels: []registry.PackageChannel{743 {Name: stableChannel, CurrentCSVName: updatedDependentCSV.GetName()},744 },745 DefaultChannelName: stableChannel,746 },747 }748 updateInternalCatalog(GinkgoT(), c, crc, dependentCatalogName, ns.GetName(), []apiextensions.CustomResourceDefinition{dependentCRD}, []operatorsv1alpha1.ClusterServiceVersion{dependentCSV, updatedDependentCSV}, dependentManifests)749 // Wait for subscription to update750 updatedDepSubscription, err := fetchSubscription(crc, ns.GetName(), strings.Join([]string{dependentPackageStable, dependentCatalogName, ns.GetName()}, "-"), subscriptionHasCurrentCSV(updatedDependentCSV.GetName()))751 require.NoError(GinkgoT(), err)752 // Verify installplan created and installed753 fetchedUpdatedDepInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, updatedDepSubscription.Status.InstallPlanRef.Name, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))754 require.NoError(GinkgoT(), err)755 log(fmt.Sprintf("Install plan %s fetched with status %s", fetchedUpdatedDepInstallPlan.GetName(), fetchedUpdatedDepInstallPlan.Status.Phase))756 require.NotEqual(GinkgoT(), fetchedInstallPlan.GetName(), fetchedUpdatedDepInstallPlan.GetName())757 // Wait for csv to update758 _, err = awaitCSV(crc, ns.GetName(), updatedDependentCSV.GetName(), csvAnyChecker)759 require.NoError(GinkgoT(), err)760 })761 Context("creation with pre existing CRD owners", func() {762 It("OnePreExistingCRDOwner", func() {763 mainPackageName := genName("nginx-")764 dependentPackageName := genName("nginx-dep-")765 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)766 mainPackageBeta := fmt.Sprintf("%s-beta", mainPackageName)767 dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName)768 dependentPackageBeta := fmt.Sprintf("%s-beta", dependentPackageName)769 stableChannel := "stable"770 betaChannel := "beta"771 // Create manifests772 mainManifests := []registry.PackageManifest{773 {774 PackageName: mainPackageName,775 Channels: []registry.PackageChannel{776 {Name: stableChannel, CurrentCSVName: mainPackageStable},777 },778 DefaultChannelName: stableChannel,779 },780 {781 PackageName: dependentPackageName,782 Channels: []registry.PackageChannel{783 {Name: stableChannel, CurrentCSVName: dependentPackageStable},784 {Name: betaChannel, CurrentCSVName: dependentPackageBeta},785 },786 DefaultChannelName: stableChannel,787 },788 }789 // Create new CRDs790 mainCRD := newCRD(genName("ins-"))791 dependentCRD := newCRD(genName("ins-"))792 // Create new CSVs793 mainStableCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, nil)794 mainBetaCSV := newCSV(mainPackageBeta, ns.GetName(), mainPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, nil)795 dependentStableCSV := newCSV(dependentPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, nil)796 dependentBetaCSV := newCSV(dependentPackageBeta, ns.GetName(), dependentPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, nil)797 // Defer CRD clean up798 defer func() {799 Eventually(func() error {800 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), mainCRD.GetName(), metav1.DeleteOptions{}))801 }).Should(Succeed())802 Eventually(func() error {803 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{}))804 }).Should(Succeed())805 }()806 defer func() {807 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))808 }()809 // Create the catalog source810 mainCatalogSourceName := genName("mock-ocs-main-" + strings.ToLower(K8sSafeCurrentTestDescription()) + "-")811 _, cleanupCatalogSource := createInternalCatalogSource(c, crc, mainCatalogSourceName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{dependentCRD, mainCRD}, []operatorsv1alpha1.ClusterServiceVersion{dependentBetaCSV, dependentStableCSV, mainStableCSV, mainBetaCSV})812 defer cleanupCatalogSource()813 // Attempt to get the catalog source before creating install plan(s)814 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogSourceName, ns.GetName(), catalogSourceRegistryPodSynced)815 require.NoError(GinkgoT(), err)816 expectedSteps := map[registry.ResourceKey]struct{}{817 {Name: mainCRD.Name, Kind: "CustomResourceDefinition"}: {},818 {Name: mainPackageStable, Kind: operatorsv1alpha1.ClusterServiceVersionKind}: {},819 }820 // Create the preexisting CRD and CSV821 cleanupCRD, err := createCRD(c, dependentCRD)822 require.NoError(GinkgoT(), err)823 defer cleanupCRD()824 cleanupCSV, err := createCSV(c, crc, dependentBetaCSV, ns.GetName(), true, false)825 require.NoError(GinkgoT(), err)826 defer cleanupCSV()827 GinkgoT().Log("Dependent CRD and preexisting CSV created")828 subscriptionName := genName("sub-nginx-")829 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogSourceName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)830 defer subscriptionCleanup()831 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)832 require.NoError(GinkgoT(), err)833 require.NotNil(GinkgoT(), subscription)834 installPlanName := subscription.Status.InstallPlanRef.Name835 // Wait for InstallPlan to be status: Complete or Failed before checking resource presence836 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete, operatorsv1alpha1.InstallPlanPhaseFailed))837 require.NoError(GinkgoT(), err)838 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)839 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)840 // Fetch installplan again to check for unnecessary control loops841 fetchedInstallPlan, err = fetchInstallPlan(GinkgoT(), crc, fetchedInstallPlan.GetName(), ns.GetName(), func(fip *operatorsv1alpha1.InstallPlan) bool {842 Expect(equality.Semantic.DeepEqual(fetchedInstallPlan, fip)).Should(BeTrue(), diff.ObjectDiff(fetchedInstallPlan, fip))843 return true844 })845 require.NoError(GinkgoT(), err)846 for _, step := range fetchedInstallPlan.Status.Plan {847 GinkgoT().Logf("%#v", step)848 }849 require.Equal(GinkgoT(), len(fetchedInstallPlan.Status.Plan), len(expectedSteps), "number of expected steps does not match installed")850 GinkgoT().Logf("Number of resolved steps matches the number of expected steps")851 for _, step := range fetchedInstallPlan.Status.Plan {852 key := registry.ResourceKey{853 Name: step.Resource.Name,854 Kind: step.Resource.Kind,855 }856 _, ok := expectedSteps[key]857 require.True(GinkgoT(), ok)858 // Remove the entry from the expected steps set (to ensure no duplicates in resolved plan)859 delete(expectedSteps, key)860 }861 // Should have removed every matching step862 require.Equal(GinkgoT(), 0, len(expectedSteps), "Actual resource steps do not match expected")863 // Delete CRDs864 Expect(client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCRD))).To(Succeed())865 Expect(client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &dependentCRD))).To(Succeed())866 })867 })868 Describe("with CRD schema change", func() {869 type schemaPayload struct {870 name string871 expectedPhase operatorsv1alpha1.InstallPlanPhase872 oldCRD *apiextensions.CustomResourceDefinition873 intermediateCRD *apiextensions.CustomResourceDefinition874 newCRD *apiextensions.CustomResourceDefinition875 }876 var min float64 = 2877 var max float64 = 256878 var newMax float64 = 50879 // generated outside of the test table so that the same naming can be used for both old and new CSVs880 mainCRDPlural := genName("testcrd-")881 // excluded: new CRD, same version, same schema - won't trigger a CRD update882 tableEntries := []table.TableEntry{883 table.Entry("all existing versions are present, different (backwards compatible) schema", schemaPayload{884 name: "all existing versions are present, different (backwards compatible) schema",885 expectedPhase: operatorsv1alpha1.InstallPlanPhaseComplete,886 oldCRD: func() *apiextensions.CustomResourceDefinition {887 oldCRD := newCRD(mainCRDPlural + "a")888 oldCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{889 {890 Name: "v1alpha1",891 Served: true,892 Storage: true,893 Schema: &apiextensions.CustomResourceValidation{894 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{895 Type: "object",896 Properties: map[string]apiextensions.JSONSchemaProps{897 "spec": {898 Type: "object",899 Description: "Spec of a test object.",900 Properties: map[string]apiextensions.JSONSchemaProps{901 "scalar": {902 Type: "number",903 Description: "Scalar value that should have a min and max.",904 Minimum: &min,905 Maximum: &max,906 },907 },908 },909 },910 },911 },912 },913 }914 return &oldCRD915 }(),916 newCRD: func() *apiextensions.CustomResourceDefinition {917 newCRD := newCRD(mainCRDPlural + "a")918 newCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{919 {920 Name: "v1alpha1",921 Served: true,922 Storage: false,923 Schema: &apiextensions.CustomResourceValidation{924 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{925 Type: "object",926 Properties: map[string]apiextensions.JSONSchemaProps{927 "spec": {928 Type: "object",929 Description: "Spec of a test object.",930 Properties: map[string]apiextensions.JSONSchemaProps{931 "scalar": {932 Type: "number",933 Description: "Scalar value that should have a min and max.",934 Minimum: &min,935 Maximum: &max,936 },937 },938 },939 },940 },941 },942 },943 {944 Name: "v1alpha2",945 Served: true,946 Storage: true,947 Schema: &apiextensions.CustomResourceValidation{948 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{949 Type: "object",950 Properties: map[string]apiextensions.JSONSchemaProps{951 "spec": {952 Type: "object",953 Description: "Spec of a test object.",954 Properties: map[string]apiextensions.JSONSchemaProps{955 "scalar": {956 Type: "number",957 Description: "Scalar value that should have a min and max.",958 Minimum: &min,959 Maximum: &max,960 },961 },962 },963 },964 },965 },966 },967 }968 return &newCRD969 }(),970 }),971 table.Entry("all existing versions are present, different (backwards incompatible) schema", schemaPayload{name: "all existing versions are present, different (backwards incompatible) schema",972 expectedPhase: operatorsv1alpha1.InstallPlanPhaseFailed,973 oldCRD: func() *apiextensions.CustomResourceDefinition {974 oldCRD := newCRD(mainCRDPlural + "b")975 oldCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{976 {977 Name: "v1alpha1",978 Served: true,979 Storage: true,980 Schema: &apiextensions.CustomResourceValidation{981 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{982 Type: "object",983 Properties: map[string]apiextensions.JSONSchemaProps{984 "spec": {985 Type: "object",986 Description: "Spec of a test object.",987 Properties: map[string]apiextensions.JSONSchemaProps{988 "scalar": {989 Type: "number",990 Description: "Scalar value that should have a min and max.",991 },992 },993 },994 },995 },996 },997 },998 }999 return &oldCRD1000 }(),1001 newCRD: func() *apiextensions.CustomResourceDefinition {1002 newCRD := newCRD(mainCRDPlural + "b")1003 newCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{1004 {1005 Name: "v1alpha1",1006 Served: true,1007 Storage: true,1008 Schema: &apiextensions.CustomResourceValidation{1009 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1010 Type: "object",1011 Properties: map[string]apiextensions.JSONSchemaProps{1012 "spec": {1013 Type: "object",1014 Description: "Spec of a test object.",1015 Properties: map[string]apiextensions.JSONSchemaProps{1016 "scalar": {1017 Type: "number",1018 Description: "Scalar value that should have a min and max.",1019 Minimum: &min,1020 Maximum: &newMax,1021 },1022 },1023 },1024 },1025 },1026 },1027 },1028 }1029 return &newCRD1030 }(),1031 }),1032 table.Entry("missing existing versions in new CRD", schemaPayload{name: "missing existing versions in new CRD",1033 expectedPhase: operatorsv1alpha1.InstallPlanPhaseComplete,1034 oldCRD: func() *apiextensions.CustomResourceDefinition {1035 oldCRD := newCRD(mainCRDPlural + "c")1036 oldCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{1037 {1038 Name: "v1alpha1",1039 Served: true,1040 Storage: true,1041 Schema: &apiextensions.CustomResourceValidation{1042 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1043 Type: "object",1044 Description: "my crd schema",1045 },1046 },1047 },1048 {1049 Name: "v1alpha2",1050 Served: true,1051 Storage: false,1052 Schema: &apiextensions.CustomResourceValidation{1053 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1054 Type: "object",1055 Description: "my crd schema",1056 },1057 },1058 },1059 }1060 return &oldCRD1061 }(),1062 newCRD: func() *apiextensions.CustomResourceDefinition {1063 newCRD := newCRD(mainCRDPlural + "c")1064 newCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{1065 {1066 Name: "v1alpha1",1067 Served: true,1068 Storage: true,1069 Schema: &apiextensions.CustomResourceValidation{1070 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1071 Type: "object",1072 Properties: map[string]apiextensions.JSONSchemaProps{1073 "spec": {1074 Type: "object",1075 Description: "Spec of a test object.",1076 Properties: map[string]apiextensions.JSONSchemaProps{1077 "scalar": {1078 Type: "number",1079 Description: "Scalar value that should have a min and max.",1080 Minimum: &min,1081 Maximum: &max,1082 },1083 },1084 },1085 },1086 },1087 },1088 },1089 {1090 Name: "v1",1091 Served: true,1092 Storage: false,1093 Schema: &apiextensions.CustomResourceValidation{1094 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1095 Type: "object",1096 Properties: map[string]apiextensions.JSONSchemaProps{1097 "spec": {1098 Type: "object",1099 Description: "Spec of a test object.",1100 Properties: map[string]apiextensions.JSONSchemaProps{1101 "scalar": {1102 Type: "number",1103 Description: "Scalar value that should have a min and max.",1104 Minimum: &min,1105 Maximum: &max,1106 },1107 },1108 },1109 },1110 },1111 },1112 },1113 }1114 return &newCRD1115 }()}),1116 table.Entry("existing version is present in new CRD (deprecated field)", schemaPayload{name: "existing version is present in new CRD (deprecated field)",1117 expectedPhase: operatorsv1alpha1.InstallPlanPhaseComplete,1118 oldCRD: func() *apiextensions.CustomResourceDefinition {1119 oldCRD := newCRD(mainCRDPlural + "d")1120 return &oldCRD1121 }(),1122 newCRD: func() *apiextensions.CustomResourceDefinition {1123 newCRD := newCRD(mainCRDPlural + "d")1124 newCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{1125 {1126 Name: "v1alpha1",1127 Served: true,1128 Storage: true,1129 Schema: &apiextensions.CustomResourceValidation{1130 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1131 Type: "object",1132 Properties: map[string]apiextensions.JSONSchemaProps{1133 "spec": {1134 Type: "object",1135 Description: "Spec of a test object.",1136 Properties: map[string]apiextensions.JSONSchemaProps{1137 "scalar": {1138 Type: "number",1139 Description: "Scalar value that should have a min and max.",1140 Minimum: &min,1141 Maximum: &max,1142 },1143 },1144 },1145 },1146 },1147 },1148 },1149 {1150 Name: "v1alpha3",1151 Served: false,1152 Storage: false,1153 Schema: &apiextensions.CustomResourceValidation{1154 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Type: "object"},1155 },1156 },1157 }1158 return &newCRD1159 }()}),1160 }1161 table.DescribeTable("Test", func(tt schemaPayload) {1162 mainPackageName := genName("nginx-")1163 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)1164 mainPackageBeta := fmt.Sprintf("%s-beta", mainPackageName)1165 stableChannel := "stable"1166 betaChannel := "beta"1167 // Create manifests1168 mainManifests := []registry.PackageManifest{1169 {1170 PackageName: mainPackageName,1171 Channels: []registry.PackageChannel{1172 {Name: stableChannel, CurrentCSVName: mainPackageStable},1173 {Name: betaChannel, CurrentCSVName: mainPackageBeta},1174 },1175 DefaultChannelName: stableChannel,1176 },1177 }1178 // Create new CSVs1179 mainStableCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{*tt.oldCRD}, nil, nil)1180 mainBetaCSV := newCSV(mainPackageBeta, ns.GetName(), mainPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{*tt.oldCRD}, nil, nil)1181 // Defer CRD clean up1182 defer func() {1183 Eventually(func() error {1184 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), tt.oldCRD.GetName(), metav1.DeleteOptions{}))1185 }).Should(Succeed())1186 Eventually(func() error {1187 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), tt.newCRD.GetName(), metav1.DeleteOptions{}))1188 }).Should(Succeed())1189 if tt.intermediateCRD != nil {1190 Eventually(func() error {1191 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), tt.intermediateCRD.GetName(), metav1.DeleteOptions{}))1192 }).Should(Succeed())1193 }1194 }()1195 // Existing custom resource1196 existingCR := &unstructured.Unstructured{1197 Object: map[string]interface{}{1198 "apiVersion": "cluster.com/v1alpha1",1199 "kind": tt.oldCRD.Spec.Names.Kind,1200 "metadata": map[string]interface{}{1201 "namespace": ns.GetName(),1202 "name": "my-cr-1",1203 },1204 "spec": map[string]interface{}{1205 "scalar": 100,1206 },1207 },1208 }1209 // Create the catalog source1210 mainCatalogSourceName := genName("mock-ocs-main-")1211 _, cleanupCatalogSource := createInternalCatalogSource(c, crc, mainCatalogSourceName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{*tt.oldCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainStableCSV, mainBetaCSV})1212 defer cleanupCatalogSource()1213 // Attempt to get the catalog source before creating install plan(s)1214 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogSourceName, ns.GetName(), catalogSourceRegistryPodSynced)1215 require.NoError(GinkgoT(), err)1216 subscriptionName := genName("sub-nginx-alpha-")1217 cleanupSubscription := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogSourceName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)1218 defer cleanupSubscription()1219 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)1220 require.NoError(GinkgoT(), err)1221 require.NotNil(GinkgoT(), subscription)1222 installPlanName := subscription.Status.InstallPlanRef.Name1223 // Wait for InstallPlan to be status: Complete or failed before checking resource presence1224 completeOrFailedFunc := buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete, operatorsv1alpha1.InstallPlanPhaseFailed)1225 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), completeOrFailedFunc)1226 require.NoError(GinkgoT(), err)1227 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)1228 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)1229 // Ensure that the desired resources have been created1230 expectedSteps := map[registry.ResourceKey]struct{}{1231 {Name: tt.oldCRD.Name, Kind: "CustomResourceDefinition"}: {},1232 {Name: mainPackageStable, Kind: operatorsv1alpha1.ClusterServiceVersionKind}: {},1233 }1234 require.Equal(GinkgoT(), len(expectedSteps), len(fetchedInstallPlan.Status.Plan), "number of expected steps does not match installed")1235 for _, step := range fetchedInstallPlan.Status.Plan {1236 key := registry.ResourceKey{1237 Name: step.Resource.Name,1238 Kind: step.Resource.Kind,1239 }1240 _, ok := expectedSteps[key]1241 require.True(GinkgoT(), ok, "couldn't find %v in expected steps: %#v", key, expectedSteps)1242 // Remove the entry from the expected steps set (to ensure no duplicates in resolved plan)1243 delete(expectedSteps, key)1244 }1245 // Should have removed every matching step1246 require.Equal(GinkgoT(), 0, len(expectedSteps), "Actual resource steps do not match expected")1247 // Create initial CR1248 cleanupCR, err := createCR(c, existingCR, "cluster.com", "v1alpha1", ns.GetName(), tt.oldCRD.Spec.Names.Plural, "my-cr-1")1249 require.NoError(GinkgoT(), err)1250 defer cleanupCR()1251 updateInternalCatalog(GinkgoT(), c, crc, mainCatalogSourceName, ns.GetName(), []apiextensions.CustomResourceDefinition{*tt.newCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainStableCSV, mainBetaCSV}, mainManifests)1252 // Attempt to get the catalog source before creating install plan(s)1253 _, err = fetchCatalogSourceOnStatus(crc, mainCatalogSourceName, ns.GetName(), catalogSourceRegistryPodSynced)1254 require.NoError(GinkgoT(), err)1255 // Update the subscription resource to point to the beta CSV1256 err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {1257 subscription, err = fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)1258 require.NoError(GinkgoT(), err)1259 require.NotNil(GinkgoT(), subscription)1260 subscription.Spec.Channel = betaChannel1261 subscription, err = crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).Update(context.Background(), subscription, metav1.UpdateOptions{})1262 return err1263 })1264 // Wait for subscription to have a new installplan1265 subscription, err = fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanDifferentChecker(fetchedInstallPlan.GetName()))1266 require.NoError(GinkgoT(), err)1267 require.NotNil(GinkgoT(), subscription)1268 installPlanName = subscription.Status.InstallPlanRef.Name1269 // Wait for InstallPlan to be status: Complete or Failed before checking resource presence1270 fetchedInstallPlan, err = fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(tt.expectedPhase))1271 require.NoError(GinkgoT(), err)1272 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)1273 require.Equal(GinkgoT(), tt.expectedPhase, fetchedInstallPlan.Status.Phase)1274 // Ensure correct in-cluster resource(s)1275 fetchedCSV, err := fetchCSV(crc, mainBetaCSV.GetName(), ns.GetName(), csvAnyChecker)1276 require.NoError(GinkgoT(), err)1277 GinkgoT().Logf("All expected resources resolved %s", fetchedCSV.Status.Phase)1278 }, tableEntries...)1279 })1280 Describe("with deprecated version CRD", func() {1281 // generated outside of the test table so that the same naming can be used for both old and new CSVs1282 mainCRDPlural := genName("ins")1283 type schemaPayload struct {1284 name string1285 expectedPhase operatorsv1alpha1.InstallPlanPhase1286 oldCRD *apiextensions.CustomResourceDefinition1287 intermediateCRD *apiextensions.CustomResourceDefinition1288 newCRD *apiextensions.CustomResourceDefinition1289 }1290 // excluded: new CRD, same version, same schema - won't trigger a CRD update1291 tableEntries := []table.TableEntry{1292 table.Entry("upgrade CRD with deprecated version", schemaPayload{1293 name: "upgrade CRD with deprecated version",1294 expectedPhase: operatorsv1alpha1.InstallPlanPhaseComplete,1295 oldCRD: func() *apiextensions.CustomResourceDefinition {1296 oldCRD := newCRD(mainCRDPlural)1297 oldCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{1298 {1299 Name: "v1alpha1",1300 Served: true,1301 Storage: true,1302 Schema: &apiextensions.CustomResourceValidation{1303 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1304 Type: "object",1305 Description: "my crd schema",1306 },1307 },1308 },1309 }1310 return &oldCRD1311 }(),1312 intermediateCRD: func() *apiextensions.CustomResourceDefinition {1313 intermediateCRD := newCRD(mainCRDPlural)1314 intermediateCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{1315 {1316 Name: "v1alpha2",1317 Served: true,1318 Storage: true,1319 Schema: &apiextensions.CustomResourceValidation{1320 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1321 Type: "object",1322 Description: "my crd schema",1323 },1324 },1325 },1326 {1327 Name: "v1alpha1",1328 Served: false,1329 Storage: false,1330 Schema: &apiextensions.CustomResourceValidation{1331 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1332 Type: "object",1333 Description: "my crd schema",1334 },1335 },1336 },1337 }1338 return &intermediateCRD1339 }(),1340 newCRD: func() *apiextensions.CustomResourceDefinition {1341 newCRD := newCRD(mainCRDPlural)1342 newCRD.Spec.Versions = []apiextensions.CustomResourceDefinitionVersion{1343 {1344 Name: "v1alpha2",1345 Served: true,1346 Storage: true,1347 Schema: &apiextensions.CustomResourceValidation{1348 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1349 Type: "object",1350 Description: "my crd schema",1351 },1352 },1353 },1354 {1355 Name: "v1beta1",1356 Served: true,1357 Storage: false,1358 Schema: &apiextensions.CustomResourceValidation{1359 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1360 Type: "object",1361 Description: "my crd schema",1362 },1363 },1364 },1365 {1366 Name: "v1alpha1",1367 Served: false,1368 Storage: false,1369 Schema: &apiextensions.CustomResourceValidation{1370 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1371 Type: "object",1372 Description: "my crd schema",1373 },1374 },1375 },1376 }1377 return &newCRD1378 }(),1379 }),1380 }1381 table.DescribeTable("Test", func(tt schemaPayload) {1382 mainPackageName := genName("nginx-")1383 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)1384 mainPackageBeta := fmt.Sprintf("%s-beta", mainPackageName)1385 mainPackageDelta := fmt.Sprintf("%s-delta", mainPackageName)1386 stableChannel := "stable"1387 // Create manifests1388 mainManifests := []registry.PackageManifest{1389 {1390 PackageName: mainPackageName,1391 Channels: []registry.PackageChannel{1392 {Name: stableChannel, CurrentCSVName: mainPackageStable},1393 },1394 DefaultChannelName: stableChannel,1395 },1396 }1397 // Create new CSVs1398 mainStableCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{*tt.oldCRD}, nil, nil)1399 mainBetaCSV := newCSV(mainPackageBeta, ns.GetName(), mainPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{*tt.intermediateCRD}, nil, nil)1400 mainDeltaCSV := newCSV(mainPackageDelta, ns.GetName(), mainPackageBeta, semver.MustParse("0.3.0"), []apiextensions.CustomResourceDefinition{*tt.newCRD}, nil, nil)1401 // Defer CRD clean up1402 defer func() {1403 Eventually(func() error {1404 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), tt.oldCRD.GetName(), metav1.DeleteOptions{}))1405 }).Should(Succeed())1406 Eventually(func() error {1407 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), tt.newCRD.GetName(), metav1.DeleteOptions{}))1408 }).Should(Succeed())1409 if tt.intermediateCRD != nil {1410 Eventually(func() error {1411 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), tt.intermediateCRD.GetName(), metav1.DeleteOptions{}))1412 }).Should(Succeed())1413 }1414 }()1415 // Defer crd clean up1416 defer func() {1417 Expect(client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), tt.newCRD))).To(Succeed())1418 Expect(client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), tt.oldCRD))).To(Succeed())1419 Expect(client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), tt.intermediateCRD))).To(Succeed())1420 }()1421 // Create the catalog source1422 mainCatalogSourceName := genName("mock-ocs-main-")1423 _, cleanupCatalogSource := createInternalCatalogSource(c, crc, mainCatalogSourceName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{*tt.oldCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainStableCSV})1424 defer cleanupCatalogSource()1425 // Attempt to get the catalog source before creating install plan(s)1426 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogSourceName, ns.GetName(), catalogSourceRegistryPodSynced)1427 require.NoError(GinkgoT(), err)1428 subscriptionName := genName("sub-nginx-")1429 // this subscription will be cleaned up below without the clean up function1430 createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogSourceName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)1431 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)1432 require.NoError(GinkgoT(), err)1433 require.NotNil(GinkgoT(), subscription)1434 installPlanName := subscription.Status.InstallPlanRef.Name1435 // Wait for InstallPlan to be status: Complete or failed before checking resource presence1436 completeOrFailedFunc := buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete, operatorsv1alpha1.InstallPlanPhaseFailed)1437 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), completeOrFailedFunc)1438 require.NoError(GinkgoT(), err)1439 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)1440 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)1441 // Ensure CRD versions are accurate1442 expectedVersions := map[string]struct{}{1443 "v1alpha1": {},1444 }1445 validateCRDVersions(GinkgoT(), c, tt.oldCRD.GetName(), expectedVersions)1446 // Update the manifest1447 mainManifests = []registry.PackageManifest{1448 {1449 PackageName: mainPackageName,1450 Channels: []registry.PackageChannel{1451 {Name: stableChannel, CurrentCSVName: mainPackageBeta},1452 },1453 DefaultChannelName: stableChannel,1454 },1455 }1456 updateInternalCatalog(GinkgoT(), c, crc, mainCatalogSourceName, ns.GetName(), []apiextensions.CustomResourceDefinition{*tt.intermediateCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainStableCSV, mainBetaCSV}, mainManifests)1457 // Attempt to get the catalog source before creating install plan(s)1458 _, err = fetchCatalogSourceOnStatus(crc, mainCatalogSourceName, ns.GetName(), catalogSourceRegistryPodSynced)1459 require.NoError(GinkgoT(), err)1460 subscription, err = fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanDifferentChecker(installPlanName))1461 require.NoError(GinkgoT(), err)1462 require.NotNil(GinkgoT(), subscription)1463 installPlanName = subscription.Status.InstallPlanRef.Name1464 // Wait for InstallPlan to be status: Complete or Failed before checking resource presence1465 fetchedInstallPlan, err = fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete, operatorsv1alpha1.InstallPlanPhaseFailed))1466 require.NoError(GinkgoT(), err)1467 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)1468 require.Equal(GinkgoT(), tt.expectedPhase, fetchedInstallPlan.Status.Phase)1469 // Ensure correct in-cluster resource(s)1470 fetchedCSV, err := fetchCSV(crc, mainBetaCSV.GetName(), ns.GetName(), csvSucceededChecker)1471 require.NoError(GinkgoT(), err)1472 // Ensure CRD versions are accurate1473 expectedVersions = map[string]struct{}{1474 "v1alpha1": {},1475 "v1alpha2": {},1476 }1477 validateCRDVersions(GinkgoT(), c, tt.oldCRD.GetName(), expectedVersions)1478 // Update the manifest1479 mainManifests = []registry.PackageManifest{1480 {1481 PackageName: mainPackageName,1482 Channels: []registry.PackageChannel{1483 {Name: stableChannel, CurrentCSVName: mainPackageDelta},1484 },1485 DefaultChannelName: stableChannel,1486 },1487 }1488 updateInternalCatalog(GinkgoT(), c, crc, mainCatalogSourceName, ns.GetName(), []apiextensions.CustomResourceDefinition{*tt.newCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainStableCSV, mainBetaCSV, mainDeltaCSV}, mainManifests)1489 // Attempt to get the catalog source before creating install plan(s)1490 _, err = fetchCatalogSourceOnStatus(crc, mainCatalogSourceName, ns.GetName(), catalogSourceRegistryPodSynced)1491 require.NoError(GinkgoT(), err)1492 subscription, err = fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanDifferentChecker(installPlanName))1493 require.NoError(GinkgoT(), err)1494 require.NotNil(GinkgoT(), subscription)1495 installPlanName = subscription.Status.InstallPlanRef.Name1496 // Wait for InstallPlan to be status: Complete or Failed before checking resource presence1497 fetchedInstallPlan, err = fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete, operatorsv1alpha1.InstallPlanPhaseFailed))1498 require.NoError(GinkgoT(), err)1499 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)1500 require.Equal(GinkgoT(), tt.expectedPhase, fetchedInstallPlan.Status.Phase)1501 // Ensure correct in-cluster resource(s)1502 fetchedCSV, err = fetchCSV(crc, mainDeltaCSV.GetName(), ns.GetName(), csvSucceededChecker)1503 require.NoError(GinkgoT(), err)1504 // Ensure CRD versions are accurate1505 expectedVersions = map[string]struct{}{1506 "v1alpha2": {},1507 "v1beta1": {},1508 "v1alpha1": {},1509 }1510 validateCRDVersions(GinkgoT(), c, tt.oldCRD.GetName(), expectedVersions)1511 GinkgoT().Logf("All expected resources resolved %s", fetchedCSV.Status.Phase)1512 }, tableEntries...)1513 })1514 Describe("update catalog for subscription", func() {1515 // crdVersionKey uniquely identifies a version within a CRD.1516 type crdVersionKey struct {1517 name string1518 served bool1519 storage bool1520 }1521 It("AmplifyPermissions", func() {1522 defer func() {1523 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))1524 }()1525 // Build initial catalog1526 mainPackageName := genName("nginx-amplify-")1527 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)1528 stableChannel := "stable"1529 crdPlural := genName("ins-amplify-")1530 crdName := crdPlural + ".cluster.com"1531 mainCRD := apiextensions.CustomResourceDefinition{1532 ObjectMeta: metav1.ObjectMeta{1533 Name: crdName,1534 },1535 Spec: apiextensions.CustomResourceDefinitionSpec{1536 Group: "cluster.com",1537 Versions: []apiextensions.CustomResourceDefinitionVersion{1538 {1539 Name: "v1alpha1",1540 Served: true,1541 Storage: true,1542 Schema: &apiextensions.CustomResourceValidation{1543 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1544 Type: "object",1545 Description: "my crd schema",1546 },1547 },1548 },1549 },1550 Names: apiextensions.CustomResourceDefinitionNames{1551 Plural: crdPlural,1552 Singular: crdPlural,1553 Kind: crdPlural,1554 ListKind: "list" + crdPlural,1555 },1556 Scope: apiextensions.NamespaceScoped,1557 },1558 }1559 // Generate permissions1560 serviceAccountName := genName("nginx-sa")1561 permissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1562 {1563 ServiceAccountName: serviceAccountName,1564 Rules: []rbacv1.PolicyRule{1565 {1566 Verbs: []string{rbac.VerbAll},1567 APIGroups: []string{"cluster.com"},1568 Resources: []string{crdPlural},1569 },1570 },1571 },1572 }1573 // Generate permissions1574 clusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1575 {1576 ServiceAccountName: serviceAccountName,1577 Rules: []rbacv1.PolicyRule{1578 {1579 Verbs: []string{rbac.VerbAll},1580 APIGroups: []string{"cluster.com"},1581 Resources: []string{crdPlural},1582 },1583 },1584 },1585 }1586 // Create the catalog sources1587 mainNamedStrategy := newNginxInstallStrategy(genName("dep-"), permissions, clusterPermissions)1588 mainCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), nil, nil, &mainNamedStrategy)1589 mainCatalogName := genName("mock-ocs-amplify-")1590 mainManifests := []registry.PackageManifest{1591 {1592 PackageName: mainPackageName,1593 Channels: []registry.PackageChannel{1594 {Name: stableChannel, CurrentCSVName: mainCSV.GetName()},1595 },1596 DefaultChannelName: stableChannel,1597 },1598 }1599 // Defer CRD clean up1600 defer func() {1601 Eventually(func() error {1602 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), mainCRD.GetName(), metav1.DeleteOptions{}))1603 }).Should(Succeed())1604 }()1605 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{mainCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})1606 defer cleanupMainCatalogSource()1607 // Attempt to get the catalog source before creating install plan1608 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)1609 require.NoError(GinkgoT(), err)1610 subscriptionName := genName("sub-nginx-update-perms1")1611 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)1612 defer subscriptionCleanup()1613 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)1614 require.NoError(GinkgoT(), err)1615 require.NotNil(GinkgoT(), subscription)1616 require.NotNil(GinkgoT(), subscription.Status.InstallPlanRef)1617 require.Equal(GinkgoT(), mainCSV.GetName(), subscription.Status.CurrentCSV)1618 installPlanName := subscription.Status.InstallPlanRef.Name1619 // Wait for InstallPlan to be status: Complete before checking resource presence1620 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))1621 require.NoError(GinkgoT(), err)1622 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)1623 // Verify CSV is created1624 _, err = awaitCSV(crc, ns.GetName(), mainCSV.GetName(), csvSucceededChecker)1625 require.NoError(GinkgoT(), err)1626 // Update CatalogSource with a new CSV with more permissions1627 updatedPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1628 {1629 ServiceAccountName: serviceAccountName,1630 Rules: []rbacv1.PolicyRule{1631 {1632 Verbs: []string{rbac.VerbAll},1633 APIGroups: []string{"cluster.com"},1634 Resources: []string{crdPlural},1635 },1636 {1637 Verbs: []string{rbac.VerbAll},1638 APIGroups: []string{"local.cluster.com"},1639 Resources: []string{"locals"},1640 },1641 },1642 },1643 }1644 updatedClusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1645 {1646 ServiceAccountName: serviceAccountName,1647 Rules: []rbacv1.PolicyRule{1648 {1649 Verbs: []string{rbac.VerbAll},1650 APIGroups: []string{"cluster.com"},1651 Resources: []string{crdPlural},1652 },1653 {1654 Verbs: []string{rbac.VerbAll},1655 APIGroups: []string{"two.cluster.com"},1656 Resources: []string{"twos"},1657 },1658 },1659 },1660 }1661 // Create the catalog sources1662 updatedNamedStrategy := newNginxInstallStrategy(genName("dep-"), updatedPermissions, updatedClusterPermissions)1663 updatedCSV := newCSV(mainPackageStable+"-next", ns.GetName(), mainCSV.GetName(), semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, &updatedNamedStrategy)1664 updatedManifests := []registry.PackageManifest{1665 {1666 PackageName: mainPackageName,1667 Channels: []registry.PackageChannel{1668 {Name: stableChannel, CurrentCSVName: updatedCSV.GetName()},1669 },1670 DefaultChannelName: stableChannel,1671 },1672 }1673 // Update catalog with updated CSV with more permissions1674 updateInternalCatalog(GinkgoT(), c, crc, mainCatalogName, ns.GetName(), []apiextensions.CustomResourceDefinition{mainCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV, updatedCSV}, updatedManifests)1675 _, err = fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanDifferentChecker(fetchedInstallPlan.GetName()))1676 require.NoError(GinkgoT(), err)1677 updatedInstallPlanName := subscription.Status.InstallPlanRef.Name1678 // Wait for InstallPlan to be status: Complete before checking resource presence1679 fetchedUpdatedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, updatedInstallPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))1680 require.NoError(GinkgoT(), err)1681 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedUpdatedInstallPlan.Status.Phase)1682 // Wait for csv to update1683 _, err = awaitCSV(crc, ns.GetName(), updatedCSV.GetName(), csvSucceededChecker)1684 require.NoError(GinkgoT(), err)1685 // If the CSV is succeeded, we successfully rolled out the RBAC changes1686 })1687 It("AttenuatePermissions", func() {1688 defer func() {1689 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))1690 }()1691 // Build initial catalog1692 mainPackageName := genName("nginx-attenuate-")1693 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)1694 stableChannel := "stable"1695 crdPlural := genName("ins-attenuate-")1696 crdName := crdPlural + ".cluster.com"1697 mainCRD := apiextensions.CustomResourceDefinition{1698 ObjectMeta: metav1.ObjectMeta{1699 Name: crdName,1700 },1701 Spec: apiextensions.CustomResourceDefinitionSpec{1702 Group: "cluster.com",1703 Versions: []apiextensions.CustomResourceDefinitionVersion{1704 {1705 Name: "v1alpha1",1706 Served: true,1707 Storage: true,1708 Schema: &apiextensions.CustomResourceValidation{1709 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1710 Type: "object",1711 Description: "my crd schema",1712 },1713 },1714 },1715 },1716 Names: apiextensions.CustomResourceDefinitionNames{1717 Plural: crdPlural,1718 Singular: crdPlural,1719 Kind: crdPlural,1720 ListKind: "list" + crdPlural,1721 },1722 Scope: apiextensions.NamespaceScoped,1723 },1724 }1725 // Generate permissions1726 serviceAccountName := genName("nginx-sa")1727 permissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1728 {1729 ServiceAccountName: serviceAccountName,1730 Rules: []rbacv1.PolicyRule{1731 {1732 Verbs: []string{rbac.VerbAll},1733 APIGroups: []string{"cluster.com"},1734 Resources: []string{crdPlural},1735 },1736 {1737 Verbs: []string{rbac.VerbAll},1738 APIGroups: []string{"local.cluster.com"},1739 Resources: []string{"locals"},1740 },1741 },1742 },1743 }1744 // Generate permissions1745 clusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1746 {1747 ServiceAccountName: serviceAccountName,1748 Rules: []rbacv1.PolicyRule{1749 {1750 Verbs: []string{rbac.VerbAll},1751 APIGroups: []string{"cluster.com"},1752 Resources: []string{crdPlural},1753 },1754 {1755 Verbs: []string{rbac.VerbAll},1756 APIGroups: []string{"two.cluster.com"},1757 Resources: []string{"twos"},1758 },1759 },1760 },1761 }1762 // Create the catalog sources1763 mainNamedStrategy := newNginxInstallStrategy(genName("dep-"), permissions, clusterPermissions)1764 mainCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), nil, nil, &mainNamedStrategy)1765 mainCatalogName := genName("mock-ocs-main-update-perms1-")1766 mainManifests := []registry.PackageManifest{1767 {1768 PackageName: mainPackageName,1769 Channels: []registry.PackageChannel{1770 {Name: stableChannel, CurrentCSVName: mainCSV.GetName()},1771 },1772 DefaultChannelName: stableChannel,1773 },1774 }1775 // Defer CRD clean up1776 defer func() {1777 Eventually(func() error {1778 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), mainCRD.GetName(), metav1.DeleteOptions{}))1779 }).Should(Succeed())1780 }()1781 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{mainCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})1782 defer cleanupMainCatalogSource()1783 // Attempt to get the catalog source before creating install plan1784 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)1785 require.NoError(GinkgoT(), err)1786 subscriptionName := genName("sub-nginx-update-perms1")1787 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)1788 defer subscriptionCleanup()1789 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)1790 require.NoError(GinkgoT(), err)1791 require.NotNil(GinkgoT(), subscription)1792 require.NotNil(GinkgoT(), subscription.Status.InstallPlanRef)1793 require.Equal(GinkgoT(), mainCSV.GetName(), subscription.Status.CurrentCSV)1794 installPlanName := subscription.Status.InstallPlanRef.Name1795 // Wait for InstallPlan to be status: Complete before checking resource presence1796 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))1797 require.NoError(GinkgoT(), err)1798 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)1799 // Verify CSV is created1800 _, err = awaitCSV(crc, ns.GetName(), mainCSV.GetName(), csvSucceededChecker)1801 require.NoError(GinkgoT(), err)1802 // Update CatalogSource with a new CSV with more permissions1803 updatedPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1804 {1805 ServiceAccountName: serviceAccountName,1806 Rules: []rbacv1.PolicyRule{1807 {1808 Verbs: []string{rbac.VerbAll},1809 APIGroups: []string{"local.cluster.com"},1810 Resources: []string{"locals"},1811 },1812 },1813 },1814 }1815 updatedClusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1816 {1817 ServiceAccountName: serviceAccountName,1818 Rules: []rbacv1.PolicyRule{1819 {1820 Verbs: []string{rbac.VerbAll},1821 APIGroups: []string{"two.cluster.com"},1822 Resources: []string{"twos"},1823 },1824 },1825 },1826 }1827 oldSecrets, err := c.KubernetesInterface().CoreV1().Secrets(ns.GetName()).List(context.Background(), metav1.ListOptions{})1828 require.NoError(GinkgoT(), err, "error listing secrets")1829 // Create the catalog sources1830 updatedNamedStrategy := newNginxInstallStrategy(genName("dep-"), updatedPermissions, updatedClusterPermissions)1831 updatedCSV := newCSV(mainPackageStable+"-next", ns.GetName(), mainCSV.GetName(), semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, &updatedNamedStrategy)1832 updatedManifests := []registry.PackageManifest{1833 {1834 PackageName: mainPackageName,1835 Channels: []registry.PackageChannel{1836 {Name: stableChannel, CurrentCSVName: updatedCSV.GetName()},1837 },1838 DefaultChannelName: stableChannel,1839 },1840 }1841 // Update catalog with updated CSV with more permissions1842 updateInternalCatalog(GinkgoT(), c, crc, mainCatalogName, ns.GetName(), []apiextensions.CustomResourceDefinition{mainCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV, updatedCSV}, updatedManifests)1843 // Wait for subscription to update its status1844 _, err = fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanDifferentChecker(fetchedInstallPlan.GetName()))1845 require.NoError(GinkgoT(), err)1846 updatedInstallPlanName := subscription.Status.InstallPlanRef.Name1847 // Wait for InstallPlan to be status: Complete before checking resource presence1848 fetchedUpdatedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, updatedInstallPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))1849 require.NoError(GinkgoT(), err)1850 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedUpdatedInstallPlan.Status.Phase)1851 // Wait for csv to update1852 _, err = awaitCSV(crc, ns.GetName(), updatedCSV.GetName(), csvSucceededChecker)1853 require.NoError(GinkgoT(), err)1854 newSecrets, err := c.KubernetesInterface().CoreV1().Secrets(ns.GetName()).List(context.Background(), metav1.ListOptions{})1855 require.NoError(GinkgoT(), err, "error listing secrets")1856 // Assert that the number of secrets is not increased from updating service account as part of the install plan,1857 assert.EqualValues(GinkgoT(), len(oldSecrets.Items), len(newSecrets.Items))1858 // And that the secret list is indeed updated.1859 assert.Equal(GinkgoT(), oldSecrets.Items, newSecrets.Items)1860 // Wait for ServiceAccount to not have access anymore1861 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {1862 res, err := c.KubernetesInterface().AuthorizationV1().SubjectAccessReviews().Create(context.Background(), &authorizationv1.SubjectAccessReview{1863 Spec: authorizationv1.SubjectAccessReviewSpec{1864 User: "system:serviceaccount:" + ns.GetName() + ":" + serviceAccountName,1865 ResourceAttributes: &authorizationv1.ResourceAttributes{1866 Group: "cluster.com",1867 Version: "v1alpha1",1868 Resource: crdPlural,1869 Verb: rbac.VerbAll,1870 },1871 },1872 }, metav1.CreateOptions{})1873 if err != nil {1874 return false, err1875 }1876 if res == nil {1877 return false, nil1878 }1879 GinkgoT().Log("checking serviceaccount for permission")1880 // should not be allowed1881 return !res.Status.Allowed, nil1882 })1883 })1884 It("StopOnCSVModifications", func() {1885 defer func() {1886 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))1887 }()1888 // Build initial catalog1889 mainPackageName := genName("nginx-amplify-")1890 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)1891 stableChannel := "stable"1892 crdPlural := genName("ins-amplify-")1893 crdName := crdPlural + ".cluster.com"1894 mainCRD := apiextensions.CustomResourceDefinition{1895 ObjectMeta: metav1.ObjectMeta{1896 Name: crdName,1897 },1898 Spec: apiextensions.CustomResourceDefinitionSpec{1899 Group: "cluster.com",1900 Versions: []apiextensions.CustomResourceDefinitionVersion{1901 {1902 Name: "v1alpha1",1903 Served: true,1904 Storage: true,1905 Schema: &apiextensions.CustomResourceValidation{1906 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{1907 Type: "object",1908 Description: "my crd schema",1909 },1910 },1911 },1912 },1913 Names: apiextensions.CustomResourceDefinitionNames{1914 Plural: crdPlural,1915 Singular: crdPlural,1916 Kind: crdPlural,1917 ListKind: "list" + crdPlural,1918 },1919 Scope: apiextensions.NamespaceScoped,1920 },1921 }1922 // Generate permissions1923 serviceAccountName := genName("nginx-sa")1924 permissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1925 {1926 ServiceAccountName: serviceAccountName,1927 Rules: []rbacv1.PolicyRule{1928 {1929 Verbs: []string{rbac.VerbAll},1930 APIGroups: []string{"cluster.com"},1931 Resources: []string{crdPlural},1932 },1933 },1934 },1935 }1936 // Generate permissions1937 clusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{1938 {1939 ServiceAccountName: serviceAccountName,1940 Rules: []rbacv1.PolicyRule{1941 {1942 Verbs: []string{rbac.VerbAll},1943 APIGroups: []string{"cluster.com"},1944 Resources: []string{crdPlural},1945 },1946 },1947 },1948 }1949 // Create the catalog sources1950 deploymentName := genName("dep-")1951 mainNamedStrategy := newNginxInstallStrategy(deploymentName, permissions, clusterPermissions)1952 mainCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), nil, nil, &mainNamedStrategy)1953 mainCatalogName := genName("mock-ocs-stomper-")1954 mainManifests := []registry.PackageManifest{1955 {1956 PackageName: mainPackageName,1957 Channels: []registry.PackageChannel{1958 {Name: stableChannel, CurrentCSVName: mainCSV.GetName()},1959 },1960 DefaultChannelName: stableChannel,1961 },1962 }1963 // Defer CRD clean up1964 defer func() {1965 Eventually(func() error {1966 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), mainCRD.GetName(), metav1.DeleteOptions{}))1967 }).Should(Succeed())1968 }()1969 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{mainCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})1970 defer cleanupMainCatalogSource()1971 // Attempt to get the catalog source before creating install plan1972 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)1973 require.NoError(GinkgoT(), err)1974 subscriptionName := genName("sub-nginx-stompy-")1975 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)1976 defer subscriptionCleanup()1977 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)1978 require.NoError(GinkgoT(), err)1979 require.NotNil(GinkgoT(), subscription)1980 require.NotNil(GinkgoT(), subscription.Status.InstallPlanRef)1981 require.Equal(GinkgoT(), mainCSV.GetName(), subscription.Status.CurrentCSV)1982 installPlanName := subscription.Status.InstallPlanRef.Name1983 // Wait for InstallPlan to be status: Complete before checking resource presence1984 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))1985 require.NoError(GinkgoT(), err)1986 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)1987 // Verify CSV is created1988 csv, err := awaitCSV(crc, ns.GetName(), mainCSV.GetName(), csvSucceededChecker)1989 require.NoError(GinkgoT(), err)1990 addedEnvVar := corev1.EnvVar{Name: "EXAMPLE", Value: "value"}1991 modifiedDetails := operatorsv1alpha1.StrategyDetailsDeployment{1992 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{1993 {1994 Name: deploymentName,1995 Spec: appsv1.DeploymentSpec{1996 Selector: &metav1.LabelSelector{1997 MatchLabels: map[string]string{"app": "nginx"},1998 },1999 Replicas: &singleInstance,2000 Template: corev1.PodTemplateSpec{2001 ObjectMeta: metav1.ObjectMeta{2002 Labels: map[string]string{"app": "nginx"},2003 },2004 Spec: corev1.PodSpec{Containers: []corev1.Container{2005 {2006 Name: genName("nginx"),2007 Image: *dummyImage,2008 Ports: []corev1.ContainerPort{{ContainerPort: 80}},2009 ImagePullPolicy: corev1.PullIfNotPresent,2010 Env: []corev1.EnvVar{addedEnvVar},2011 },2012 }},2013 },2014 },2015 },2016 },2017 Permissions: permissions,2018 ClusterPermissions: clusterPermissions,2019 }2020 csv.Spec.InstallStrategy = operatorsv1alpha1.NamedInstallStrategy{2021 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment,2022 StrategySpec: modifiedDetails,2023 }2024 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(ns.GetName()).Update(context.Background(), csv, metav1.UpdateOptions{})2025 require.NoError(GinkgoT(), err)2026 // Wait for csv to update2027 _, err = awaitCSV(crc, ns.GetName(), csv.GetName(), csvSucceededChecker)2028 require.NoError(GinkgoT(), err)2029 // Should have the updated env var2030 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {2031 dep, err := c.GetDeployment(ns.GetName(), deploymentName)2032 if err != nil {2033 return false, nil2034 }2035 if len(dep.Spec.Template.Spec.Containers[0].Env) == 0 {2036 return false, nil2037 }2038 for _, envVar := range dep.Spec.Template.Spec.Containers[0].Env {2039 if envVar == addedEnvVar {2040 return true, nil2041 }2042 }2043 return false, nil2044 })2045 require.NoError(GinkgoT(), err)2046 // Create the catalog sources2047 // Updated csv has the same deployment strategy as main2048 updatedCSV := newCSV(mainPackageStable+"-next", ns.GetName(), mainCSV.GetName(), semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, &mainNamedStrategy)2049 updatedManifests := []registry.PackageManifest{2050 {2051 PackageName: mainPackageName,2052 Channels: []registry.PackageChannel{2053 {Name: stableChannel, CurrentCSVName: updatedCSV.GetName()},2054 },2055 DefaultChannelName: stableChannel,2056 },2057 }2058 // Update catalog with updated CSV with more permissions2059 updateInternalCatalog(GinkgoT(), c, crc, mainCatalogName, ns.GetName(), []apiextensions.CustomResourceDefinition{mainCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV, updatedCSV}, updatedManifests)2060 _, err = fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanDifferentChecker(fetchedInstallPlan.GetName()))2061 require.NoError(GinkgoT(), err)2062 updatedInstallPlanName := subscription.Status.InstallPlanRef.Name2063 // Wait for InstallPlan to be status: Complete before checking resource presence2064 fetchedUpdatedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, updatedInstallPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))2065 require.NoError(GinkgoT(), err)2066 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedUpdatedInstallPlan.Status.Phase)2067 // Wait for csv to update2068 _, err = awaitCSV(crc, ns.GetName(), updatedCSV.GetName(), csvSucceededChecker)2069 require.NoError(GinkgoT(), err)2070 // Should have created deployment and stomped on the env changes2071 updatedDep, err := c.GetDeployment(ns.GetName(), deploymentName)2072 require.NoError(GinkgoT(), err)2073 require.NotNil(GinkgoT(), updatedDep)2074 // Should have the updated env var2075 for _, envVar := range updatedDep.Spec.Template.Spec.Containers[0].Env {2076 require.False(GinkgoT(), envVar == addedEnvVar)2077 }2078 })2079 It("UpdateSingleExistingCRDOwner", func() {2080 mainPackageName := genName("nginx-update-")2081 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)2082 mainPackageBeta := fmt.Sprintf("%s-beta", mainPackageName)2083 stableChannel := "stable"2084 crdPlural := genName("ins-update-")2085 crdName := crdPlural + ".cluster.com"2086 mainCRD := apiextensions.CustomResourceDefinition{2087 ObjectMeta: metav1.ObjectMeta{2088 Name: crdName,2089 },2090 Spec: apiextensions.CustomResourceDefinitionSpec{2091 Group: "cluster.com",2092 Versions: []apiextensions.CustomResourceDefinitionVersion{2093 {2094 Name: "v1alpha1",2095 Served: true,2096 Storage: true,2097 Schema: &apiextensions.CustomResourceValidation{2098 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{2099 Type: "object",2100 Description: "my crd schema",2101 },2102 },2103 },2104 },2105 Names: apiextensions.CustomResourceDefinitionNames{2106 Plural: crdPlural,2107 Singular: crdPlural,2108 Kind: crdPlural,2109 ListKind: "list" + crdPlural,2110 },2111 Scope: apiextensions.NamespaceScoped,2112 },2113 }2114 updatedCRD := apiextensions.CustomResourceDefinition{2115 ObjectMeta: metav1.ObjectMeta{2116 Name: crdName,2117 },2118 Spec: apiextensions.CustomResourceDefinitionSpec{2119 Group: "cluster.com",2120 Versions: []apiextensions.CustomResourceDefinitionVersion{2121 {2122 Name: "v1alpha1",2123 Served: true,2124 Storage: true,2125 Schema: &apiextensions.CustomResourceValidation{2126 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{2127 Type: "object",2128 Description: "my crd schema",2129 },2130 },2131 },2132 {2133 Name: "v1alpha2",2134 Served: true,2135 Storage: false,2136 Schema: &apiextensions.CustomResourceValidation{2137 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{2138 Type: "object",2139 Description: "my crd schema",2140 },2141 },2142 },2143 },2144 Names: apiextensions.CustomResourceDefinitionNames{2145 Plural: crdPlural,2146 Singular: crdPlural,2147 Kind: crdPlural,2148 ListKind: "list" + crdPlural,2149 },2150 Scope: apiextensions.NamespaceScoped,2151 },2152 }2153 mainCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, nil)2154 betaCSV := newCSV(mainPackageBeta, ns.GetName(), mainPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{updatedCRD}, nil, nil)2155 // Defer CRD clean up2156 defer func() {2157 Eventually(func() error {2158 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), mainCRD.GetName(), metav1.DeleteOptions{}))2159 }).Should(Succeed())2160 Eventually(func() error {2161 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), updatedCRD.GetName(), metav1.DeleteOptions{}))2162 }).Should(Succeed())2163 }()2164 defer func() {2165 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))2166 }()2167 mainCatalogName := genName("mock-ocs-main-update-")2168 // Create separate manifests for each CatalogSource2169 mainManifests := []registry.PackageManifest{2170 {2171 PackageName: mainPackageName,2172 Channels: []registry.PackageChannel{2173 {Name: stableChannel, CurrentCSVName: mainPackageStable},2174 },2175 DefaultChannelName: stableChannel,2176 },2177 }2178 // Create the catalog sources2179 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{mainCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})2180 defer cleanupMainCatalogSource()2181 // Attempt to get the catalog source before creating install plan2182 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)2183 require.NoError(GinkgoT(), err)2184 subscriptionName := genName("sub-nginx-update-")2185 createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)2186 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)2187 require.NoError(GinkgoT(), err)2188 require.NotNil(GinkgoT(), subscription)2189 require.NotNil(GinkgoT(), subscription.Status.InstallPlanRef)2190 require.Equal(GinkgoT(), mainCSV.GetName(), subscription.Status.CurrentCSV)2191 installPlanName := subscription.Status.InstallPlanRef.Name2192 // Wait for InstallPlan to be status: Complete before checking resource presence2193 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))2194 require.NoError(GinkgoT(), err)2195 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)2196 // Fetch installplan again to check for unnecessary control loops2197 fetchedInstallPlan, err = fetchInstallPlan(GinkgoT(), crc, fetchedInstallPlan.GetName(), ns.GetName(), func(fip *operatorsv1alpha1.InstallPlan) bool {2198 Expect(equality.Semantic.DeepEqual(fetchedInstallPlan, fip)).Should(BeTrue(), diff.ObjectDiff(fetchedInstallPlan, fip))2199 return true2200 })2201 require.NoError(GinkgoT(), err)2202 // Verify CSV is created2203 _, err = awaitCSV(crc, ns.GetName(), mainCSV.GetName(), csvAnyChecker)2204 require.NoError(GinkgoT(), err)2205 mainManifests = []registry.PackageManifest{2206 {2207 PackageName: mainPackageName,2208 Channels: []registry.PackageChannel{2209 {Name: stableChannel, CurrentCSVName: mainPackageBeta},2210 },2211 DefaultChannelName: stableChannel,2212 },2213 }2214 updateInternalCatalog(GinkgoT(), c, crc, mainCatalogName, ns.GetName(), []apiextensions.CustomResourceDefinition{updatedCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV, betaCSV}, mainManifests)2215 // Wait for subscription to update2216 updatedSubscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanDifferentChecker(fetchedInstallPlan.GetName()))2217 require.NoError(GinkgoT(), err)2218 // Verify installplan created and installed2219 fetchedUpdatedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, updatedSubscription.Status.InstallPlanRef.Name, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))2220 require.NoError(GinkgoT(), err)2221 require.NotEqual(GinkgoT(), fetchedInstallPlan.GetName(), fetchedUpdatedInstallPlan.GetName())2222 // Wait for csv to update2223 _, err = awaitCSV(crc, ns.GetName(), betaCSV.GetName(), csvAnyChecker)2224 require.NoError(GinkgoT(), err)2225 // Get the CRD to see if it is updated2226 fetchedCRD, err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), crdName, metav1.GetOptions{})2227 require.NoError(GinkgoT(), err)2228 require.Equal(GinkgoT(), len(fetchedCRD.Spec.Versions), len(updatedCRD.Spec.Versions), "The CRD versions counts don't match")2229 fetchedCRDVersions := map[crdVersionKey]struct{}{}2230 for _, version := range fetchedCRD.Spec.Versions {2231 key := crdVersionKey{2232 name: version.Name,2233 served: version.Served,2234 storage: version.Storage,2235 }2236 fetchedCRDVersions[key] = struct{}{}2237 }2238 for _, version := range updatedCRD.Spec.Versions {2239 key := crdVersionKey{2240 name: version.Name,2241 served: version.Served,2242 storage: version.Storage,2243 }2244 _, ok := fetchedCRDVersions[key]2245 require.True(GinkgoT(), ok, "couldn't find %v in fetched CRD versions: %#v", key, fetchedCRDVersions)2246 }2247 })2248 It("UpdatePreexistingCRDFailed", func() {2249 defer func() {2250 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))2251 }()2252 mainPackageName := genName("nginx-update2-")2253 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)2254 stableChannel := "stable"2255 crdPlural := genName("ins-update2-")2256 crdName := crdPlural + ".cluster.com"2257 mainCRD := apiextensions.CustomResourceDefinition{2258 ObjectMeta: metav1.ObjectMeta{2259 Name: crdName,2260 },2261 Spec: apiextensions.CustomResourceDefinitionSpec{2262 Group: "cluster.com",2263 Versions: []apiextensions.CustomResourceDefinitionVersion{2264 {2265 Name: "v1alpha1",2266 Served: true,2267 Storage: true,2268 Schema: &apiextensions.CustomResourceValidation{2269 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{2270 Type: "object",2271 Description: "my crd schema",2272 },2273 },2274 },2275 },2276 Names: apiextensions.CustomResourceDefinitionNames{2277 Plural: crdPlural,2278 Singular: crdPlural,2279 Kind: crdPlural,2280 ListKind: "list" + crdPlural,2281 },2282 Scope: apiextensions.NamespaceScoped,2283 },2284 }2285 updatedCRD := apiextensions.CustomResourceDefinition{2286 ObjectMeta: metav1.ObjectMeta{2287 Name: crdName,2288 },2289 Spec: apiextensions.CustomResourceDefinitionSpec{2290 Group: "cluster.com",2291 Versions: []apiextensions.CustomResourceDefinitionVersion{2292 {2293 Name: "v1alpha1",2294 Served: true,2295 Storage: true,2296 Schema: &apiextensions.CustomResourceValidation{2297 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{2298 Type: "object",2299 Description: "my crd schema",2300 },2301 },2302 },2303 {2304 Name: "v1alpha2",2305 Served: true,2306 Storage: false,2307 Schema: &apiextensions.CustomResourceValidation{2308 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{2309 Type: "object",2310 Description: "my crd schema",2311 },2312 },2313 },2314 },2315 Names: apiextensions.CustomResourceDefinitionNames{2316 Plural: crdPlural,2317 Singular: crdPlural,2318 Kind: crdPlural,2319 ListKind: "list" + crdPlural,2320 },2321 Scope: apiextensions.NamespaceScoped,2322 },2323 }2324 expectedCRDVersions := map[crdVersionKey]struct{}{}2325 for _, version := range mainCRD.Spec.Versions {2326 key := crdVersionKey{2327 name: version.Name,2328 served: version.Served,2329 storage: version.Storage,2330 }2331 expectedCRDVersions[key] = struct{}{}2332 }2333 // Create the initial CSV2334 cleanupCRD, err := createCRD(c, mainCRD)2335 require.NoError(GinkgoT(), err)2336 defer cleanupCRD()2337 mainCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), nil, nil, nil)2338 mainCatalogName := genName("mock-ocs-main-update2-")2339 // Create separate manifests for each CatalogSource2340 mainManifests := []registry.PackageManifest{2341 {2342 PackageName: mainPackageName,2343 Channels: []registry.PackageChannel{2344 {Name: stableChannel, CurrentCSVName: mainPackageStable},2345 },2346 DefaultChannelName: stableChannel,2347 },2348 }2349 // Create the catalog sources2350 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{updatedCRD}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})2351 defer cleanupMainCatalogSource()2352 // Attempt to get the catalog source before creating install plan2353 _, err = fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)2354 require.NoError(GinkgoT(), err)2355 subscriptionName := genName("sub-nginx-update2-")2356 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)2357 defer subscriptionCleanup()2358 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)2359 require.NoError(GinkgoT(), err)2360 require.NotNil(GinkgoT(), subscription)2361 require.NotNil(GinkgoT(), subscription.Status.InstallPlanRef)2362 require.Equal(GinkgoT(), mainCSV.GetName(), subscription.Status.CurrentCSV)2363 installPlanName := subscription.Status.InstallPlanRef.Name2364 // Wait for InstallPlan to be status: Complete before checking resource presence2365 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))2366 require.NoError(GinkgoT(), err)2367 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)2368 // Fetch installplan again to check for unnecessary control loops2369 fetchedInstallPlan, err = fetchInstallPlan(GinkgoT(), crc, fetchedInstallPlan.GetName(), ns.GetName(), func(fip *operatorsv1alpha1.InstallPlan) bool {2370 Expect(equality.Semantic.DeepEqual(fetchedInstallPlan, fip)).Should(BeTrue(), diff.ObjectDiff(fetchedInstallPlan, fip))2371 return true2372 })2373 require.NoError(GinkgoT(), err)2374 // Verify CSV is created2375 _, err = awaitCSV(crc, ns.GetName(), mainCSV.GetName(), csvAnyChecker)2376 require.NoError(GinkgoT(), err)2377 // Get the CRD to see if it is updated2378 fetchedCRD, err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), crdName, metav1.GetOptions{})2379 require.NoError(GinkgoT(), err)2380 require.Equal(GinkgoT(), len(fetchedCRD.Spec.Versions), len(mainCRD.Spec.Versions), "The CRD versions counts don't match")2381 fetchedCRDVersions := map[crdVersionKey]struct{}{}2382 for _, version := range fetchedCRD.Spec.Versions {2383 key := crdVersionKey{2384 name: version.Name,2385 served: version.Served,2386 storage: version.Storage,2387 }2388 fetchedCRDVersions[key] = struct{}{}2389 }2390 for _, version := range mainCRD.Spec.Versions {2391 key := crdVersionKey{2392 name: version.Name,2393 served: version.Served,2394 storage: version.Storage,2395 }2396 _, ok := fetchedCRDVersions[key]2397 require.True(GinkgoT(), ok, "couldn't find %v in fetched CRD versions: %#v", key, fetchedCRDVersions)2398 }2399 })2400 })2401 // This It spec creates an InstallPlan with a CSV containing a set of permissions to be resolved.2402 It("creation with permissions", func() {2403 packageName := genName("nginx")2404 stableChannel := "stable"2405 stableCSVName := packageName + "-stable"2406 // Create manifests2407 manifests := []registry.PackageManifest{2408 {2409 PackageName: packageName,2410 Channels: []registry.PackageChannel{2411 {2412 Name: stableChannel,2413 CurrentCSVName: stableCSVName,2414 },2415 },2416 DefaultChannelName: stableChannel,2417 },2418 }2419 // Create new CRDs2420 crdPlural := genName("ins")2421 crd := newCRD(crdPlural)2422 // Defer CRD clean up2423 defer func() {2424 Eventually(func() error {2425 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{}))2426 }).Should(Succeed())2427 }()2428 // Generate permissions2429 serviceAccountName := genName("nginx-sa")2430 permissions := []operatorsv1alpha1.StrategyDeploymentPermissions{2431 {2432 ServiceAccountName: serviceAccountName,2433 Rules: []rbacv1.PolicyRule{2434 {2435 Verbs: []string{rbac.VerbAll},2436 APIGroups: []string{"cluster.com"},2437 Resources: []string{crdPlural},2438 },2439 // Permissions must be different than ClusterPermissions defined below if OLM is going to lift role/rolebindings to cluster level.2440 {2441 Verbs: []string{rbac.VerbAll},2442 APIGroups: []string{corev1.GroupName},2443 Resources: []string{corev1.ResourceConfigMaps.String()},2444 },2445 },2446 },2447 }2448 // Generate permissions2449 clusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{2450 {2451 ServiceAccountName: serviceAccountName,2452 Rules: []rbacv1.PolicyRule{2453 {2454 Verbs: []string{rbac.VerbAll},2455 APIGroups: []string{"cluster.com"},2456 Resources: []string{crdPlural},2457 },2458 },2459 },2460 }2461 // Create a new NamedInstallStrategy2462 namedStrategy := newNginxInstallStrategy(genName("dep-"), permissions, clusterPermissions)2463 // Create new CSVs2464 stableCSV := newCSV(stableCSVName, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, &namedStrategy)2465 defer func() {2466 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))2467 }()2468 // Create CatalogSource2469 mainCatalogSourceName := genName("nginx-catalog")2470 _, cleanupCatalogSource := createInternalCatalogSource(c, crc, mainCatalogSourceName, ns.GetName(), manifests, []apiextensions.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{stableCSV})2471 defer cleanupCatalogSource()2472 // Attempt to get CatalogSource2473 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogSourceName, ns.GetName(), catalogSourceRegistryPodSynced)2474 require.NoError(GinkgoT(), err)2475 subscriptionName := genName("sub-nginx-")2476 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogSourceName, packageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)2477 defer subscriptionCleanup()2478 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)2479 require.NoError(GinkgoT(), err)2480 require.NotNil(GinkgoT(), subscription)2481 installPlanName := subscription.Status.InstallPlanRef.Name2482 // Attempt to get InstallPlan2483 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseFailed, operatorsv1alpha1.InstallPlanPhaseComplete))2484 require.NoError(GinkgoT(), err)2485 require.NotEqual(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseFailed, fetchedInstallPlan.Status.Phase, "InstallPlan failed")2486 // Expect correct RBAC resources to be resolved and created2487 expectedSteps := map[registry.ResourceKey]struct{}{2488 {Name: crd.Name, Kind: "CustomResourceDefinition"}: {},2489 {Name: stableCSVName, Kind: "ClusterServiceVersion"}: {},2490 {Name: serviceAccountName, Kind: "ServiceAccount"}: {},2491 {Name: stableCSVName, Kind: "Role"}: {},2492 {Name: stableCSVName, Kind: "RoleBinding"}: {},2493 {Name: stableCSVName, Kind: "ClusterRole"}: {},2494 {Name: stableCSVName, Kind: "ClusterRoleBinding"}: {},2495 }2496 require.Equal(GinkgoT(), len(expectedSteps), len(fetchedInstallPlan.Status.Plan), "number of expected steps does not match installed")2497 for _, step := range fetchedInstallPlan.Status.Plan {2498 key := registry.ResourceKey{2499 Name: step.Resource.Name,2500 Kind: step.Resource.Kind,2501 }2502 for expected := range expectedSteps {2503 if expected == key {2504 delete(expectedSteps, expected)2505 } else if strings.HasPrefix(key.Name, expected.Name) && key.Kind == expected.Kind {2506 delete(expectedSteps, expected)2507 } else {2508 GinkgoT().Logf("%v, %v: %v && %v", key, expected, strings.HasPrefix(key.Name, expected.Name), key.Kind == expected.Kind)2509 }2510 }2511 // This operator was installed into a global operator group, so the roles should have been lifted to clusterroles2512 if step.Resource.Kind == "Role" {2513 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {2514 _, err = c.GetClusterRole(step.Resource.Name)2515 if err != nil {2516 if apierrors.IsNotFound(err) {2517 return false, nil2518 }2519 return false, err2520 }2521 return true, nil2522 })2523 require.NoError(GinkgoT(), err)2524 }2525 if step.Resource.Kind == "RoleBinding" {2526 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {2527 _, err = c.GetClusterRoleBinding(step.Resource.Name)2528 if err != nil {2529 if apierrors.IsNotFound(err) {2530 return false, nil2531 }2532 return false, err2533 }2534 return true, nil2535 })2536 require.NoError(GinkgoT(), err)2537 }2538 }2539 // Should have removed every matching step2540 require.Equal(GinkgoT(), 0, len(expectedSteps), "Actual resource steps do not match expected: %#v", expectedSteps)2541 // the test from here out verifies created RBAC is removed after CSV deletion2542 createdClusterRoles, err := c.KubernetesInterface().RbacV1().ClusterRoles().List(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})2543 createdClusterRoleNames := map[string]struct{}{}2544 for _, role := range createdClusterRoles.Items {2545 createdClusterRoleNames[role.GetName()] = struct{}{}2546 GinkgoT().Logf("Monitoring cluster role %v", role.GetName())2547 }2548 createdClusterRoleBindings, err := c.KubernetesInterface().RbacV1().ClusterRoleBindings().List(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})2549 createdClusterRoleBindingNames := map[string]struct{}{}2550 for _, binding := range createdClusterRoleBindings.Items {2551 createdClusterRoleBindingNames[binding.GetName()] = struct{}{}2552 GinkgoT().Logf("Monitoring cluster role binding %v", binding.GetName())2553 }2554 crWatcher, err := c.KubernetesInterface().RbacV1().ClusterRoles().Watch(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})2555 require.NoError(GinkgoT(), err)2556 crbWatcher, err := c.KubernetesInterface().RbacV1().ClusterRoleBindings().Watch(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", ownerutil.OwnerKey, stableCSVName)})2557 require.NoError(GinkgoT(), err)2558 done := make(chan struct{})2559 errExit := make(chan error)2560 go func() {2561 defer GinkgoRecover()2562 for {2563 select {2564 case evt, ok := <-crWatcher.ResultChan():2565 if !ok {2566 errExit <- errors.New("cr watch channel closed unexpectedly")2567 return2568 }2569 if evt.Type == watch.Deleted {2570 cr, ok := evt.Object.(*rbacv1.ClusterRole)2571 if !ok {2572 continue2573 }2574 delete(createdClusterRoleNames, cr.GetName())2575 if len(createdClusterRoleNames) == 0 && len(createdClusterRoleBindingNames) == 0 {2576 done <- struct{}{}2577 return2578 }2579 }2580 case evt, ok := <-crbWatcher.ResultChan():2581 if !ok {2582 errExit <- errors.New("crb watch channel closed unexpectedly")2583 return2584 }2585 if evt.Type == watch.Deleted {2586 crb, ok := evt.Object.(*rbacv1.ClusterRoleBinding)2587 if !ok {2588 continue2589 }2590 delete(createdClusterRoleBindingNames, crb.GetName())2591 if len(createdClusterRoleNames) == 0 && len(createdClusterRoleBindingNames) == 0 {2592 done <- struct{}{}2593 return2594 }2595 }2596 case <-time.After(pollDuration):2597 done <- struct{}{}2598 return2599 }2600 }2601 }()2602 GinkgoT().Logf("Deleting CSV '%v' in namespace %v", stableCSVName, ns.GetName())2603 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().ClusterServiceVersions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))2604 select {2605 case <-done:2606 break2607 case err := <-errExit:2608 GinkgoT().Fatal(err)2609 }2610 require.Emptyf(GinkgoT(), createdClusterRoleNames, "unexpected cluster role remain: %v", createdClusterRoleNames)2611 require.Emptyf(GinkgoT(), createdClusterRoleBindingNames, "unexpected cluster role binding remain: %v", createdClusterRoleBindingNames)2612 Eventually(func() error {2613 _, err := c.GetServiceAccount(ns.GetName(), serviceAccountName)2614 if err == nil {2615 return fmt.Errorf("The %v/%v ServiceAccount should have been deleted", ns.GetName(), serviceAccountName)2616 }2617 if !apierrors.IsNotFound(err) {2618 return err2619 }2620 return nil2621 }, timeout, interval).Should(BeNil())2622 })2623 It("CRD validation", func() {2624 // Tests if CRD validation works with the "minimum" property after being2625 // pulled from a CatalogSource's operator-registry.2626 crdPlural := genName("ins")2627 crdName := crdPlural + ".cluster.com"2628 var min float64 = 22629 var max float64 = 2562630 // Create CRD with offending property2631 crd := apiextensions.CustomResourceDefinition{2632 ObjectMeta: metav1.ObjectMeta{2633 Name: crdName,2634 },2635 Spec: apiextensions.CustomResourceDefinitionSpec{2636 Group: "cluster.com",2637 Versions: []apiextensions.CustomResourceDefinitionVersion{2638 {2639 Name: "v1alpha1",2640 Served: true,2641 Storage: true,2642 Schema: &apiextensions.CustomResourceValidation{2643 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{2644 Type: "object",2645 Properties: map[string]apiextensions.JSONSchemaProps{2646 "spec": {2647 Type: "object",2648 Description: "Spec of a test object.",2649 Properties: map[string]apiextensions.JSONSchemaProps{2650 "scalar": {2651 Type: "number",2652 Description: "Scalar value that should have a min and max.",2653 Minimum: &min,2654 Maximum: &max,2655 },2656 },2657 },2658 },2659 },2660 },2661 },2662 },2663 Names: apiextensions.CustomResourceDefinitionNames{2664 Plural: crdPlural,2665 Singular: crdPlural,2666 Kind: crdPlural,2667 ListKind: "list" + crdPlural,2668 },2669 Scope: apiextensions.NamespaceScoped,2670 },2671 }2672 // Defer CRD clean up2673 defer func() {2674 Eventually(func() error {2675 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{}))2676 }).Should(Succeed())2677 }()2678 // Create CSV2679 packageName := genName("nginx-")2680 stableChannel := "stable"2681 packageNameStable := packageName + "-" + stableChannel2682 csv := newCSV(packageNameStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, nil)2683 // Create PackageManifests2684 manifests := []registry.PackageManifest{2685 {2686 PackageName: packageName,2687 Channels: []registry.PackageChannel{2688 {Name: stableChannel, CurrentCSVName: packageNameStable},2689 },2690 DefaultChannelName: stableChannel,2691 },2692 }2693 // Create the CatalogSource2694 catalogSourceName := genName("mock-nginx-")2695 _, cleanupCatalogSource := createInternalCatalogSource(c, crc, catalogSourceName, ns.GetName(), manifests, []apiextensions.CustomResourceDefinition{crd}, []operatorsv1alpha1.ClusterServiceVersion{csv})2696 defer cleanupCatalogSource()2697 // Attempt to get the catalog source before creating install plan2698 _, err := fetchCatalogSourceOnStatus(crc, catalogSourceName, ns.GetName(), catalogSourceRegistryPodSynced)2699 require.NoError(GinkgoT(), err)2700 subscriptionName := genName("sub-nginx-")2701 cleanupSubscription := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, catalogSourceName, packageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)2702 defer cleanupSubscription()2703 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)2704 require.NoError(GinkgoT(), err)2705 require.NotNil(GinkgoT(), subscription)2706 installPlanName := subscription.Status.InstallPlanRef.Name2707 // Wait for InstallPlan to be status: Complete before checking resource presence2708 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete, operatorsv1alpha1.InstallPlanPhaseFailed))2709 require.NoError(GinkgoT(), err)2710 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)2711 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)2712 })2713 It("unpacks bundle image", func() {2714 ns, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{2715 ObjectMeta: metav1.ObjectMeta{2716 Name: genName("ns-"),2717 },2718 }, metav1.CreateOptions{})2719 require.NoError(GinkgoT(), err)2720 og := &operatorsv1.OperatorGroup{}2721 og.SetName("og")2722 _, err = crc.OperatorsV1().OperatorGroups(ns.GetName()).Create(context.Background(), og, metav1.CreateOptions{})2723 require.NoError(GinkgoT(), err)2724 deleteOpts := &metav1.DeleteOptions{}2725 defer func() {2726 require.NoError(GinkgoT(), c.KubernetesInterface().CoreV1().Namespaces().Delete(context.Background(), ns.GetName(), *deleteOpts))2727 }()2728 catsrc := &operatorsv1alpha1.CatalogSource{2729 ObjectMeta: metav1.ObjectMeta{2730 Name: genName("kiali-"),2731 Namespace: ns.GetName(),2732 Labels: map[string]string{"olm.catalogSource": "kaili-catalog"},2733 },2734 Spec: operatorsv1alpha1.CatalogSourceSpec{2735 Image: "quay.io/operator-framework/ci-index:latest",2736 SourceType: operatorsv1alpha1.SourceTypeGrpc,2737 },2738 }2739 catsrc, err = crc.OperatorsV1alpha1().CatalogSources(catsrc.GetNamespace()).Create(context.Background(), catsrc, metav1.CreateOptions{})2740 require.NoError(GinkgoT(), err)2741 defer func() {2742 Eventually(func() error {2743 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), catsrc))2744 }).Should(Succeed())2745 }()2746 // Wait for the CatalogSource to be ready2747 catsrc, err = fetchCatalogSourceOnStatus(crc, catsrc.GetName(), catsrc.GetNamespace(), catalogSourceRegistryPodSynced)2748 require.NoError(GinkgoT(), err)2749 // Generate a Subscription2750 subName := genName("kiali-")2751 cleanUpSubscriptionFn := createSubscriptionForCatalog(crc, catsrc.GetNamespace(), subName, catsrc.GetName(), "kiali", stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)2752 defer cleanUpSubscriptionFn()2753 sub, err := fetchSubscription(crc, catsrc.GetNamespace(), subName, subscriptionHasInstallPlanChecker)2754 require.NoError(GinkgoT(), err)2755 // Wait for the expected InstallPlan's execution to either fail or succeed2756 ipName := sub.Status.InstallPlanRef.Name2757 ip, err := waitForInstallPlan(crc, ipName, sub.GetNamespace(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseFailed, operatorsv1alpha1.InstallPlanPhaseComplete))2758 require.NoError(GinkgoT(), err)2759 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, ip.Status.Phase, "InstallPlan not complete")2760 // Ensure the InstallPlan contains the steps resolved from the bundle image2761 operatorName := "kiali-operator"2762 expectedSteps := map[registry.ResourceKey]struct{}{2763 {Name: operatorName, Kind: "ClusterServiceVersion"}: {},2764 {Name: "kialis.kiali.io", Kind: "CustomResourceDefinition"}: {},2765 {Name: "monitoringdashboards.monitoring.kiali.io", Kind: "CustomResourceDefinition"}: {},2766 {Name: operatorName, Kind: "ServiceAccount"}: {},2767 {Name: operatorName, Kind: "ClusterRole"}: {},2768 {Name: operatorName, Kind: "ClusterRoleBinding"}: {},2769 }2770 require.Lenf(GinkgoT(), ip.Status.Plan, len(expectedSteps), "number of expected steps does not match installed: %v", ip.Status.Plan)2771 for _, step := range ip.Status.Plan {2772 key := registry.ResourceKey{2773 Name: step.Resource.Name,2774 Kind: step.Resource.Kind,2775 }2776 for expected := range expectedSteps {2777 if strings.HasPrefix(key.Name, expected.Name) && key.Kind == expected.Kind {2778 delete(expectedSteps, expected)2779 }2780 }2781 }2782 require.Lenf(GinkgoT(), expectedSteps, 0, "Actual resource steps do not match expected: %#v", expectedSteps)2783 })2784 // This It spec verifies that, in cases where there are multiple options to fulfil a dependency2785 // across multiple catalogs, we only generate one installplan with one set of resolved resources.2786 //issue: https://github.com/operator-framework/operator-lifecycle-manager/issues/26332787 It("[FLAKE] consistent generation", func() {2788 // Configure catalogs:2789 // - one catalog with a package that has a dependency2790 // - several duplicate catalog with a package that satisfies the dependency2791 // Install the package from the main catalog2792 // Should see only 1 installplan created2793 // Should see the main CSV installed2794 log := func(s string) {2795 GinkgoT().Logf("%s: %s", time.Now().Format("15:04:05.9999"), s)2796 }2797 ns := &corev1.Namespace{}2798 ns.SetName(genName("ns-"))2799 // Create a namespace an OperatorGroup2800 ns, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{})2801 require.NoError(GinkgoT(), err)2802 deleteOpts := &metav1.DeleteOptions{}2803 defer func() {2804 require.NoError(GinkgoT(), c.KubernetesInterface().CoreV1().Namespaces().Delete(context.Background(), ns.GetName(), *deleteOpts))2805 }()2806 og := &operatorsv1.OperatorGroup{}2807 og.SetName("og")2808 _, err = crc.OperatorsV1().OperatorGroups(ns.GetName()).Create(context.Background(), og, metav1.CreateOptions{})2809 require.NoError(GinkgoT(), err)2810 mainPackageName := genName("nginx-")2811 dependentPackageName := genName("nginxdep-")2812 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)2813 dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName)2814 stableChannel := "stable"2815 dependentCRD := newCRD(genName("ins-"))2816 mainCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, nil)2817 dependentCSV := newCSV(dependentPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, nil)2818 defer func() {2819 require.NoError(GinkgoT(), crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{}))2820 }()2821 dependentCatalogName := genName("mock-ocs-dependent-")2822 mainCatalogName := genName("mock-ocs-main-")2823 // Create separate manifests for each CatalogSource2824 mainManifests := []registry.PackageManifest{2825 {2826 PackageName: mainPackageName,2827 Channels: []registry.PackageChannel{2828 {Name: stableChannel, CurrentCSVName: mainPackageStable},2829 },2830 DefaultChannelName: stableChannel,2831 },2832 }2833 dependentManifests := []registry.PackageManifest{2834 {2835 PackageName: dependentPackageName,2836 Channels: []registry.PackageChannel{2837 {Name: stableChannel, CurrentCSVName: dependentPackageStable},2838 },2839 DefaultChannelName: stableChannel,2840 },2841 }2842 // Defer CRD clean up2843 defer func() {2844 Eventually(func() error {2845 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{}))2846 }).Should(Succeed())2847 }()2848 // Create the dependent catalog source2849 _, cleanupDependentCatalogSource := createInternalCatalogSource(c, crc, dependentCatalogName, ns.GetName(), dependentManifests, []apiextensions.CustomResourceDefinition{dependentCRD}, []operatorsv1alpha1.ClusterServiceVersion{dependentCSV})2850 defer cleanupDependentCatalogSource()2851 // Attempt to get the catalog source before creating install plan2852 dependentCatalogSource, err := fetchCatalogSourceOnStatus(crc, dependentCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)2853 require.NoError(GinkgoT(), err)2854 // Create the alt dependent catalog sources2855 var wg sync.WaitGroup2856 for i := 0; i < 4; i++ { // Creating more increases the odds that the race condition will be triggered2857 wg.Add(1)2858 go func(i int) {2859 defer GinkgoRecover()2860 // Create a CatalogSource pointing to the grpc pod2861 addressSource := &operatorsv1alpha1.CatalogSource{2862 TypeMeta: metav1.TypeMeta{2863 Kind: operatorsv1alpha1.CatalogSourceKind,2864 APIVersion: operatorsv1alpha1.CatalogSourceCRDAPIVersion,2865 },2866 Spec: operatorsv1alpha1.CatalogSourceSpec{2867 SourceType: operatorsv1alpha1.SourceTypeGrpc,2868 Address: dependentCatalogSource.Status.RegistryServiceStatus.Address(),2869 },2870 }2871 addressSource.SetName(genName("alt-dep-"))2872 _, err := crc.OperatorsV1alpha1().CatalogSources(ns.GetName()).Create(context.Background(), addressSource, metav1.CreateOptions{})2873 require.NoError(GinkgoT(), err)2874 // Attempt to get the catalog source before creating install plan2875 _, err = fetchCatalogSourceOnStatus(crc, addressSource.GetName(), ns.GetName(), catalogSourceRegistryPodSynced)2876 require.NoError(GinkgoT(), err)2877 wg.Done()2878 }(i)2879 }2880 wg.Wait()2881 // Create the main catalog source2882 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, nil, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})2883 defer cleanupMainCatalogSource()2884 // Attempt to get the catalog source before creating install plan2885 _, err = fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)2886 require.NoError(GinkgoT(), err)2887 subscriptionName := genName("sub-nginx-")2888 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)2889 defer subscriptionCleanup()2890 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)2891 require.NoError(GinkgoT(), err)2892 require.NotNil(GinkgoT(), subscription)2893 installPlanName := subscription.Status.InstallPlanRef.Name2894 // Wait for InstallPlan to be status: Complete before checking resource presence2895 fetchedInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))2896 require.NoError(GinkgoT(), err)2897 log(fmt.Sprintf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase))2898 require.Equal(GinkgoT(), operatorsv1alpha1.InstallPlanPhaseComplete, fetchedInstallPlan.Status.Phase)2899 // Verify CSV is created2900 _, err = awaitCSV(crc, ns.GetName(), mainCSV.GetName(), csvSucceededChecker)2901 require.NoError(GinkgoT(), err)2902 // Make sure to clean up the installed CRD2903 defer func() {2904 require.NoError(GinkgoT(), c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), *deleteOpts))2905 }()2906 // ensure there is only one installplan2907 ips, err := crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).List(context.Background(), metav1.ListOptions{})2908 require.NoError(GinkgoT(), err)2909 require.Equal(GinkgoT(), 1, len(ips.Items), "If this test fails it should be taken seriously and not treated as a flake. \n%v", ips.Items)2910 })2911 When("an InstallPlan is created with no valid OperatorGroup present", func() {2912 var (2913 installPlanName string2914 ns *corev1.Namespace2915 )2916 BeforeEach(func() {2917 ns = &corev1.Namespace{}2918 ns.SetName(genName("ns-"))2919 // Create a namespace2920 Eventually(func() error {2921 return ctx.Ctx().Client().Create(context.Background(), ns)2922 }, timeout, interval).Should(Succeed(), "could not create Namespace")2923 // Create InstallPlan2924 installPlanName = "ip"2925 ip := newInstallPlanWithDummySteps(installPlanName, ns.GetName(), operatorsv1alpha1.InstallPlanPhaseInstalling)2926 outIP, err := crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).Create(context.Background(), ip, metav1.CreateOptions{})2927 Expect(err).NotTo(HaveOccurred())2928 Expect(outIP).NotTo(BeNil())2929 // The status gets ignored on create so we need to update it else the InstallPlan sync ignores2930 // InstallPlans without any steps or bundle lookups2931 outIP.Status = ip.Status2932 _, err = crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).UpdateStatus(context.Background(), outIP, metav1.UpdateOptions{})2933 Expect(err).NotTo(HaveOccurred())2934 })2935 AfterEach(func() {2936 err := crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).Delete(context.Background(), installPlanName, metav1.DeleteOptions{})2937 Expect(err).NotTo(HaveOccurred())2938 err = c.KubernetesInterface().CoreV1().Namespaces().Delete(context.Background(), ns.GetName(), metav1.DeleteOptions{})2939 Expect(err).ToNot(HaveOccurred())2940 })2941 // issue: https://github.com/operator-framework/operator-lifecycle-manager/issues/26362942 It("[FLAKE] should clear up the condition in the InstallPlan status that contains an error message when a valid OperatorGroup is created", func() {2943 // first wait for a condition with a message exists2944 cond := operatorsv1alpha1.InstallPlanCondition{Type: operatorsv1alpha1.InstallPlanInstalled, Status: corev1.ConditionFalse, Reason: operatorsv1alpha1.InstallPlanReasonInstallCheckFailed,2945 Message: "no operator group found that is managing this namespace"}2946 Eventually(func() bool {2947 fetchedInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseInstalling))2948 if err != nil || fetchedInstallPlan == nil {2949 return false2950 }2951 if fetchedInstallPlan.Status.Phase != operatorsv1alpha1.InstallPlanPhaseInstalling {2952 return false2953 }2954 return hasCondition(fetchedInstallPlan, cond)2955 }, 5*time.Minute, interval).Should(BeTrue())2956 // Create an operatorgroup for the same namespace2957 og := &operatorsv1.OperatorGroup{2958 ObjectMeta: metav1.ObjectMeta{2959 Name: "og",2960 Namespace: ns.GetName(),2961 },2962 Spec: operatorsv1.OperatorGroupSpec{2963 TargetNamespaces: []string{ns.GetName()},2964 },2965 }2966 Eventually(func() error {2967 return ctx.Ctx().Client().Create(context.Background(), og)2968 }, timeout, interval).Should(Succeed(), "could not create OperatorGroup")2969 // Wait for the OperatorGroup to be synced2970 Eventually(2971 func() ([]string, error) {2972 err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(og), og)2973 ctx.Ctx().Logf("Waiting for OperatorGroup(%v) to be synced with status.namespaces: %v", og.Name, og.Status.Namespaces)2974 return og.Status.Namespaces, err2975 },2976 1*time.Minute,2977 interval,2978 ).Should(ContainElement(ns.GetName()))2979 // check that the condition has been cleared up2980 Eventually(func() (bool, error) {2981 fetchedInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseInstalling))2982 if err != nil {2983 return false, err2984 }2985 if fetchedInstallPlan == nil {2986 return false, err2987 }2988 if hasCondition(fetchedInstallPlan, cond) {2989 return false, nil2990 }2991 return true, nil2992 }).Should(BeTrue())2993 })2994 })2995 When("waiting on the bundle unpacking job", func() {2996 var (2997 ns *corev1.Namespace2998 catsrcName string2999 ip *operatorsv1alpha1.InstallPlan3000 )3001 BeforeEach(func() {3002 ns = &corev1.Namespace{}3003 ns.SetName(genName("ns-"))3004 Eventually(func() error {3005 return ctx.Ctx().Client().Create(context.Background(), ns)3006 }, timeout, interval).Should(Succeed(), "could not create Namespace")3007 // Create a dummy CatalogSource to bypass the bundle unpacker's check for a CatalogSource3008 catsrc := &operatorsv1alpha1.CatalogSource{3009 ObjectMeta: metav1.ObjectMeta{3010 Name: genName("dummy-catsrc-"),3011 Namespace: ns.GetName(),3012 },3013 Spec: operatorsv1alpha1.CatalogSourceSpec{3014 Image: "localhost:0/not/exist:catsrc",3015 SourceType: operatorsv1alpha1.SourceTypeGrpc,3016 },3017 }3018 Eventually(func() error {3019 return ctx.Ctx().Client().Create(context.Background(), catsrc)3020 }, timeout, interval).Should(Succeed(), "could not create CatalogSource")3021 catsrcName = catsrc.GetName()3022 // Create the OperatorGroup3023 og := &operatorsv1.OperatorGroup{3024 ObjectMeta: metav1.ObjectMeta{3025 Name: "og",3026 Namespace: ns.GetName(),3027 },3028 Spec: operatorsv1.OperatorGroupSpec{3029 TargetNamespaces: []string{ns.GetName()},3030 },3031 }3032 Eventually(func() error {3033 return ctx.Ctx().Client().Create(context.Background(), og)3034 }, timeout, interval).Should(Succeed(), "could not create OperatorGroup")3035 // Wait for the OperatorGroup to be synced so the InstallPlan doesn't have to be resynced due to an invalid OperatorGroup3036 Eventually(3037 func() ([]string, error) {3038 err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(og), og)3039 ctx.Ctx().Logf("Waiting for OperatorGroup(%v) to be synced with status.namespaces: %v", og.Name, og.Status.Namespaces)3040 return og.Status.Namespaces, err3041 },3042 1*time.Minute,3043 interval,3044 ).Should(ContainElement(ns.GetName()))3045 ip = &operatorsv1alpha1.InstallPlan{3046 ObjectMeta: metav1.ObjectMeta{3047 Name: "ip",3048 Namespace: ns.GetName(),3049 },3050 Spec: operatorsv1alpha1.InstallPlanSpec{3051 ClusterServiceVersionNames: []string{"foobar"},3052 Approval: operatorsv1alpha1.ApprovalAutomatic,3053 Approved: true,3054 },3055 }3056 })3057 AfterEach(func() {3058 Eventually(func() error {3059 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), ns))3060 }, timeout, interval).Should(Succeed(), "could not delete Namespace")3061 Eventually(func() error {3062 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), ip))3063 }, timeout, interval).Should(Succeed(), "could not delete Namespace")3064 })3065 It("should show an error on the bundlelookup condition for a non-existent bundle image", func() {3066 // We wait for some time over the bundle unpack timeout (i.e ActiveDeadlineSeconds) so that the Job can eventually fail3067 // Since the default --bundle-unpack-timeout=10m, we override with a shorter timeout via the3068 // unpack timeout annotation on the InstallPlan3069 annotations := make(map[string]string)3070 annotations[bundle.BundleUnpackTimeoutAnnotationKey] = "1m"3071 ip.SetAnnotations(annotations)3072 Eventually(func() error {3073 return ctx.Ctx().Client().Create(context.Background(), ip)3074 }, timeout, interval).Should(Succeed(), "could not create InstallPlan")3075 now := metav1.Now()3076 // Create an InstallPlan status.bundleLookups.Path specified for a non-existent bundle image3077 ip.Status = operatorsv1alpha1.InstallPlanStatus{3078 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,3079 CatalogSources: []string{},3080 BundleLookups: []operatorsv1alpha1.BundleLookup{3081 {3082 Path: "localhost:0/not/exist:v0.0.1",3083 Identifier: "foobar.v0.0.1",3084 CatalogSourceRef: &corev1.ObjectReference{3085 Namespace: ns.GetName(),3086 Name: catsrcName,3087 },3088 Conditions: []operatorsv1alpha1.BundleLookupCondition{3089 {3090 Type: operatorsv1alpha1.BundleLookupPending,3091 Status: corev1.ConditionTrue,3092 Reason: "JobIncomplete",3093 Message: "unpack job not completed",3094 LastTransitionTime: &now,3095 },3096 },3097 },3098 },3099 }3100 // The status gets ignored on create so we need to update it else the InstallPlan sync ignores3101 // InstallPlans without any steps or bundle lookups3102 Eventually(func() error {3103 return ctx.Ctx().Client().Status().Update(context.Background(), ip)3104 }, timeout, interval).Should(Succeed(), "could not update InstallPlan status")3105 // The InstallPlan's status.bundleLookup.conditions should have a BundleLookupPending condition3106 // with the container status from unpack pod that mentions an image pull failure for the non-existent3107 // image, e.g ErrImagePull or ImagePullBackOff3108 Eventually(3109 func() (string, error) {3110 err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(ip), ip)3111 if err != nil {3112 return "", err3113 }3114 for _, bl := range ip.Status.BundleLookups {3115 for _, cond := range bl.Conditions {3116 if cond.Type != operatorsv1alpha1.BundleLookupPending {3117 continue3118 }3119 return cond.Message, nil3120 }3121 }3122 return "", fmt.Errorf("%s condition not found", operatorsv1alpha1.BundleLookupPending)3123 },3124 1*time.Minute,3125 interval,3126 ).Should(ContainSubstring("ErrImagePull"))3127 waitFor := 1*time.Minute + 30*time.Second3128 // The InstallPlan should eventually fail due to the ActiveDeadlineSeconds limit3129 Eventually(3130 func() (*operatorsv1alpha1.InstallPlan, error) {3131 err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(ip), ip)3132 return ip, err3133 },3134 waitFor,3135 interval,3136 ).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseFailed))3137 })3138 It("should timeout and fail the InstallPlan for an invalid bundle image", func() {3139 Eventually(func() error {3140 return ctx.Ctx().Client().Create(context.Background(), ip)3141 }, timeout, interval).Should(Succeed(), "could not create InstallPlan")3142 // The status gets ignored on create so we need to update it else the InstallPlan sync ignores3143 // InstallPlans without any steps or bundle lookups3144 // Create an InstallPlan status.bundleLookups.Path specified for an invalid bundle image3145 now := metav1.Now()3146 ip.Status = operatorsv1alpha1.InstallPlanStatus{3147 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,3148 CatalogSources: []string{},3149 BundleLookups: []operatorsv1alpha1.BundleLookup{3150 {3151 Path: "alpine:3.13",3152 Identifier: "foobar.v0.0.1",3153 CatalogSourceRef: &corev1.ObjectReference{3154 Namespace: ns.GetName(),3155 Name: catsrcName,3156 },3157 Conditions: []operatorsv1alpha1.BundleLookupCondition{3158 {3159 Type: operatorsv1alpha1.BundleLookupPending,3160 Status: corev1.ConditionTrue,3161 Reason: "JobIncomplete",3162 Message: "unpack job not completed",3163 LastTransitionTime: &now,3164 },3165 },3166 },3167 },3168 }3169 Eventually(func() error {3170 return ctx.Ctx().Client().Status().Update(context.Background(), ip)3171 }, timeout, interval).Should(Succeed(), "could not update InstallPlan status")3172 // The InstallPlan should fail after the unpack pod keeps failing and exceeds the job's3173 // BackoffLimit(set to 3), which for 4 failures is an exponential backoff (10s + 20s + 40s + 80s)= 2m30s3174 // so we wait a little over that.3175 Eventually(3176 func() (*operatorsv1alpha1.InstallPlan, error) {3177 err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(ip), ip)3178 return ip, err3179 },3180 5*time.Minute,3181 interval,3182 ).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseFailed))3183 })3184 })3185 It("compresses installplan step resource manifests to configmap references", func() {3186 // Test ensures that all steps for index-based catalogs are references to configmaps. This avoids the problem3187 // of installplans growing beyond the etcd size limit when manifests are written to the ip status.3188 ns, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{3189 ObjectMeta: metav1.ObjectMeta{3190 Name: genName("ns-"),3191 },3192 }, metav1.CreateOptions{})3193 Expect(err).ToNot(HaveOccurred())3194 og := &operatorsv1.OperatorGroup{}3195 og.SetName("og")3196 _, err = crc.OperatorsV1().OperatorGroups(ns.GetName()).Create(context.Background(), og, metav1.CreateOptions{})3197 Expect(err).ToNot(HaveOccurred())3198 deleteOpts := &metav1.DeleteOptions{}3199 defer c.KubernetesInterface().CoreV1().Namespaces().Delete(context.Background(), ns.GetName(), *deleteOpts)3200 catsrc := &operatorsv1alpha1.CatalogSource{3201 ObjectMeta: metav1.ObjectMeta{3202 Name: genName("kiali-"),3203 Namespace: ns.GetName(),3204 Labels: map[string]string{"olm.catalogSource": "kaili-catalog"},3205 },3206 Spec: operatorsv1alpha1.CatalogSourceSpec{3207 Image: "quay.io/operator-framework/ci-index:latest",3208 SourceType: operatorsv1alpha1.SourceTypeGrpc,3209 },3210 }3211 catsrc, err = crc.OperatorsV1alpha1().CatalogSources(catsrc.GetNamespace()).Create(context.Background(), catsrc, metav1.CreateOptions{})3212 Expect(err).ToNot(HaveOccurred())3213 // Wait for the CatalogSource to be ready3214 catsrc, err = fetchCatalogSourceOnStatus(crc, catsrc.GetName(), catsrc.GetNamespace(), catalogSourceRegistryPodSynced)3215 Expect(err).ToNot(HaveOccurred())3216 // Generate a Subscription3217 subName := genName("kiali-")3218 cleanUpSubscriptionFn := createSubscriptionForCatalog(crc, catsrc.GetNamespace(), subName, catsrc.GetName(), "kiali", stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)3219 defer cleanUpSubscriptionFn()3220 sub, err := fetchSubscription(crc, catsrc.GetNamespace(), subName, subscriptionHasInstallPlanChecker)3221 Expect(err).ToNot(HaveOccurred())3222 // Wait for the expected InstallPlan's execution to either fail or succeed3223 ipName := sub.Status.InstallPlanRef.Name3224 ip, err := waitForInstallPlan(crc, ipName, sub.GetNamespace(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseFailed, operatorsv1alpha1.InstallPlanPhaseComplete))3225 Expect(err).ToNot(HaveOccurred())3226 Expect(operatorsv1alpha1.InstallPlanPhaseComplete).To(Equal(ip.Status.Phase), "InstallPlan not complete")3227 // Ensure the InstallPlan contains the steps resolved from the bundle image3228 operatorName := "kiali-operator"3229 expectedSteps := map[registry.ResourceKey]struct{}{3230 {Name: operatorName, Kind: "ClusterServiceVersion"}: {},3231 {Name: "kialis.kiali.io", Kind: "CustomResourceDefinition"}: {},3232 {Name: "monitoringdashboards.monitoring.kiali.io", Kind: "CustomResourceDefinition"}: {},3233 {Name: operatorName, Kind: "ServiceAccount"}: {},3234 {Name: operatorName, Kind: "ClusterRole"}: {},3235 {Name: operatorName, Kind: "ClusterRoleBinding"}: {},3236 }3237 Expect(ip.Status.Plan).To(HaveLen(len(expectedSteps)), "number of expected steps does not match installed: %v", ip.Status.Plan)3238 for _, step := range ip.Status.Plan {3239 key := registry.ResourceKey{3240 Name: step.Resource.Name,3241 Kind: step.Resource.Kind,3242 }3243 for expected := range expectedSteps {3244 if strings.HasPrefix(key.Name, expected.Name) && key.Kind == expected.Kind {3245 delete(expectedSteps, expected)3246 }3247 }3248 }3249 Expect(expectedSteps).To(HaveLen(0), "Actual resource steps do not match expected: %#v", expectedSteps)3250 // Ensure that all the steps have a configmap based reference3251 for _, step := range ip.Status.Plan {3252 manifest := step.Resource.Manifest3253 var ref catalog.UnpackedBundleReference3254 err := json.Unmarshal([]byte(manifest), &ref)3255 Expect(err).ToNot(HaveOccurred())3256 Expect(ref.Kind).To(Equal("ConfigMap"))3257 }3258 })3259 It("limits installed resources if the scoped serviceaccount has no permissions", func() {3260 By("creating a scoped serviceaccount specified in the operatorgroup")3261 ns, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{3262 ObjectMeta: metav1.ObjectMeta{3263 Name: genName("ns-"),3264 },3265 }, metav1.CreateOptions{})3266 Expect(err).ToNot(HaveOccurred())3267 defer c.KubernetesInterface().CoreV1().Namespaces().Delete(context.Background(), ns.GetName(), metav1.DeleteOptions{})3268 // create SA3269 sa := &corev1.ServiceAccount{3270 ObjectMeta: metav1.ObjectMeta{3271 Name: genName("sa-"),3272 Namespace: ns.GetName(),3273 },3274 }3275 _, err = c.KubernetesInterface().CoreV1().ServiceAccounts(ns.GetName()).Create(context.Background(), sa, metav1.CreateOptions{})3276 Expect(err).ToNot(HaveOccurred())3277 // Create token secret for the serviceaccount3278 _, cleanupSE := newTokenSecret(c, ns.GetName(), sa.GetName())3279 defer cleanupSE()3280 // role has no explicit permissions3281 role := &rbacv1.ClusterRole{3282 ObjectMeta: metav1.ObjectMeta{3283 Name: genName("role-"),3284 },3285 Rules: []rbacv1.PolicyRule{},3286 }3287 // bind role to SA3288 rb := &rbacv1.ClusterRoleBinding{3289 ObjectMeta: metav1.ObjectMeta{3290 Name: genName("rb-"),3291 },3292 RoleRef: rbacv1.RoleRef{3293 Name: role.GetName(),3294 Kind: "ClusterRole",3295 APIGroup: "rbac.authorization.k8s.io",3296 },3297 Subjects: []rbacv1.Subject{3298 {3299 Kind: "ServiceAccount",3300 Name: sa.GetName(),3301 APIGroup: "",3302 Namespace: sa.GetNamespace(),3303 },3304 },3305 }3306 _, err = c.KubernetesInterface().RbacV1().ClusterRoleBindings().Create(context.Background(), rb, metav1.CreateOptions{})3307 Expect(err).ToNot(HaveOccurred())3308 defer c.KubernetesInterface().RbacV1().ClusterRoles().Delete(context.Background(), role.GetName(), metav1.DeleteOptions{})3309 // create operator group referencing the SA3310 og := &operatorsv1.OperatorGroup{3311 ObjectMeta: metav1.ObjectMeta{3312 Name: genName("og-"),3313 Namespace: ns.GetName(),3314 },3315 Spec: operatorsv1.OperatorGroupSpec{3316 ServiceAccountName: sa.GetName(),3317 },3318 }3319 _, err = crc.OperatorsV1().OperatorGroups(ns.GetName()).Create(context.Background(), og, metav1.CreateOptions{})3320 Expect(err).ToNot(HaveOccurred())3321 // Wait for the OperatorGroup to be synced and have a status.ServiceAccountRef3322 // before moving on. Otherwise the catalog operator treats it as an invalid OperatorGroup3323 // and the InstallPlan is resynced3324 Eventually(func() (*corev1.ObjectReference, error) {3325 outOG, err := crc.OperatorsV1().OperatorGroups(ns.GetName()).Get(context.Background(), og.Name, metav1.GetOptions{})3326 if err != nil {3327 return nil, err3328 }3329 ctx.Ctx().Logf("[DEBUG] Operator Group Status: %+v\n", outOG.Status)3330 return outOG.Status.ServiceAccountRef, nil3331 }).ShouldNot(BeNil())3332 crd := apiextensionsv1.CustomResourceDefinition{3333 ObjectMeta: metav1.ObjectMeta{3334 Name: "ins" + ".cluster.com",3335 },3336 TypeMeta: metav1.TypeMeta{3337 Kind: "CustomResourceDefinition",3338 APIVersion: "v1",3339 },3340 Spec: apiextensionsv1.CustomResourceDefinitionSpec{3341 Group: "cluster.com",3342 Names: apiextensionsv1.CustomResourceDefinitionNames{3343 Plural: "ins",3344 Singular: "ins",3345 Kind: "ins",3346 ListKind: "ins" + "list",3347 },3348 Scope: apiextensionsv1.NamespaceScoped,3349 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{3350 {3351 Name: "v1alpha1",3352 Served: true,3353 Storage: true,3354 Schema: &apiextensionsv1.CustomResourceValidation{3355 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{3356 Type: "object",3357 Description: "my crd schema",3358 },3359 },3360 },3361 },3362 },3363 }3364 // Defer CRD clean up3365 defer func() {3366 Eventually(func() error {3367 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{}))3368 }).Should(Succeed())3369 }()3370 scheme := runtime.NewScheme()3371 Expect(apiextensionsv1.AddToScheme(scheme)).To(Succeed())3372 var crdManifest bytes.Buffer3373 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(&crd, &crdManifest)).To(Succeed())3374 By("using the OLM client to create the CRD")3375 plan := &operatorsv1alpha1.InstallPlan{3376 ObjectMeta: metav1.ObjectMeta{3377 Namespace: ns.GetName(),3378 Name: genName("ip-"),3379 },3380 Spec: operatorsv1alpha1.InstallPlanSpec{3381 Approval: operatorsv1alpha1.ApprovalAutomatic,3382 Approved: true,3383 ClusterServiceVersionNames: []string{},3384 },3385 }3386 Expect(ctx.Ctx().Client().Create(context.Background(), plan)).To(Succeed())3387 plan.Status = operatorsv1alpha1.InstallPlanStatus{3388 AttenuatedServiceAccountRef: &corev1.ObjectReference{3389 Name: sa.GetName(),3390 Namespace: sa.GetNamespace(),3391 Kind: "ServiceAccount",3392 },3393 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,3394 CatalogSources: []string{},3395 Plan: []*operatorsv1alpha1.Step{3396 {3397 Status: operatorsv1alpha1.StepStatusUnknown,3398 Resource: operatorsv1alpha1.StepResource{3399 Name: crd.GetName(),3400 Version: "v1",3401 Kind: "CustomResourceDefinition",3402 Manifest: crdManifest.String(),3403 },3404 },3405 },3406 }3407 Expect(ctx.Ctx().Client().Status().Update(context.Background(), plan)).To(Succeed())3408 key := client.ObjectKeyFromObject(plan)3409 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {3410 return plan, ctx.Ctx().Client().Get(context.Background(), key, plan)3411 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseComplete))3412 // delete installplan, then create one with an additional resource that the SA does not have permissions to create3413 // expect installplan to fail3414 By("failing to install resources that are not explicitly allowed in the SA")3415 err = crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).Delete(context.Background(), plan.GetName(), metav1.DeleteOptions{})3416 Expect(err).ToNot(HaveOccurred())3417 service := &corev1.Service{3418 TypeMeta: metav1.TypeMeta{3419 Kind: "Service",3420 APIVersion: "v1",3421 },3422 ObjectMeta: metav1.ObjectMeta{3423 Namespace: ns.GetName(),3424 Name: "test-service",3425 },3426 Spec: corev1.ServiceSpec{3427 Type: corev1.ServiceTypeClusterIP,3428 Ports: []corev1.ServicePort{3429 {3430 Port: 12345,3431 },3432 },3433 },3434 }3435 Expect(corev1.AddToScheme(scheme)).To(Succeed())3436 var manifest bytes.Buffer3437 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(service, &manifest)).To(Succeed())3438 newPlan := &operatorsv1alpha1.InstallPlan{3439 ObjectMeta: metav1.ObjectMeta{3440 Namespace: ns.GetName(),3441 Name: genName("ip-"),3442 },3443 Spec: operatorsv1alpha1.InstallPlanSpec{3444 Approval: operatorsv1alpha1.ApprovalAutomatic,3445 Approved: true,3446 ClusterServiceVersionNames: []string{},3447 },3448 }3449 Expect(ctx.Ctx().Client().Create(context.Background(), newPlan)).To(Succeed())3450 newPlan.Status = operatorsv1alpha1.InstallPlanStatus{3451 StartTime: &metav1.Time{Time: time.Unix(0, 0)}, // disable retries3452 AttenuatedServiceAccountRef: &corev1.ObjectReference{3453 Name: sa.GetName(),3454 Namespace: sa.GetNamespace(),3455 Kind: "ServiceAccount",3456 },3457 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,3458 CatalogSources: []string{},3459 Plan: []*operatorsv1alpha1.Step{3460 {3461 Status: operatorsv1alpha1.StepStatusUnknown,3462 Resource: operatorsv1alpha1.StepResource{3463 Name: service.Name,3464 Version: "v1",3465 Kind: "Service",3466 Manifest: manifest.String(),3467 },3468 },3469 },3470 }3471 Expect(ctx.Ctx().Client().Status().Update(context.Background(), newPlan)).To(Succeed())3472 newKey := client.ObjectKeyFromObject(newPlan)3473 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {3474 return newPlan, ctx.Ctx().Client().Get(context.Background(), newKey, newPlan)3475 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseFailed))3476 Expect(client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &crd))).To(Succeed())3477 Eventually(func() error {3478 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), ns))3479 }, timeout, interval).Should(Succeed(), "could not delete Namespace")3480 })3481 It("uses the correct client when installing resources from an installplan", func() {3482 By("creating a scoped serviceaccount specifified in the operatorgroup")3483 ns, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{3484 ObjectMeta: metav1.ObjectMeta{3485 Name: genName("ns-"),3486 },3487 }, metav1.CreateOptions{})3488 Expect(err).ToNot(HaveOccurred())3489 defer c.KubernetesInterface().CoreV1().Namespaces().Delete(context.Background(), ns.GetName(), metav1.DeleteOptions{})3490 // create SA3491 sa := &corev1.ServiceAccount{3492 ObjectMeta: metav1.ObjectMeta{3493 Name: genName("sa-"),3494 Namespace: ns.GetName(),3495 },3496 }3497 _, err = c.KubernetesInterface().CoreV1().ServiceAccounts(ns.GetName()).Create(context.Background(), sa, metav1.CreateOptions{})3498 Expect(err).ToNot(HaveOccurred())3499 // Create token secret for the serviceaccount3500 _, cleanupSE := newTokenSecret(c, ns.GetName(), sa.GetName())3501 defer cleanupSE()3502 // see https://github.com/operator-framework/operator-lifecycle-manager/blob/master/doc/design/scoped-operator-install.md3503 role := &rbacv1.ClusterRole{3504 ObjectMeta: metav1.ObjectMeta{3505 Name: genName("role-"),3506 },3507 Rules: []rbacv1.PolicyRule{3508 {3509 APIGroups: []string{"operators.coreos.com"},3510 Resources: []string{"subscriptions", "clusterserviceversions"},3511 Verbs: []string{"get", "create", "update", "patch"},3512 },3513 {3514 APIGroups: []string{""},3515 Resources: []string{"services", "serviceaccounts", "configmaps", "endpoints", "events", "persistentvolumeclaims", "pods"},3516 Verbs: []string{"create", "delete", "get", "list", "update", "patch", "watch"},3517 },3518 {3519 APIGroups: []string{"apps"},3520 Resources: []string{"deployments", "replicasets", "statefulsets"},3521 Verbs: []string{"list", "watch", "get", "create", "update", "patch", "delete"},3522 },3523 {3524 // ability to get and list CRDs, but not create CRDs3525 APIGroups: []string{"apiextensions.k8s.io"},3526 Resources: []string{"customresourcedefinitions"},3527 Verbs: []string{"get", "list", "watch"},3528 },3529 },3530 }3531 _, err = c.KubernetesInterface().RbacV1().ClusterRoles().Create(context.Background(), role, metav1.CreateOptions{})3532 Expect(err).ToNot(HaveOccurred())3533 // bind role to SA3534 rb := &rbacv1.ClusterRoleBinding{3535 ObjectMeta: metav1.ObjectMeta{3536 Name: genName("rb-"),3537 },3538 RoleRef: rbacv1.RoleRef{3539 Name: role.GetName(),3540 Kind: "ClusterRole",3541 APIGroup: "rbac.authorization.k8s.io",3542 },3543 Subjects: []rbacv1.Subject{3544 {3545 Kind: "ServiceAccount",3546 Name: sa.GetName(),3547 APIGroup: "",3548 Namespace: sa.GetNamespace(),3549 },3550 },3551 }3552 _, err = c.KubernetesInterface().RbacV1().ClusterRoleBindings().Create(context.Background(), rb, metav1.CreateOptions{})3553 Expect(err).ToNot(HaveOccurred())3554 defer c.KubernetesInterface().RbacV1().ClusterRoles().Delete(context.Background(), role.GetName(), metav1.DeleteOptions{})3555 // create operator group referencing the SA3556 og := &operatorsv1.OperatorGroup{3557 ObjectMeta: metav1.ObjectMeta{3558 Name: genName("og-"),3559 Namespace: ns.GetName(),3560 },3561 Spec: operatorsv1.OperatorGroupSpec{3562 ServiceAccountName: sa.GetName(),3563 },3564 }3565 _, err = crc.OperatorsV1().OperatorGroups(ns.GetName()).Create(context.Background(), og, metav1.CreateOptions{})3566 Expect(err).ToNot(HaveOccurred())3567 // Wait for the OperatorGroup to be synced and have a status.ServiceAccountRef3568 // before moving on. Otherwise the catalog operator treats it as an invalid OperatorGroup3569 // and the InstallPlan is resynced3570 Eventually(func() (*corev1.ObjectReference, error) {3571 outOG, err := crc.OperatorsV1().OperatorGroups(ns.GetName()).Get(context.Background(), og.Name, metav1.GetOptions{})3572 if err != nil {3573 return nil, err3574 }3575 ctx.Ctx().Logf("[DEBUG] Operator Group Status: %+v\n", outOG.Status)3576 return outOG.Status.ServiceAccountRef, nil3577 }).ShouldNot(BeNil())3578 By("using the OLM client to install CRDs from the installplan and the scoped client for other resources")3579 crd := apiextensionsv1.CustomResourceDefinition{3580 ObjectMeta: metav1.ObjectMeta{3581 Name: "ins" + ".cluster.com",3582 },3583 TypeMeta: metav1.TypeMeta{3584 Kind: "CustomResourceDefinition",3585 APIVersion: "v1",3586 },3587 Spec: apiextensionsv1.CustomResourceDefinitionSpec{3588 Group: "cluster.com",3589 Names: apiextensionsv1.CustomResourceDefinitionNames{3590 Plural: "ins",3591 Singular: "ins",3592 Kind: "ins",3593 ListKind: "ins" + "list",3594 },3595 Scope: apiextensionsv1.NamespaceScoped,3596 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{3597 {3598 Name: "v1alpha1",3599 Served: true,3600 Storage: true,3601 Schema: &apiextensionsv1.CustomResourceValidation{3602 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{3603 Type: "object",3604 Description: "my crd schema",3605 },3606 },3607 },3608 },3609 },3610 }3611 csv := newCSV("stable", ns.GetName(), "", semver.MustParse("0.1.0"), nil, nil, nil)3612 // Defer CRD clean up3613 defer func() {3614 Eventually(func() error {3615 return client.IgnoreNotFound(ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{}))3616 }).Should(Succeed())3617 Eventually(func() error {3618 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &csv))3619 }).Should(Succeed())3620 }()3621 scheme := runtime.NewScheme()3622 Expect(apiextensionsv1.AddToScheme(scheme)).To(Succeed())3623 Expect(operatorsv1alpha1.AddToScheme(scheme)).To(Succeed())3624 var crdManifest, csvManifest bytes.Buffer3625 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(&crd, &crdManifest)).To(Succeed())3626 Expect(k8sjson.NewSerializer(k8sjson.DefaultMetaFactory, scheme, scheme, false).Encode(&csv, &csvManifest)).To(Succeed())3627 plan := &operatorsv1alpha1.InstallPlan{3628 ObjectMeta: metav1.ObjectMeta{3629 Namespace: ns.GetName(),3630 Name: genName("ip-"),3631 },3632 Spec: operatorsv1alpha1.InstallPlanSpec{3633 Approval: operatorsv1alpha1.ApprovalAutomatic,3634 Approved: true,3635 ClusterServiceVersionNames: []string{csv.GetName()},3636 },3637 }3638 Expect(ctx.Ctx().Client().Create(context.Background(), plan)).To(Succeed())3639 plan.Status = operatorsv1alpha1.InstallPlanStatus{3640 AttenuatedServiceAccountRef: &corev1.ObjectReference{3641 Name: sa.GetName(),3642 Namespace: sa.GetNamespace(),3643 Kind: "ServiceAccount",3644 },3645 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,3646 CatalogSources: []string{},3647 Plan: []*operatorsv1alpha1.Step{3648 {3649 Status: operatorsv1alpha1.StepStatusUnknown,3650 Resource: operatorsv1alpha1.StepResource{3651 Name: csv.GetName(),3652 Version: "v1alpha1",3653 Kind: "ClusterServiceVersion",3654 Manifest: csvManifest.String(),3655 },3656 },3657 {3658 Status: operatorsv1alpha1.StepStatusUnknown,3659 Resource: operatorsv1alpha1.StepResource{3660 Name: crd.GetName(),3661 Version: "v1",3662 Kind: "CustomResourceDefinition",3663 Manifest: crdManifest.String(),3664 },3665 },3666 },3667 }3668 Expect(ctx.Ctx().Client().Status().Update(context.Background(), plan)).To(Succeed())3669 key := client.ObjectKeyFromObject(plan)3670 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {3671 return plan, ctx.Ctx().Client().Get(context.Background(), key, plan)3672 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseComplete))3673 // delete installplan, and create one with just a CSV resource which should succeed3674 By("installing additional resources that are allowed in the SA")3675 err = crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).Delete(context.Background(), plan.GetName(), metav1.DeleteOptions{})3676 Expect(err).ToNot(HaveOccurred())3677 newPlan := &operatorsv1alpha1.InstallPlan{3678 ObjectMeta: metav1.ObjectMeta{3679 Namespace: ns.GetName(),3680 Name: genName("ip-"),3681 },3682 Spec: operatorsv1alpha1.InstallPlanSpec{3683 Approval: operatorsv1alpha1.ApprovalAutomatic,3684 Approved: true,3685 ClusterServiceVersionNames: []string{csv.GetName()},3686 },3687 }3688 Expect(ctx.Ctx().Client().Create(context.Background(), newPlan)).To(Succeed())3689 newPlan.Status = operatorsv1alpha1.InstallPlanStatus{3690 AttenuatedServiceAccountRef: &corev1.ObjectReference{3691 Name: sa.GetName(),3692 Namespace: sa.GetNamespace(),3693 Kind: "ServiceAccount",3694 },3695 Phase: operatorsv1alpha1.InstallPlanPhaseInstalling,3696 CatalogSources: []string{},3697 Plan: []*operatorsv1alpha1.Step{3698 {3699 Status: operatorsv1alpha1.StepStatusUnknown,3700 Resource: operatorsv1alpha1.StepResource{3701 Name: csv.GetName(),3702 Version: "v1alpha1",3703 Kind: "ClusterServiceVersion",3704 Manifest: csvManifest.String(),3705 },3706 },3707 },3708 }3709 Expect(ctx.Ctx().Client().Status().Update(context.Background(), newPlan)).To(Succeed())3710 newKey := client.ObjectKeyFromObject(newPlan)3711 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {3712 return newPlan, ctx.Ctx().Client().Get(context.Background(), newKey, newPlan)3713 }).Should(HavePhase(operatorsv1alpha1.InstallPlanPhaseComplete))3714 })3715})3716type checkInstallPlanFunc func(fip *operatorsv1alpha1.InstallPlan) bool3717func validateCRDVersions(t GinkgoTInterface, c operatorclient.ClientInterface, name string, expectedVersions map[string]struct{}) {3718 // Retrieve CRD information3719 crd, err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), name, metav1.GetOptions{})3720 require.NoError(t, err)3721 require.Equal(t, len(expectedVersions), len(crd.Spec.Versions), "number of CRD versions don't not match installed")3722 for _, version := range crd.Spec.Versions {3723 _, ok := expectedVersions[version.Name]3724 require.True(t, ok, "couldn't find %v in expected versions: %#v", version.Name, expectedVersions)3725 // Remove the entry from the expected steps set (to ensure no duplicates in resolved plan)3726 delete(expectedVersions, version.Name)3727 }3728 // Should have removed every matching version3729 require.Equal(t, 0, len(expectedVersions), "Actual CRD versions do not match expected")3730}3731func buildInstallPlanPhaseCheckFunc(phases ...operatorsv1alpha1.InstallPlanPhase) checkInstallPlanFunc {3732 return func(fip *operatorsv1alpha1.InstallPlan) bool {3733 ctx.Ctx().Logf("installplan %v is in phase %v", fip.GetName(), fip.Status.Phase)3734 satisfiesAny := false3735 for _, phase := range phases {3736 satisfiesAny = satisfiesAny || fip.Status.Phase == phase3737 }3738 return satisfiesAny3739 }3740}3741func buildInstallPlanCleanupFunc(crc versioned.Interface, namespace string, installPlan *operatorsv1alpha1.InstallPlan) cleanupFunc {3742 return func() {3743 deleteOptions := &metav1.DeleteOptions{}3744 for _, step := range installPlan.Status.Plan {3745 if step.Resource.Kind == operatorsv1alpha1.ClusterServiceVersionKind {3746 if err := crc.OperatorsV1alpha1().ClusterServiceVersions(namespace).Delete(context.Background(), step.Resource.Name, *deleteOptions); err != nil {3747 fmt.Println(err)3748 }3749 }3750 }3751 if err := crc.OperatorsV1alpha1().InstallPlans(namespace).Delete(context.Background(), installPlan.GetName(), *deleteOptions); err != nil {3752 fmt.Println(err)3753 }3754 err := waitForDelete(func() error {3755 _, err := crc.OperatorsV1alpha1().InstallPlans(namespace).Get(context.Background(), installPlan.GetName(), metav1.GetOptions{})3756 return err3757 })3758 if err != nil {3759 fmt.Println(err)3760 }3761 }3762}3763func fetchInstallPlan(t GinkgoTInterface, c versioned.Interface, name string, namespace string, checkPhase checkInstallPlanFunc) (*operatorsv1alpha1.InstallPlan, error) {3764 return fetchInstallPlanWithNamespace(t, c, name, namespace, checkPhase)3765}3766func fetchInstallPlanWithNamespace(t GinkgoTInterface, c versioned.Interface, name string, namespace string, checkPhase checkInstallPlanFunc) (*operatorsv1alpha1.InstallPlan, error) {3767 var fetchedInstallPlan *operatorsv1alpha1.InstallPlan3768 var err error3769 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {3770 fetchedInstallPlan, err = c.OperatorsV1alpha1().InstallPlans(namespace).Get(context.Background(), name, metav1.GetOptions{})3771 if err != nil || fetchedInstallPlan == nil {3772 return false, err3773 }3774 return checkPhase(fetchedInstallPlan), nil3775 })3776 return fetchedInstallPlan, err3777}3778// do not return an error if the installplan has not been created yet3779func waitForInstallPlan(c versioned.Interface, name string, namespace string, checkPhase checkInstallPlanFunc) (*operatorsv1alpha1.InstallPlan, error) {3780 var fetchedInstallPlan *operatorsv1alpha1.InstallPlan3781 var err error3782 err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {3783 fetchedInstallPlan, err = c.OperatorsV1alpha1().InstallPlans(namespace).Get(context.Background(), name, metav1.GetOptions{})3784 if err != nil && !apierrors.IsNotFound(err) {3785 return false, err3786 }3787 return checkPhase(fetchedInstallPlan), nil3788 })3789 return fetchedInstallPlan, err3790}3791func newNginxInstallStrategy(name string, permissions []operatorsv1alpha1.StrategyDeploymentPermissions, clusterPermissions []operatorsv1alpha1.StrategyDeploymentPermissions) operatorsv1alpha1.NamedInstallStrategy {3792 // Create an nginx details deployment3793 details := operatorsv1alpha1.StrategyDetailsDeployment{3794 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{3795 {3796 Name: name,3797 Spec: appsv1.DeploymentSpec{3798 Selector: &metav1.LabelSelector{3799 MatchLabels: map[string]string{"app": "nginx"},3800 },3801 Replicas: &singleInstance,3802 Template: corev1.PodTemplateSpec{3803 ObjectMeta: metav1.ObjectMeta{3804 Labels: map[string]string{"app": "nginx"},3805 },3806 Spec: corev1.PodSpec{Containers: []corev1.Container{3807 {3808 Name: genName("nginx"),3809 Image: *dummyImage,3810 Ports: []corev1.ContainerPort{{ContainerPort: 80}},3811 ImagePullPolicy: corev1.PullIfNotPresent,3812 },3813 }},3814 },3815 },3816 },3817 },3818 Permissions: permissions,3819 ClusterPermissions: clusterPermissions,3820 }3821 namedStrategy := operatorsv1alpha1.NamedInstallStrategy{3822 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment,3823 StrategySpec: details,3824 }3825 return namedStrategy3826}3827func newCRD(plural string) apiextensions.CustomResourceDefinition {3828 crd := apiextensions.CustomResourceDefinition{3829 ObjectMeta: metav1.ObjectMeta{3830 Name: plural + ".cluster.com",3831 },3832 Spec: apiextensions.CustomResourceDefinitionSpec{3833 Group: "cluster.com",3834 Versions: []apiextensions.CustomResourceDefinitionVersion{3835 {3836 Name: "v1alpha1",3837 Served: true,3838 Storage: true,3839 Schema: &apiextensions.CustomResourceValidation{3840 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{3841 Type: "object",3842 Description: "my crd schema",3843 },3844 },3845 },3846 },3847 Names: apiextensions.CustomResourceDefinitionNames{3848 Plural: plural,3849 Singular: plural,3850 Kind: plural,3851 ListKind: plural + "list",3852 },3853 Scope: apiextensions.NamespaceScoped,3854 },3855 }3856 return crd3857}3858func newCSV(name, namespace, replaces string, version semver.Version, owned []apiextensions.CustomResourceDefinition, required []apiextensions.CustomResourceDefinition, namedStrategy *operatorsv1alpha1.NamedInstallStrategy) operatorsv1alpha1.ClusterServiceVersion {3859 csvType = metav1.TypeMeta{3860 Kind: operatorsv1alpha1.ClusterServiceVersionKind,3861 APIVersion: operatorsv1alpha1.SchemeGroupVersion.String(),3862 }3863 // set a simple default strategy if none given3864 var strategy operatorsv1alpha1.NamedInstallStrategy3865 if namedStrategy == nil {3866 strategy = newNginxInstallStrategy(genName("dep"), nil, nil)3867 } else {3868 strategy = *namedStrategy3869 }3870 csv := operatorsv1alpha1.ClusterServiceVersion{3871 TypeMeta: csvType,3872 ObjectMeta: metav1.ObjectMeta{3873 Name: name,3874 Namespace: namespace,3875 },3876 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{3877 Replaces: replaces,3878 Version: opver.OperatorVersion{Version: version},3879 MinKubeVersion: "0.0.0",3880 InstallModes: []operatorsv1alpha1.InstallMode{3881 {3882 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace,3883 Supported: true,3884 },3885 {3886 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace,3887 Supported: true,3888 },3889 {3890 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace,3891 Supported: true,3892 },3893 {3894 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces,3895 Supported: true,3896 },3897 },3898 InstallStrategy: strategy,3899 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{3900 Owned: nil,3901 Required: nil,3902 },3903 },3904 }3905 // Populate owned and required3906 for _, crd := range owned {3907 crdVersion := "v1alpha1"3908 for _, v := range crd.Spec.Versions {3909 if v.Served && v.Storage {3910 crdVersion = v.Name3911 break3912 }3913 }3914 desc := operatorsv1alpha1.CRDDescription{3915 Name: crd.GetName(),3916 Version: crdVersion,3917 Kind: crd.Spec.Names.Plural,3918 DisplayName: crd.GetName(),3919 Description: crd.GetName(),3920 }3921 csv.Spec.CustomResourceDefinitions.Owned = append(csv.Spec.CustomResourceDefinitions.Owned, desc)3922 }3923 for _, crd := range required {3924 crdVersion := "v1alpha1"3925 for _, v := range crd.Spec.Versions {3926 if v.Served && v.Storage {3927 crdVersion = v.Name3928 break3929 }3930 }3931 desc := operatorsv1alpha1.CRDDescription{3932 Name: crd.GetName(),3933 Version: crdVersion,3934 Kind: crd.Spec.Names.Plural,3935 DisplayName: crd.GetName(),3936 Description: crd.GetName(),3937 }3938 csv.Spec.CustomResourceDefinitions.Required = append(csv.Spec.CustomResourceDefinitions.Required, desc)3939 }3940 return csv3941}3942func newInstallPlanWithDummySteps(name, namespace string, phase operatorsv1alpha1.InstallPlanPhase) *operatorsv1alpha1.InstallPlan {3943 return &operatorsv1alpha1.InstallPlan{3944 ObjectMeta: metav1.ObjectMeta{3945 Name: name,3946 Namespace: namespace,3947 },3948 Spec: operatorsv1alpha1.InstallPlanSpec{3949 ClusterServiceVersionNames: []string{"foobar"},3950 Approval: operatorsv1alpha1.ApprovalAutomatic,...

Full Screen

Full Screen

crd_e2e_test.go

Source:crd_e2e_test.go Github

copy

Full Screen

...28 crc = ctx.Ctx().OperatorClient()29 ns = SetupGeneratedTestNamespace(genName("crd-e2e-"))30 })31 AfterEach(func() {32 TeardownNamespace(ns.GetName())33 })34 // issue: https://github.com/operator-framework/operator-lifecycle-manager/issues/264035 It("[FLAKE] creates v1 CRDs with a v1 schema successfully", func() {36 By("v1 crds with a valid openapiv3 schema should be created successfully by OLM")37 mainPackageName := genName("nginx-update2-")38 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)39 stableChannel := "stable"40 crdPlural := genName("ins-")41 crdName := crdPlural + ".cluster.com"42 v1crd := apiextensionsv1.CustomResourceDefinition{43 ObjectMeta: metav1.ObjectMeta{44 Name: crdName,45 },46 Spec: apiextensionsv1.CustomResourceDefinitionSpec{47 Scope: apiextensionsv1.NamespaceScoped,48 Group: "cluster.com",49 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{50 {51 Name: "v1alpha1",52 Served: true,53 Storage: true,54 Schema: &apiextensionsv1.CustomResourceValidation{55 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{56 Type: "object",57 Description: "my crd schema",58 },59 },60 },61 },62 },63 }64 mainCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), nil, nil, nil)65 mainCatalogName := genName("mock-ocs-main-update2-")66 mainManifests := []registry.PackageManifest{67 {68 PackageName: mainPackageName,69 Channels: []registry.PackageChannel{70 {Name: stableChannel, CurrentCSVName: mainPackageStable},71 },72 DefaultChannelName: stableChannel,73 },74 }75 // Create the catalog sources76 _, cleanupMainCatalogSource := createV1CRDInternalCatalogSource(GinkgoT(), c, crc, mainCatalogName, ns.GetName(), mainManifests, []apiextensionsv1.CustomResourceDefinition{v1crd}, []operatorsv1alpha1.ClusterServiceVersion{mainCSV})77 defer cleanupMainCatalogSource()78 defer func() {79 _ = crc.OperatorsV1alpha1().ClusterServiceVersions(ns.GetName()).Delete(context.TODO(), mainCSV.GetName(), metav1.DeleteOptions{})80 _ = c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.TODO(), v1crd.GetName(), metav1.DeleteOptions{})81 }()82 // Attempt to get the catalog source before creating install plan83 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)84 Expect(err).ToNot(HaveOccurred())85 subscriptionName := genName("sub-nginx-update2-")86 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)87 defer subscriptionCleanup()88 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)89 Expect(err).ToNot(HaveOccurred())90 Expect(subscription).ToNot(Equal(nil))91 Expect(subscription.Status.InstallPlanRef).ToNot(Equal(nil))92 Expect(mainCSV.GetName()).To(Equal(subscription.Status.CurrentCSV))93 installPlanName := subscription.Status.InstallPlanRef.Name94 // Wait for InstallPlan to be status: Complete before checking resource presence95 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))96 Expect(err).ToNot(HaveOccurred())97 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)98 Expect(fetchedInstallPlan.Status.Phase).To(Equal(operatorsv1alpha1.InstallPlanPhaseComplete))99 })100 // issue:https://github.com/operator-framework/operator-lifecycle-manager/issues/2638101 It("[FLAKE] blocks a CRD upgrade that could cause data loss", func() {102 By("checking the storage versions in the existing CRD status and the spec of the new CRD")103 mainPackageName := genName("nginx-update2-")104 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)105 stableChannel := "stable"106 crdPlural := genName("ins-")107 crdName := crdPlural + ".cluster.com"108 oldCRD := apiextensions.CustomResourceDefinition{109 ObjectMeta: metav1.ObjectMeta{110 Name: crdName,111 },112 Spec: apiextensions.CustomResourceDefinitionSpec{113 Group: "cluster.com",114 Versions: []apiextensions.CustomResourceDefinitionVersion{115 {116 Name: "v1alpha2",117 Served: true,118 Storage: true,119 Schema: &apiextensions.CustomResourceValidation{120 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{121 Type: "object",122 Description: "my crd schema",123 },124 },125 },126 {127 Name: "v2alpha1",128 Served: true,129 Storage: false,130 Schema: &apiextensions.CustomResourceValidation{131 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{132 Type: "object",133 Description: "my crd schema",134 },135 },136 },137 },138 Names: apiextensions.CustomResourceDefinitionNames{139 Plural: crdPlural,140 Singular: crdPlural,141 Kind: crdPlural,142 ListKind: "list" + crdPlural,143 },144 Scope: apiextensions.NamespaceScoped,145 },146 }147 alphaChannel := "alpha"148 mainPackageAlpha := fmt.Sprintf("%s-alpha", mainPackageName)149 newCRD := apiextensions.CustomResourceDefinition{150 ObjectMeta: metav1.ObjectMeta{151 Name: crdName,152 },153 Spec: apiextensions.CustomResourceDefinitionSpec{154 Group: "cluster.com",155 Versions: []apiextensions.CustomResourceDefinitionVersion{156 {157 Name: "v1alpha3",158 Served: true,159 Storage: true,160 Schema: &apiextensions.CustomResourceValidation{161 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{162 Type: "object",163 Description: "my crd schema",164 },165 },166 },167 {168 Name: "v2alpha2",169 Served: true,170 Storage: false,171 Schema: &apiextensions.CustomResourceValidation{172 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{173 Type: "object",174 Description: "my crd schema",175 },176 },177 },178 },179 Names: apiextensions.CustomResourceDefinitionNames{180 Plural: crdPlural,181 Singular: crdPlural,182 Kind: crdPlural,183 ListKind: "list" + crdPlural,184 },185 Scope: apiextensions.NamespaceScoped,186 },187 }188 oldCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{oldCRD}, nil, nil)189 newCSV := newCSV(mainPackageAlpha, ns.GetName(), mainPackageStable, semver.MustParse("0.1.1"), []apiextensions.CustomResourceDefinition{newCRD}, nil, nil)190 mainCatalogName := genName("mock-ocs-main-update2-")191 mainManifests := []registry.PackageManifest{192 {193 PackageName: mainPackageName,194 Channels: []registry.PackageChannel{195 {Name: stableChannel, CurrentCSVName: mainPackageStable},196 {Name: alphaChannel, CurrentCSVName: mainPackageAlpha},197 },198 DefaultChannelName: stableChannel,199 },200 }201 // Create the catalog sources202 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{oldCRD, newCRD}, []operatorsv1alpha1.ClusterServiceVersion{oldCSV, newCSV})203 defer cleanupMainCatalogSource()204 defer func() {205 _ = crc.OperatorsV1alpha1().ClusterServiceVersions(ns.GetName()).Delete(context.TODO(), oldCSV.GetName(), metav1.DeleteOptions{})206 _ = crc.OperatorsV1alpha1().ClusterServiceVersions(ns.GetName()).Delete(context.TODO(), newCSV.GetName(), metav1.DeleteOptions{})207 _ = c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.TODO(), oldCRD.GetName(), metav1.DeleteOptions{})208 _ = c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.TODO(), newCRD.GetName(), metav1.DeleteOptions{})209 }()210 // Attempt to get the catalog source before creating install plan211 _, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)212 Expect(err).ToNot(HaveOccurred())213 subscriptionName := genName("sub-nginx-update2-")214 subscriptionCleanup := createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)215 defer subscriptionCleanup()216 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)217 Expect(err).ToNot(HaveOccurred())218 Expect(subscription).ToNot(BeNil())219 Expect(subscription.Status.InstallPlanRef).ToNot(Equal(nil))220 Expect(oldCSV.GetName()).To(Equal(subscription.Status.CurrentCSV))221 installPlanName := subscription.Status.InstallPlanRef.Name222 // Wait for InstallPlan to be status: Complete before checking resource presence223 fetchedInstallPlan, err := fetchInstallPlan(GinkgoT(), crc, installPlanName, ns.GetName(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseComplete))224 Expect(err).ToNot(HaveOccurred())225 GinkgoT().Logf("Install plan %s fetched with status %s", fetchedInstallPlan.GetName(), fetchedInstallPlan.Status.Phase)226 Expect(fetchedInstallPlan.Status.Phase).To(Equal(operatorsv1alpha1.InstallPlanPhaseComplete))227 // old CRD has been installed onto the cluster - now upgrade the subscription to point to the channel with the new CRD228 // installing the new CSV should fail with a warning about data loss, since a storage version is missing in the new CRD229 // use server-side apply to apply the update to the subscription point to the alpha channel230 Eventually(Apply(subscription, func(s *operatorsv1alpha1.Subscription) error {231 s.Spec.Channel = alphaChannel232 return nil233 })).Should(Succeed())234 ctx.Ctx().Logf("updated subscription to point to alpha channel")235 subscriptionAtLatestWithDifferentInstallPlan := func(v *operatorsv1alpha1.Subscription) bool {236 return subscriptionStateAtLatestChecker(v) && v.Status.InstallPlanRef != nil && v.Status.InstallPlanRef.Name != fetchedInstallPlan.Name237 }238 // fetch new subscription239 s, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionAtLatestWithDifferentInstallPlan)240 Expect(err).ToNot(HaveOccurred())241 Expect(s).ToNot(BeNil())242 Expect(s.Status.InstallPlanRef).ToNot(Equal(nil))243 // Check the error on the installplan - should be related to data loss and the CRD upgrade missing a stored version244 Eventually(func() (*operatorsv1alpha1.InstallPlan, error) {245 return crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).Get(context.TODO(), s.Status.InstallPlanRef.Name, metav1.GetOptions{})246 }).Should(And(247 WithTransform(248 func(v *operatorsv1alpha1.InstallPlan) operatorsv1alpha1.InstallPlanPhase {249 return v.Status.Phase250 },251 Equal(operatorsv1alpha1.InstallPlanPhaseFailed),252 ),253 WithTransform(254 func(v *operatorsv1alpha1.InstallPlan) string {255 return v.Status.Conditions[len(v.Status.Conditions)-1].Message256 },257 ContainSubstring("risk of data loss"),258 ),259 ))260 })261 // Create a CRD on cluster with v1alpha1 (storage)262 // Update that CRD with v1alpha2 (storage), v1alpha1 (served)263 // Now the CRD should have two versions in status.storedVersions264 // Now make a catalog with a CRD with just v1alpha2 (storage)265 // That should fail because v1alpha1 is still in status.storedVersions - risk of data loss266 // Update the CRD status to remove the v1alpha1267 // Now the installplan should succeed268 It("allows a CRD upgrade that doesn't cause data loss", func() {269 By("manually editing the storage versions in the existing CRD status")270 crdPlural := genName("ins-v1-")271 crdName := crdPlural + ".cluster.com"272 crdGroup := "cluster.com"273 oldCRD := &apiextensionsv1.CustomResourceDefinition{274 ObjectMeta: metav1.ObjectMeta{275 Name: crdName,276 },277 Spec: apiextensionsv1.CustomResourceDefinitionSpec{278 Group: crdGroup,279 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{280 {281 Name: "v1alpha1",282 Served: true,283 Storage: true,284 Schema: &apiextensionsv1.CustomResourceValidation{285 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{286 Type: "object",287 Description: "my crd schema",288 },289 },290 },291 },292 Names: apiextensionsv1.CustomResourceDefinitionNames{293 Plural: crdPlural,294 Singular: crdPlural,295 Kind: crdPlural,296 ListKind: "list" + crdPlural,297 },298 Scope: apiextensionsv1.NamespaceScoped,299 },300 }301 _, err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Create(context.TODO(), oldCRD, metav1.CreateOptions{})302 Expect(err).ToNot(HaveOccurred(), "error creating old CRD")303 // wrap CRD update in a poll because of the object has been modified related errors304 Eventually(func() error {305 oldCRD, err = c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), oldCRD.GetName(), metav1.GetOptions{})306 if err != nil {307 return err308 }309 GinkgoT().Logf("old crd status stored versions: %#v", oldCRD.Status.StoredVersions)310 // set v1alpha1 to no longer stored311 oldCRD.Spec.Versions[0].Storage = false312 // update CRD on-cluster with a new version313 oldCRD.Spec.Versions = append(oldCRD.Spec.Versions, apiextensionsv1.CustomResourceDefinitionVersion{314 Name: "v1alpha2",315 Served: true,316 Storage: true,317 Schema: &apiextensionsv1.CustomResourceValidation{318 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{319 Type: "object",320 },321 },322 })323 updatedCRD, err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), oldCRD, metav1.UpdateOptions{})324 if err != nil {325 return err326 }327 GinkgoT().Logf("updated crd status stored versions: %#v", updatedCRD.Status.StoredVersions) // both v1alpha1 and v1alpha2 should be in the status328 return nil329 }).Should(BeNil())330 // create CSV and catalog with just the catalog CRD331 catalogCRD := apiextensions.CustomResourceDefinition{332 ObjectMeta: metav1.ObjectMeta{333 Name: crdName,334 },335 Spec: apiextensions.CustomResourceDefinitionSpec{336 Group: crdGroup,337 Versions: []apiextensions.CustomResourceDefinitionVersion{338 {339 Name: "v1alpha2",340 Served: true,341 Storage: true,342 Schema: &apiextensions.CustomResourceValidation{343 OpenAPIV3Schema: &apiextensions.JSONSchemaProps{344 Type: "object",345 Description: "my crd schema",346 },347 },348 },349 },350 Names: apiextensions.CustomResourceDefinitionNames{351 Plural: crdPlural,352 Singular: crdPlural,353 Kind: crdPlural,354 ListKind: "list" + crdPlural,355 },356 Scope: apiextensions.NamespaceScoped,357 },358 }359 mainPackageName := genName("nginx-update2-")360 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)361 stableChannel := "stable"362 catalogCSV := newCSV(mainPackageStable, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{catalogCRD}, nil, nil)363 defer func() {364 _ = crc.OperatorsV1alpha1().ClusterServiceVersions(ns.GetName()).Delete(context.TODO(), catalogCSV.GetName(), metav1.DeleteOptions{})365 _ = c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.TODO(), catalogCRD.GetName(), metav1.DeleteOptions{})366 }()367 mainCatalogName := genName("mock-ocs-main-update2-")368 mainManifests := []registry.PackageManifest{369 {370 PackageName: mainPackageName,371 Channels: []registry.PackageChannel{372 {Name: stableChannel, CurrentCSVName: mainPackageStable},373 },374 DefaultChannelName: stableChannel,375 },376 }377 // Create the catalog sources378 _, cleanupMainCatalogSource := createInternalCatalogSource(c, crc, mainCatalogName, ns.GetName(), mainManifests, []apiextensions.CustomResourceDefinition{catalogCRD}, []operatorsv1alpha1.ClusterServiceVersion{catalogCSV})379 defer cleanupMainCatalogSource()380 // Attempt to get the catalog source before creating install plan381 _, err = fetchCatalogSourceOnStatus(crc, mainCatalogName, ns.GetName(), catalogSourceRegistryPodSynced)382 Expect(err).ToNot(HaveOccurred())383 subscriptionName := genName("sub-nginx-update2-")384 _ = createSubscriptionForCatalog(crc, ns.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)385 subscription, err := fetchSubscription(crc, ns.GetName(), subscriptionName, subscriptionHasInstallPlanChecker)386 Expect(err).ToNot(HaveOccurred())387 Expect(subscription).ToNot(BeNil())388 Expect(subscription.Status.InstallPlanRef).ToNot(Equal(nil))389 Expect(catalogCSV.GetName()).To(Equal(subscription.Status.CurrentCSV))390 // Check the error on the installplan - should be related to data loss and the CRD upgrade missing a stored version (v1alpha1)391 Eventually(392 func() (*operatorsv1alpha1.InstallPlan, error) {393 return crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).Get(context.TODO(), subscription.Status.InstallPlanRef.Name, metav1.GetOptions{})394 },395 90*time.Second, // exhaust retries396 ).Should(WithTransform(397 func(v *operatorsv1alpha1.InstallPlan) operatorsv1alpha1.InstallPlanPhase { return v.Status.Phase },398 Equal(operatorsv1alpha1.InstallPlanPhaseFailed),399 ))400 // update CRD status to remove the v1alpha1 stored version401 newCRD, err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), oldCRD.GetName(), metav1.GetOptions{})402 Expect(err).ToNot(HaveOccurred(), "error getting new CRD")403 newCRD.Status.StoredVersions = []string{"v1alpha2"}404 newCRD, err = c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().UpdateStatus(context.TODO(), newCRD, metav1.UpdateOptions{})405 Expect(err).ToNot(HaveOccurred(), "error updating new CRD")406 GinkgoT().Logf("new crd status stored versions: %#v", newCRD.Status.StoredVersions) // only v1alpha2 should be in the status now407 // install should now succeed408 oldInstallPlanRef := subscription.Status.InstallPlanRef.Name409 err = crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).Delete(context.TODO(), subscription.Status.InstallPlanRef.Name, metav1.DeleteOptions{})410 Expect(err).ToNot(HaveOccurred(), "error deleting failed install plan")411 // remove old subscription412 err = crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).Delete(context.TODO(), subscription.GetName(), metav1.DeleteOptions{})413 Expect(err).ToNot(HaveOccurred(), "error deleting old subscription")414 // remove old csv415 crc.OperatorsV1alpha1().ClusterServiceVersions(ns.GetName()).Delete(context.TODO(), mainPackageStable, metav1.DeleteOptions{})416 Expect(err).ToNot(HaveOccurred(), "error deleting old subscription")417 // recreate subscription418 subscriptionNameNew := genName("sub-nginx-update2-new-")419 _ = createSubscriptionForCatalog(crc, ns.GetName(), subscriptionNameNew, mainCatalogName, mainPackageName, stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)420 subscription, err = fetchSubscription(crc, ns.GetName(), subscriptionNameNew, subscriptionHasInstallPlanChecker)421 Expect(err).ToNot(HaveOccurred())422 Expect(subscription).ToNot(BeNil())423 Expect(subscription.Status.InstallPlanRef).ToNot(Equal(nil))424 Expect(catalogCSV.GetName()).To(Equal(subscription.Status.CurrentCSV))425 // eventually the subscription should create a new install plan426 Eventually(func() bool {427 sub, _ := crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).Get(context.TODO(), subscription.GetName(), metav1.GetOptions{})428 GinkgoT().Logf("waiting for subscription %s to generate a new install plan...", subscription.GetName())429 return sub.Status.InstallPlanRef.Name != oldInstallPlanRef430 }, 5*time.Minute, 10*time.Second).Should(BeTrue())431 // eventually the new installplan should succeed432 Eventually(func() bool {433 sub, _ := crc.OperatorsV1alpha1().Subscriptions(ns.GetName()).Get(context.TODO(), subscription.GetName(), metav1.GetOptions{})434 ip, err := crc.OperatorsV1alpha1().InstallPlans(ns.GetName()).Get(context.TODO(), sub.Status.InstallPlanRef.Name, metav1.GetOptions{})435 if apierrors.IsNotFound(err) {436 return false437 }438 GinkgoT().Logf("waiting for installplan to succeed...currently %s", ip.Status.Phase)439 return ip.Status.Phase == operatorsv1alpha1.InstallPlanPhaseComplete440 }).Should(BeTrue())441 GinkgoT().Log("manually reconciled potentially unsafe CRD upgrade")442 })443})...

Full Screen

Full Screen

policies_test.go

Source:policies_test.go Github

copy

Full Screen

...21 t.Errorf("Encountered error opening the file %+v", err)22 }23 expected := map[string]Conditions{24 "HelloRequest.Name": Conditions{25 CopyConditions: &[]ConditionStatement{{Allowed: true, If: "HelloRequest.GetName < main.main"}},26 PrintConditions: &[]ConditionStatement{},27 ModifyConditions: nil,28 },29 "HelloReply.Message": Conditions{30 CopyConditions: nil,31 PrintConditions: nil,32 ModifyConditions: nil,33 },34 }35 if intercepts["foo"] == nil || intercepts["foo"]["bar"] == nil {36 t.Errorf("Expected map[foo:map[bar:[baz]]]` in intercepts, got %+v", intercepts)37 }38 if len(intercepts["foo"]["bar"]) != 1 || intercepts["foo"]["bar"][0] != "baz" {39 t.Errorf("Expected map[foo:map[bar:[baz]]]` in intercepts, got %+v", intercepts)40 }41 for expected_key, expected_value := range expected {42 if found_value, ok := policies[expected_key]; ok {43 if !reflect.DeepEqual(found_value, expected_value) {44 t.Errorf("Expected %+v, got %+v", expected_value, found_value)45 }46 } else {47 t.Errorf("Expected %s to be in policies %+v", expected_key, policies)48 }49 }50}51func TestIsMatchesPositionIndependent(t *testing.T) {52 if !matches("me", []string{"me"}) {53 t.Errorf("Expected `me` to match `[me]`")54 }55 if !matches("me", []string{"you", "me"}) {56 t.Errorf("Expected `me` to match `[you, me]`")57 }58 if !matches("you", []string{"me", "you"}) {59 t.Errorf("Expected `you` to match `[me, you]`")60 }61 if matches("us", []string{"me", "you"}) {62 t.Errorf("Did not expect `us` to match `[me, you]`")63 }64 if matches("me < us", []string{"me", "you"}) {65 t.Errorf("Did not expect `me < us` to match `[me, you]`")66 }67 if !matches("me < us", []string{"me", "us", "you"}) {68 t.Errorf("Expected `me < us` to match `[me, us, you]`")69 }70 if !matches("me < us < you", []string{"me", "us", "you"}) {71 t.Errorf("Expected `me < us < you` to match `[me, us, you]`")72 }73 if !matches("me < you", []string{"me", "us", "you"}) {74 t.Errorf("Expected `me < you` to match `[me, us, you]`")75 }76 if !matches("me < you", []string{"me", "us", "them", "you"}) {77 t.Errorf("Expected `me < you` to match `[me, us,, them, you]`")78 }79 if matches("me < you", []string{"you", "us", "them", "me"}) {80 t.Errorf("Did not expect `me < you` to match `[you, us, them, me]`")81 }82 if matches("", []string{"me", "us", "you"}) {83 t.Errorf("Did not expected `us` to match `[me, us, you]`")84 }85 if !matches("me", []string{"me", "me", "me"}) {86 t.Errorf("Expected `me` to match `[me, me, me]`")87 }88 if matches("me < me < me < me", []string{"me", "me", "me"}) {89 t.Errorf("Did not expect `me < me < me < me` to match `[me, me, me]`")90 }91 if !matches("me < us", []string{"me", "you", "you", "us"}) {92 t.Errorf("Expected `me < us` to match `[me, you, you, us]`")93 }94}95func TestIsActionAllowedFunction(t *testing.T) {96 teardownFunc := setupExamplePolicy(t, "testing/files/example.json")97 defer teardownFunc(t)98 cases := []struct {99 message protoreflect.Name100 field protoreflect.Name101 act action102 trace []string103 shouldPass bool104 }{{105 "HelloRequest",106 "name",107 copying,108 []string{"HelloRequest.GetName", "main.main"},109 true,110 }, {111 "HelloRequest",112 "name",113 printing,114 []string{"HelloRequest.GetName", "main.main"},115 false,116 }, {117 "HelloRequest",118 "name",119 modifying,120 []string{"HelloRequest.GetName", "main.main"},121 true,122 }, {123 "HelloRequest",124 "name",125 copying,126 []string{"main.main"},127 false,128 }, {129 "HelloRequest",130 "name",131 printing,132 []string{"main.main"},133 false,134 }, {135 "HelloRequest",136 "name",137 modifying,138 []string{"main.main"},139 true,140 }, {141 "HelloRequest",142 "foo",143 copying,144 []string{"HelloRequest.GetName", "main.main"},145 true,146 }, {147 "HelloRequest",148 "foo",149 printing,150 []string{"HelloRequest.GetName", "main.main"},151 true,152 }, {153 "HelloRequest",154 "foo",155 modifying,156 []string{"HelloRequest.GetName", "main.main"},157 true,158 }, {159 "HelloRequest2",160 "name",161 copying,162 []string{"HelloRequest.GetName", "main.main"},163 true,164 }, {165 "HelloRequest2",166 "name",167 printing,168 []string{"HelloRequest.GetName", "main.main"},169 true,170 }, {171 "HelloRequest2",172 "name",173 modifying,174 []string{"HelloRequest.GetName", "main.main"},175 true,176 }}177 for _, caseof := range cases {178 if isActionAllowed(caseof.message, caseof.field, caseof.act, caseof.trace) != caseof.shouldPass {179 t.Errorf("Expected case %v to pass, but did not", caseof)180 }181 }182}...

Full Screen

Full Screen

GetName

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 fmt.Println(GetName())4}5import "fmt"6func main() {7 fmt.Println(GetName())8}9import "fmt"10func main() {11 fmt.Println(GetName())12}13import "fmt"14func main() {15 fmt.Println(GetName())16}17import "fmt"18func main() {19 fmt.Println(GetName())20}21import "fmt"22func main() {23 fmt.Println(GetName())24}25import "fmt"26func main() {27 fmt.Println(GetName())28}29import "fmt"30func main() {31 fmt.Println(GetName())32}33import "fmt"34func main() {35 fmt.Println(GetName())36}37import "fmt"38func main() {39 fmt.Println(GetName())40}41import "fmt"42func main() {43 fmt.Println(GetName())44}45import "fmt"46func main() {47 fmt.Println(GetName())48}49import "fmt"50func main() {51 fmt.Println(GetName())52}53import "fmt"54func main() {55 fmt.Println(GetName())56}57import "fmt"

Full Screen

Full Screen

GetName

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 name = GetName()4 fmt.Println(name)5}6import "fmt"7func main() {8 name = GetName()9 fmt.Println(name)10}11import "fmt"12func main() {13 name = GetName()14 fmt.Println(name)15}16import "fmt"17func main() {18 name = GetName()19 fmt.Println(name)20}21import "fmt"22func main() {23 name = GetName()24 fmt.Println(name)25}26import "fmt"27func main() {28 name = GetName()29 fmt.Println(name)30}31import "fmt"32func main() {33 name = GetName()34 fmt.Println(name)35}36import "fmt"37func main() {38 name = GetName()39 fmt.Println(name)40}41import "fmt"42func main() {43 name = GetName()44 fmt.Println(name)45}46import "fmt"47func main() {48 name = GetName()49 fmt.Println(name)50}51import "fmt"52func main() {53 name = GetName()54 fmt.Println(name)55}56import "fmt"57func main() {

Full Screen

Full Screen

GetName

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Hello World")4 fmt.Println(GetName())5}6import (7func main() {8 fmt.Println("Hello World")9 fmt.Println(GetName())10}11import (12func main() {13 fmt.Println("Hello World")14 fmt.Println(GetName())15}16import (17func main() {18 fmt.Println("Hello World")19 fmt.Println(GetName())20}21import (22func main() {23 fmt.Println("Hello World")24 fmt.Println(GetName())25}26import (27func main() {28 fmt.Println("Hello World")29 fmt.Println(GetName())30}31import (32func main() {33 fmt.Println("Hello World")34 fmt.Println(GetName())35}36import (37func main() {38 fmt.Println("Hello World")39 fmt.Println(GetName())40}41import (42func main() {43 fmt.Println("Hello World")44 fmt.Println(GetName())45}46import (47func main() {48 fmt.Println("Hello World")49 fmt.Println(GetName())50}51import (52func main() {53 fmt.Println("Hello World")54 fmt.Println(GetName())55}56import (57func main() {58 fmt.Println("Hello World")59 fmt.Println(GetName())60}

Full Screen

Full Screen

GetName

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 name = GetName()4 fmt.Println("Name: ", name)5}6func GetName() string {7}

Full Screen

Full Screen

GetName

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println(first.GetName())4}5import (6func main() {7 fmt.Println(first.GetName())8}9import (10func main() {11 fmt.Println(first.GetName())12}13import (14func main() {15 fmt.Println(first.GetName())16}17import (18func main() {19 fmt.Println(first.GetName())20}21import (22func main() {23 fmt.Println(first.GetName())24}25import (26func main() {27 fmt.Println(first.GetName())28}29import (30func main() {31 fmt.Println(first.GetName())32}33import (34func main() {35 fmt.Println(first.GetName())36}37import (38func main() {39 fmt.Println(first.GetName())40}41import (42func main() {43 fmt.Println(first.GetName

Full Screen

Full Screen

GetName

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 p := Person{"Sam", 25}4 fmt.Println(p.GetName())5}6import "fmt"7type Person struct {8}9func (p Person) GetName() string {10}11func main() {12 p := Person{"Sam", 25}13 fmt.Println(p.GetName())14}15import "fmt"16type Person struct {17}18func (p Person) GetName() string {19}20func main() {21 p := Person{"Sam", 25}22 fmt.Println(p.GetName())23}24import "fmt"25type Person struct {26}27func (p Person) GetName() string {28}29func main() {30 p := Person{"Sam", 25}31 fmt.Println(p.GetName())32}33import "fmt"34type Person struct {35}36func (p Person) GetName() string {37}38func main() {39 p := Person{"Sam", 25}40 fmt.Println(p.GetName())41}42import "fmt"43type Person struct {44}45func (p Person) GetName() string {46}47func main() {48 p := Person{"Sam", 25}49 fmt.Println(p.GetName())50}

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful