github.com/operator-framework/operator-lifecycle-manager@v0.30.0/test/e2e/csv_e2e_test.go (about) 1 package e2e 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strconv" 8 "strings" 9 "time" 10 11 . "github.com/onsi/ginkgo/v2" 12 . "github.com/onsi/gomega" 13 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" 14 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 15 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" 16 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 17 appsv1 "k8s.io/api/apps/v1" 18 corev1 "k8s.io/api/core/v1" 19 rbacv1 "k8s.io/api/rbac/v1" 20 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 21 "k8s.io/apimachinery/pkg/api/equality" 22 apierrors "k8s.io/apimachinery/pkg/api/errors" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 k8slabels "k8s.io/apimachinery/pkg/labels" 25 "k8s.io/apimachinery/pkg/util/diff" 26 "k8s.io/apimachinery/pkg/util/intstr" 27 "k8s.io/apimachinery/pkg/util/wait" 28 "k8s.io/apimachinery/pkg/watch" 29 apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 32 operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" 33 operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" 34 "github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx" 35 ) 36 37 var _ = Describe("ClusterServiceVersion", func() { 38 var ( 39 generatedNamespace corev1.Namespace 40 c operatorclient.ClientInterface 41 crc versioned.Interface 42 ) 43 44 BeforeEach(func() { 45 c = ctx.Ctx().KubeClient() 46 crc = ctx.Ctx().OperatorClient() 47 }) 48 49 AfterEach(func() { 50 TeardownNamespace(generatedNamespace.GetName()) 51 }) 52 53 Context("OwnNamespace OperatorGroup", func() { 54 55 BeforeEach(func() { 56 nsName := genName("csv-e2e-") 57 generatedNamespace = SetupGeneratedTestNamespace(nsName, nsName) 58 }) 59 60 When("a CustomResourceDefinition was installed alongside a ClusterServiceVersion", func() { 61 var ( 62 crd apiextensionsv1.CustomResourceDefinition 63 apiname string 64 apifullname string 65 ) 66 67 BeforeEach(func() { 68 apiname = genName("api") 69 apifullname = apiname + "s.example.com" 70 crd = apiextensionsv1.CustomResourceDefinition{ 71 ObjectMeta: metav1.ObjectMeta{ 72 Name: apifullname, 73 Annotations: map[string]string{ 74 "operatorframework.io/installed-alongside-0": fmt.Sprintf("%s/associated-csv", generatedNamespace.GetName()), 75 }, 76 }, 77 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 78 Group: "example.com", 79 Scope: apiextensionsv1.ClusterScoped, 80 Names: apiextensionsv1.CustomResourceDefinitionNames{ 81 Plural: apiname + "s", 82 Singular: apiname, 83 Kind: strings.Title(apiname), 84 ListKind: strings.Title(apiname) + "List", 85 }, 86 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{ 87 Name: "v1", 88 Served: true, 89 Storage: true, 90 Schema: &apiextensionsv1.CustomResourceValidation{ 91 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 92 Type: "object", 93 }, 94 }, 95 }}, 96 }, 97 } 98 Eventually(func() error { 99 return ctx.Ctx().Client().Create(context.Background(), &crd) 100 }).Should(Succeed()) 101 }) 102 103 AfterEach(func() { 104 Eventually(func() error { 105 return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.TODO(), crd.GetName(), metav1.DeleteOptions{}) 106 }).Should(Succeed()) 107 }) 108 109 It("[FLAKE] can satisfy an associated ClusterServiceVersion's ownership requirement", func() { 110 associated := operatorsv1alpha1.ClusterServiceVersion{ 111 ObjectMeta: metav1.ObjectMeta{ 112 Name: "associated-csv", 113 Namespace: generatedNamespace.GetName(), 114 }, 115 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 116 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 117 Owned: []operatorsv1alpha1.CRDDescription{{ 118 Name: apifullname, 119 Version: "v1", 120 Kind: "Test", 121 }}, 122 }, 123 InstallStrategy: newNginxInstallStrategy(genName("deployment-"), nil, nil), 124 InstallModes: []operatorsv1alpha1.InstallMode{ 125 { 126 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 127 Supported: true, 128 }, 129 }, 130 }, 131 } 132 Expect(ctx.Ctx().Client().Create(context.Background(), &associated)).To(Succeed()) 133 134 Eventually(func() ([]operatorsv1alpha1.RequirementStatus, error) { 135 if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&associated), &associated); err != nil { 136 return nil, err 137 } 138 var result []operatorsv1alpha1.RequirementStatus 139 for _, s := range associated.Status.RequirementStatus { 140 result = append(result, operatorsv1alpha1.RequirementStatus{ 141 Group: s.Group, 142 Version: s.Version, 143 Kind: s.Kind, 144 Name: s.Name, 145 Status: s.Status, 146 }) 147 } 148 return result, nil 149 }).Should(ContainElement( 150 operatorsv1alpha1.RequirementStatus{ 151 Group: apiextensionsv1.SchemeGroupVersion.Group, 152 Version: apiextensionsv1.SchemeGroupVersion.Version, 153 Kind: "CustomResourceDefinition", 154 Name: crd.GetName(), 155 Status: operatorsv1alpha1.RequirementStatusReasonPresent, 156 }, 157 )) 158 159 Eventually(func() error { 160 return ctx.Ctx().Client().Delete(context.Background(), &associated) 161 }).Should(Succeed()) 162 }) 163 164 // Without this exception, upgrades can become blocked 165 // when the original CSV's CRD requirement becomes 166 // unsatisfied. 167 It("can satisfy an unassociated ClusterServiceVersion's ownership requirement if replaced by an associated ClusterServiceVersion", func() { 168 unassociated := operatorsv1alpha1.ClusterServiceVersion{ 169 ObjectMeta: metav1.ObjectMeta{ 170 Name: "unassociated-csv", 171 Namespace: generatedNamespace.GetName(), 172 }, 173 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 174 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 175 Owned: []operatorsv1alpha1.CRDDescription{{ 176 Name: apifullname, 177 Version: "v1", 178 Kind: "Test", 179 }}, 180 }, 181 InstallStrategy: newNginxInstallStrategy(genName("deployment-"), nil, nil), 182 InstallModes: []operatorsv1alpha1.InstallMode{ 183 { 184 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 185 Supported: true, 186 }, 187 }, 188 }, 189 } 190 Expect(ctx.Ctx().Client().Create(context.Background(), &unassociated)).To(Succeed()) 191 192 associated := operatorsv1alpha1.ClusterServiceVersion{ 193 ObjectMeta: metav1.ObjectMeta{ 194 Name: "associated-csv", 195 Namespace: generatedNamespace.GetName(), 196 }, 197 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 198 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 199 Owned: []operatorsv1alpha1.CRDDescription{{ 200 Name: apifullname, 201 Version: "v1", 202 Kind: "Test", 203 }}, 204 }, 205 InstallStrategy: newNginxInstallStrategy(genName("deployment-"), nil, nil), 206 InstallModes: []operatorsv1alpha1.InstallMode{ 207 { 208 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 209 Supported: true, 210 }, 211 }, 212 Replaces: unassociated.GetName(), 213 }, 214 } 215 Expect(ctx.Ctx().Client().Create(context.Background(), &associated)).To(Succeed()) 216 217 Eventually(func() error { 218 return ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&unassociated), &unassociated) 219 }).Should(WithTransform(apierrors.IsNotFound, BeTrue())) 220 221 Eventually(func() error { 222 return ctx.Ctx().Client().Delete(context.Background(), &associated) 223 }).Should(Succeed()) 224 }) 225 226 It("can satisfy an unassociated ClusterServiceVersion's non-ownership requirement", func() { 227 unassociated := operatorsv1alpha1.ClusterServiceVersion{ 228 ObjectMeta: metav1.ObjectMeta{ 229 Name: "unassociated-csv", 230 Namespace: generatedNamespace.GetName(), 231 }, 232 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 233 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 234 Required: []operatorsv1alpha1.CRDDescription{{ 235 Name: apifullname, 236 Version: "v1", 237 Kind: "Test", 238 }}, 239 }, 240 InstallStrategy: newNginxInstallStrategy(genName("deployment-"), nil, nil), 241 InstallModes: []operatorsv1alpha1.InstallMode{ 242 { 243 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 244 Supported: true, 245 }, 246 }, 247 }, 248 } 249 Expect(ctx.Ctx().Client().Create(context.Background(), &unassociated)).To(Succeed()) 250 251 Eventually(func() ([]operatorsv1alpha1.RequirementStatus, error) { 252 if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&unassociated), &unassociated); err != nil { 253 return nil, err 254 } 255 var result []operatorsv1alpha1.RequirementStatus 256 for _, s := range unassociated.Status.RequirementStatus { 257 result = append(result, operatorsv1alpha1.RequirementStatus{ 258 Group: s.Group, 259 Version: s.Version, 260 Kind: s.Kind, 261 Name: s.Name, 262 Status: s.Status, 263 }) 264 } 265 return result, nil 266 }).Should(ContainElement( 267 operatorsv1alpha1.RequirementStatus{ 268 Group: apiextensionsv1.SchemeGroupVersion.Group, 269 Version: apiextensionsv1.SchemeGroupVersion.Version, 270 Kind: "CustomResourceDefinition", 271 Name: crd.GetName(), 272 Status: operatorsv1alpha1.RequirementStatusReasonPresent, 273 }, 274 )) 275 Eventually(func() error { 276 return ctx.Ctx().Client().Delete(context.Background(), &unassociated) 277 }).Should(Succeed()) 278 }) 279 }) 280 281 When("an unassociated ClusterServiceVersion in different namespace owns the same CRD", func() { 282 var ( 283 crd apiextensionsv1.CustomResourceDefinition 284 apiname string 285 apifullname string 286 ) 287 288 BeforeEach(func() { 289 apiname = genName("api") 290 apifullname = apiname + "s.example.com" 291 crd = apiextensionsv1.CustomResourceDefinition{ 292 ObjectMeta: metav1.ObjectMeta{ 293 Name: apifullname, 294 Annotations: map[string]string{ 295 "operatorframework.io/installed-alongside-0": fmt.Sprintf("%s/associated-csv", generatedNamespace.GetName()), 296 }, 297 }, 298 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 299 Group: "example.com", 300 Scope: apiextensionsv1.ClusterScoped, 301 Names: apiextensionsv1.CustomResourceDefinitionNames{ 302 Plural: apiname + "s", 303 Singular: apiname, 304 Kind: strings.Title(apiname), 305 ListKind: strings.Title(apiname) + "List", 306 }, 307 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{ 308 Name: "v1", 309 Served: true, 310 Storage: true, 311 Schema: &apiextensionsv1.CustomResourceValidation{ 312 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 313 Type: "object", 314 }, 315 }, 316 }}, 317 }, 318 } 319 Eventually(func() error { 320 return ctx.Ctx().Client().Create(context.Background(), &crd) 321 }).Should(Succeed()) 322 }) 323 324 It("can satisfy the unassociated ClusterServiceVersion's ownership requirement", func() { 325 associated := operatorsv1alpha1.ClusterServiceVersion{ 326 ObjectMeta: metav1.ObjectMeta{ 327 Name: "associated-csv", 328 Namespace: generatedNamespace.GetName(), 329 }, 330 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 331 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 332 Owned: []operatorsv1alpha1.CRDDescription{{ 333 Name: apifullname, 334 Version: "v1", 335 Kind: "Test", 336 }}, 337 }, 338 InstallStrategy: newNginxInstallStrategy(genName("deployment-"), nil, nil), 339 InstallModes: []operatorsv1alpha1.InstallMode{ 340 { 341 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 342 Supported: true, 343 }, 344 }, 345 }, 346 } 347 Expect(ctx.Ctx().Client().Create(context.Background(), &associated)).To(Succeed()) 348 349 unassociated := operatorsv1alpha1.ClusterServiceVersion{ 350 ObjectMeta: metav1.ObjectMeta{ 351 Name: "unassociated-csv", 352 Namespace: generatedNamespace.GetName(), 353 }, 354 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 355 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 356 Owned: []operatorsv1alpha1.CRDDescription{{ 357 Name: apifullname, 358 Version: "v1", 359 Kind: "Test", 360 }}, 361 }, 362 InstallStrategy: newNginxInstallStrategy(genName("deployment-"), nil, nil), 363 InstallModes: []operatorsv1alpha1.InstallMode{ 364 { 365 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 366 Supported: true, 367 }, 368 }, 369 }, 370 } 371 Expect(ctx.Ctx().Client().Create(context.Background(), &unassociated)).To(Succeed()) 372 373 Eventually(func() ([]operatorsv1alpha1.RequirementStatus, error) { 374 if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&unassociated), &unassociated); err != nil { 375 return nil, err 376 } 377 var result []operatorsv1alpha1.RequirementStatus 378 for _, s := range unassociated.Status.RequirementStatus { 379 result = append(result, operatorsv1alpha1.RequirementStatus{ 380 Group: s.Group, 381 Version: s.Version, 382 Kind: s.Kind, 383 Name: s.Name, 384 Status: s.Status, 385 }) 386 } 387 return result, nil 388 }).Should(ContainElement( 389 operatorsv1alpha1.RequirementStatus{ 390 Group: apiextensionsv1.SchemeGroupVersion.Group, 391 Version: apiextensionsv1.SchemeGroupVersion.Version, 392 Kind: "CustomResourceDefinition", 393 Name: crd.GetName(), 394 Status: operatorsv1alpha1.RequirementStatusReasonPresent, 395 }, 396 )) 397 }) 398 }) 399 }) 400 401 Context("AllNamespaces OperatorGroup", func() { 402 BeforeEach(func() { 403 generatedNamespace = SetupGeneratedTestNamespace(genName("csv-e2e-")) 404 }) 405 406 When("a csv exists specifying two replicas with one max unavailable", func() { 407 var ( 408 csv operatorsv1alpha1.ClusterServiceVersion 409 ) 410 411 const ( 412 TestReadinessGate = "operatorframework.io/test-readiness-gate" 413 ) 414 415 BeforeEach(func() { 416 csv = operatorsv1alpha1.ClusterServiceVersion{ 417 ObjectMeta: metav1.ObjectMeta{ 418 GenerateName: "test-csv", 419 Namespace: generatedNamespace.GetName(), 420 }, 421 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 422 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 423 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 424 StrategySpec: operatorsv1alpha1.StrategyDetailsDeployment{ 425 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 426 { 427 Name: "deployment", 428 Spec: appsv1.DeploymentSpec{ 429 Strategy: appsv1.DeploymentStrategy{ 430 Type: appsv1.RollingUpdateDeploymentStrategyType, 431 RollingUpdate: &appsv1.RollingUpdateDeployment{ 432 MaxUnavailable: &[]intstr.IntOrString{intstr.FromInt(1)}[0], 433 }, 434 }, 435 Selector: &metav1.LabelSelector{ 436 MatchLabels: map[string]string{"app": "foobar"}, 437 }, 438 Replicas: &[]int32{2}[0], 439 Template: corev1.PodTemplateSpec{ 440 ObjectMeta: metav1.ObjectMeta{ 441 Labels: map[string]string{"app": "foobar"}, 442 }, 443 Spec: corev1.PodSpec{ 444 Containers: []corev1.Container{ 445 { 446 Name: "foobar", 447 Image: *dummyImage, 448 }, 449 }, 450 ReadinessGates: []corev1.PodReadinessGate{ 451 {ConditionType: TestReadinessGate}, 452 }, 453 }, 454 }, 455 }, 456 }, 457 }, 458 }, 459 }, 460 InstallModes: []operatorsv1alpha1.InstallMode{{ 461 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 462 Supported: true, 463 }}, 464 }, 465 } 466 Expect(ctx.Ctx().Client().Create(context.Background(), &csv)).To(Succeed()) 467 468 Eventually(func() (*operatorsv1alpha1.ClusterServiceVersion, error) { 469 var ps corev1.PodList 470 if err := ctx.Ctx().Client().List(context.Background(), &ps, client.MatchingLabels{"app": "foobar"}); err != nil { 471 return nil, err 472 } 473 474 if len(ps.Items) != 2 { 475 return nil, fmt.Errorf("%d pods match deployment selector, want %d", len(ps.Items), 2) 476 } 477 478 for _, pod := range ps.Items { 479 index := -1 480 for i, c := range pod.Status.Conditions { 481 if c.Type == TestReadinessGate { 482 index = i 483 break 484 } 485 } 486 if index == -1 { 487 index = len(pod.Status.Conditions) 488 pod.Status.Conditions = append(pod.Status.Conditions, corev1.PodCondition{Type: TestReadinessGate}) 489 } 490 if pod.Status.Conditions[index].Status == corev1.ConditionTrue { 491 continue 492 } 493 pod.Status.Conditions[index].Status = corev1.ConditionTrue 494 if err := ctx.Ctx().Client().Status().Update(context.Background(), &pod); err != nil { 495 return nil, err 496 } 497 } 498 499 if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&csv), &csv); err != nil { 500 return nil, err 501 } 502 return &csv, nil 503 }).Should(CSVHasPhase(operatorsv1alpha1.CSVPhaseSucceeded)) 504 }) 505 506 It("remains in phase Succeeded when only one pod is available", func() { 507 Eventually(func() int32 { 508 dep, err := c.GetDeployment(generatedNamespace.GetName(), "deployment") 509 if err != nil || dep == nil { 510 return 0 511 } 512 return dep.Status.ReadyReplicas 513 }).Should(Equal(int32(2))) 514 515 var ps corev1.PodList 516 Expect(ctx.Ctx().Client().List(context.Background(), &ps, client.MatchingLabels{"app": "foobar"})).To(Succeed()) 517 Expect(ps.Items).To(Not(BeEmpty())) 518 519 Expect(ctx.Ctx().Client().Delete(context.Background(), &ps.Items[0])).To(Succeed()) 520 521 Consistently(func() (*operatorsv1alpha1.ClusterServiceVersion, error) { 522 return &csv, ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&csv), &csv) 523 }).Should(CSVHasPhase(operatorsv1alpha1.CSVPhaseSucceeded)) 524 }) 525 }) 526 When("a copied csv exists", func() { 527 var ( 528 target corev1.Namespace 529 original operatorsv1alpha1.ClusterServiceVersion 530 copyCSV operatorsv1alpha1.ClusterServiceVersion 531 ) 532 533 BeforeEach(func() { 534 target = corev1.Namespace{ 535 ObjectMeta: metav1.ObjectMeta{ 536 GenerateName: "watched-", 537 }, 538 } 539 Expect(ctx.Ctx().Client().Create(context.Background(), &target)).To(Succeed()) 540 541 original = operatorsv1alpha1.ClusterServiceVersion{ 542 TypeMeta: metav1.TypeMeta{ 543 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 544 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 545 }, 546 ObjectMeta: metav1.ObjectMeta{ 547 GenerateName: "csv-", 548 Namespace: generatedNamespace.GetName(), 549 }, 550 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 551 InstallStrategy: newNginxInstallStrategy(genName("csv-"), nil, nil), 552 InstallModes: []operatorsv1alpha1.InstallMode{ 553 { 554 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 555 Supported: true, 556 }, 557 }, 558 }, 559 } 560 Expect(ctx.Ctx().Client().Create(context.Background(), &original)).To(Succeed()) 561 562 Eventually(func() error { 563 key := client.ObjectKeyFromObject(&original) 564 key.Namespace = target.GetName() 565 return ctx.Ctx().Client().Get(context.Background(), key, ©CSV) 566 }).Should(Succeed()) 567 }) 568 569 AfterEach(func() { 570 TeardownNamespace(target.GetName()) 571 }) 572 573 It("is synchronized with the original csv", func() { 574 Eventually(func() error { 575 key := client.ObjectKeyFromObject(©CSV) 576 577 key.Namespace = target.Name 578 if err := ctx.Ctx().Client().Get(context.Background(), key, ©CSV); err != nil { 579 return err 580 } 581 582 copyCSV.Status.LastUpdateTime = &metav1.Time{Time: time.Unix(1, 0)} 583 return ctx.Ctx().Client().Status().Update(context.Background(), ©CSV) 584 }).Should(Succeed()) 585 586 Eventually(func() (bool, error) { 587 key := client.ObjectKeyFromObject(&original) 588 589 if err := ctx.Ctx().Client().Get(context.Background(), key, &original); err != nil { 590 return false, err 591 } 592 593 key.Namespace = target.Name 594 if err := ctx.Ctx().Client().Get(context.Background(), key, ©CSV); err != nil { 595 return false, err 596 } 597 598 return original.Status.LastUpdateTime.Equal(copyCSV.Status.LastUpdateTime), nil 599 }).Should(BeTrue(), "Change to status of copy should have been reverted") 600 }) 601 }) 602 When("a csv requires a serviceaccount solely owned by a non-csv", func() { 603 var ( 604 cm corev1.ConfigMap 605 sa corev1.ServiceAccount 606 csv operatorsv1alpha1.ClusterServiceVersion 607 ) 608 609 BeforeEach(func() { 610 cm = corev1.ConfigMap{ 611 ObjectMeta: metav1.ObjectMeta{ 612 GenerateName: "cm-", 613 Namespace: generatedNamespace.GetName(), 614 }, 615 } 616 Expect(ctx.Ctx().Client().Create(context.Background(), &cm)).To(Succeed()) 617 618 sa = corev1.ServiceAccount{ 619 ObjectMeta: metav1.ObjectMeta{ 620 GenerateName: "sa-", 621 Namespace: generatedNamespace.GetName(), 622 OwnerReferences: []metav1.OwnerReference{ 623 { 624 Name: cm.GetName(), 625 APIVersion: corev1.SchemeGroupVersion.String(), 626 Kind: "ConfigMap", 627 UID: cm.GetUID(), 628 }, 629 }, 630 }, 631 } 632 Expect(ctx.Ctx().Client().Create(context.Background(), &sa)).To(Succeed()) 633 634 csv = operatorsv1alpha1.ClusterServiceVersion{ 635 TypeMeta: metav1.TypeMeta{ 636 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 637 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 638 }, 639 ObjectMeta: metav1.ObjectMeta{ 640 GenerateName: "csv-", 641 Namespace: generatedNamespace.GetName(), 642 }, 643 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 644 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 645 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 646 StrategySpec: operatorsv1alpha1.StrategyDetailsDeployment{ 647 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 648 { 649 Name: "foo", 650 Spec: appsv1.DeploymentSpec{ 651 Selector: &metav1.LabelSelector{ 652 MatchLabels: map[string]string{"app": "foo"}, 653 }, 654 Template: corev1.PodTemplateSpec{ 655 ObjectMeta: metav1.ObjectMeta{ 656 Labels: map[string]string{"app": "foo"}, 657 }, 658 Spec: corev1.PodSpec{Containers: []corev1.Container{ 659 { 660 Name: genName("foo"), 661 Image: *dummyImage, 662 }, 663 }}, 664 }, 665 }, 666 }, 667 }, 668 Permissions: []operatorsv1alpha1.StrategyDeploymentPermissions{ 669 { 670 ServiceAccountName: sa.GetName(), 671 Rules: []rbacv1.PolicyRule{}, 672 }, 673 }, 674 }, 675 }, 676 InstallModes: []operatorsv1alpha1.InstallMode{ 677 { 678 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 679 Supported: true, 680 }, 681 }, 682 }, 683 } 684 Expect(ctx.Ctx().Client().Create(context.Background(), &csv)).To(Succeed()) 685 }) 686 687 AfterEach(func() { 688 if cm.GetName() != "" { 689 Expect(ctx.Ctx().Client().Delete(context.Background(), &cm)).To(Succeed()) 690 } 691 }) 692 693 It("considers the serviceaccount requirement satisfied", func() { 694 Eventually(func() (operatorsv1alpha1.StatusReason, error) { 695 if err := ctx.Ctx().Client().Get(context.Background(), client.ObjectKeyFromObject(&csv), &csv); err != nil { 696 return "", err 697 } 698 for _, requirement := range csv.Status.RequirementStatus { 699 if requirement.Name != sa.GetName() { 700 continue 701 } 702 return requirement.Status, nil 703 } 704 return "", fmt.Errorf("missing expected requirement %q", sa.GetName()) 705 }).Should(Equal(operatorsv1alpha1.RequirementStatusReasonPresent)) 706 }) 707 }) 708 709 It("create with unmet requirements min kube version", func() { 710 711 depName := genName("dep-") 712 csv := operatorsv1alpha1.ClusterServiceVersion{ 713 TypeMeta: metav1.TypeMeta{ 714 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 715 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 716 }, 717 ObjectMeta: metav1.ObjectMeta{ 718 Name: genName("csv"), 719 }, 720 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 721 MinKubeVersion: "999.999.999", 722 InstallModes: []operatorsv1alpha1.InstallMode{ 723 { 724 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 725 Supported: true, 726 }, 727 { 728 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 729 Supported: true, 730 }, 731 { 732 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 733 Supported: true, 734 }, 735 { 736 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 737 Supported: true, 738 }, 739 }, 740 InstallStrategy: newNginxInstallStrategy(depName, nil, nil), 741 }, 742 } 743 744 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 745 Expect(err).ShouldNot(HaveOccurred()) 746 defer cleanupCSV() 747 748 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvPendingChecker) 749 Expect(err).ShouldNot(HaveOccurred()) 750 751 By("Shouldn't create deployment") 752 Consistently(func() bool { 753 _, err := c.GetDeployment(generatedNamespace.GetName(), depName) 754 return apierrors.IsNotFound(err) 755 }).Should(BeTrue()) 756 }) 757 // TODO: same test but missing serviceaccount instead 758 It("create with unmet requirements CRD", func() { 759 760 depName := genName("dep-") 761 csv := operatorsv1alpha1.ClusterServiceVersion{ 762 TypeMeta: metav1.TypeMeta{ 763 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 764 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 765 }, 766 ObjectMeta: metav1.ObjectMeta{ 767 Name: genName("csv"), 768 }, 769 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 770 MinKubeVersion: "0.0.0", 771 InstallModes: []operatorsv1alpha1.InstallMode{ 772 { 773 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 774 Supported: true, 775 }, 776 { 777 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 778 Supported: true, 779 }, 780 { 781 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 782 Supported: true, 783 }, 784 { 785 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 786 Supported: true, 787 }, 788 }, 789 790 InstallStrategy: newNginxInstallStrategy(depName, nil, nil), 791 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 792 Owned: []operatorsv1alpha1.CRDDescription{ 793 { 794 DisplayName: "Not In Cluster", 795 Description: "A CRD that is not currently in the cluster", 796 Name: "not.in.cluster.com", 797 Version: "v1alpha1", 798 Kind: "NotInCluster", 799 }, 800 }, 801 }, 802 }, 803 } 804 805 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 806 Expect(err).ShouldNot(HaveOccurred()) 807 defer cleanupCSV() 808 809 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvPendingChecker) 810 Expect(err).ShouldNot(HaveOccurred()) 811 812 By("Shouldn't create deployment") 813 Consistently(func() bool { 814 _, err := c.GetDeployment(generatedNamespace.GetName(), depName) 815 return apierrors.IsNotFound(err) 816 }).Should(BeTrue()) 817 }) 818 819 It("create with unmet permissions CRD", func() { 820 saName := genName("dep-") 821 permissions := []operatorsv1alpha1.StrategyDeploymentPermissions{ 822 { 823 ServiceAccountName: saName, 824 Rules: []rbacv1.PolicyRule{ 825 { 826 Verbs: []string{"create"}, 827 APIGroups: []string{""}, 828 Resources: []string{"deployment"}, 829 }, 830 }, 831 }, 832 } 833 834 clusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{ 835 { 836 ServiceAccountName: saName, 837 Rules: []rbacv1.PolicyRule{ 838 { 839 Verbs: []string{"get"}, 840 APIGroups: []string{""}, 841 Resources: []string{"deployment"}, 842 }, 843 }, 844 }, 845 } 846 847 crdPlural := genName("ins") 848 crdName := crdPlural + ".cluster.com" 849 depName := genName("dep-") 850 csv := operatorsv1alpha1.ClusterServiceVersion{ 851 TypeMeta: metav1.TypeMeta{ 852 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 853 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 854 }, 855 ObjectMeta: metav1.ObjectMeta{ 856 Name: genName("csv"), 857 }, 858 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 859 InstallModes: []operatorsv1alpha1.InstallMode{ 860 { 861 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 862 Supported: true, 863 }, 864 { 865 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 866 Supported: true, 867 }, 868 { 869 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 870 Supported: true, 871 }, 872 { 873 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 874 Supported: true, 875 }, 876 }, 877 InstallStrategy: newNginxInstallStrategy(depName, permissions, clusterPermissions), 878 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 879 Owned: []operatorsv1alpha1.CRDDescription{ 880 { 881 Name: crdName, 882 Version: "v1alpha1", 883 Kind: crdPlural, 884 DisplayName: crdName, 885 Description: crdName, 886 }, 887 }, 888 }, 889 }, 890 } 891 892 By("Create dependency first (CRD)") 893 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 894 ObjectMeta: metav1.ObjectMeta{ 895 Name: crdName, 896 }, 897 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 898 Group: "cluster.com", 899 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 900 { 901 Name: "v1alpha1", 902 Served: true, 903 Storage: true, 904 Schema: &apiextensionsv1.CustomResourceValidation{ 905 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 906 Type: "object", 907 Description: "my crd schema", 908 }, 909 }, 910 }, 911 }, 912 Names: apiextensionsv1.CustomResourceDefinitionNames{ 913 Plural: crdPlural, 914 Singular: crdPlural, 915 Kind: crdPlural, 916 ListKind: "list" + crdPlural, 917 }, 918 Scope: apiextensionsv1.NamespaceScoped, 919 }, 920 }) 921 Expect(err).ShouldNot(HaveOccurred()) 922 defer cleanupCRD() 923 924 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), true, false) 925 Expect(err).ShouldNot(HaveOccurred()) 926 defer cleanupCSV() 927 928 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvPendingChecker) 929 Expect(err).ShouldNot(HaveOccurred()) 930 931 By("Shouldn't create deployment") 932 Consistently(func() bool { 933 _, err := c.GetDeployment(generatedNamespace.GetName(), depName) 934 return apierrors.IsNotFound(err) 935 }).Should(BeTrue()) 936 }) 937 It("create with unmet requirements API service", func() { 938 939 depName := genName("dep-") 940 csv := operatorsv1alpha1.ClusterServiceVersion{ 941 TypeMeta: metav1.TypeMeta{ 942 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 943 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 944 }, 945 ObjectMeta: metav1.ObjectMeta{ 946 Name: genName("csv"), 947 }, 948 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 949 MinKubeVersion: "0.0.0", 950 InstallModes: []operatorsv1alpha1.InstallMode{ 951 { 952 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 953 Supported: true, 954 }, 955 { 956 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 957 Supported: true, 958 }, 959 { 960 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 961 Supported: true, 962 }, 963 { 964 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 965 Supported: true, 966 }, 967 }, 968 InstallStrategy: newNginxInstallStrategy(depName, nil, nil), 969 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 970 Required: []operatorsv1alpha1.APIServiceDescription{ 971 { 972 DisplayName: "Not In Cluster", 973 Description: "An apiservice that is not currently in the cluster", 974 Group: "not.in.cluster.com", 975 Version: "v1alpha1", 976 Kind: "NotInCluster", 977 }, 978 }, 979 }, 980 }, 981 } 982 983 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 984 Expect(err).ShouldNot(HaveOccurred()) 985 defer cleanupCSV() 986 987 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvPendingChecker) 988 Expect(err).ShouldNot(HaveOccurred()) 989 990 By("Shouldn't create deployment") 991 Consistently(func() bool { 992 _, err := c.GetDeployment(generatedNamespace.GetName(), depName) 993 return apierrors.IsNotFound(err) 994 }).Should(BeTrue()) 995 }) 996 It("create with unmet permissions API service", func() { 997 998 saName := genName("dep-") 999 permissions := []operatorsv1alpha1.StrategyDeploymentPermissions{ 1000 { 1001 ServiceAccountName: saName, 1002 Rules: []rbacv1.PolicyRule{ 1003 { 1004 Verbs: []string{"create"}, 1005 APIGroups: []string{""}, 1006 Resources: []string{"deployment"}, 1007 }, 1008 }, 1009 }, 1010 } 1011 1012 clusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{ 1013 { 1014 ServiceAccountName: saName, 1015 Rules: []rbacv1.PolicyRule{ 1016 { 1017 Verbs: []string{"get"}, 1018 APIGroups: []string{""}, 1019 Resources: []string{"deployment"}, 1020 }, 1021 }, 1022 }, 1023 } 1024 1025 depName := genName("dep-") 1026 csv := operatorsv1alpha1.ClusterServiceVersion{ 1027 TypeMeta: metav1.TypeMeta{ 1028 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 1029 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 1030 }, 1031 ObjectMeta: metav1.ObjectMeta{ 1032 Name: genName("csv"), 1033 }, 1034 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 1035 MinKubeVersion: "0.0.0", 1036 InstallModes: []operatorsv1alpha1.InstallMode{ 1037 { 1038 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 1039 Supported: true, 1040 }, 1041 { 1042 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 1043 Supported: true, 1044 }, 1045 { 1046 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 1047 Supported: true, 1048 }, 1049 { 1050 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 1051 Supported: true, 1052 }, 1053 }, 1054 InstallStrategy: newNginxInstallStrategy(depName, permissions, clusterPermissions), 1055 // "Cheating a little; this is an APIservice that will exist for the e2e tests" 1056 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 1057 Required: []operatorsv1alpha1.APIServiceDescription{ 1058 { 1059 Group: "packages.operators.coreos.com", 1060 Version: "v1", 1061 Kind: "PackageManifest", 1062 DisplayName: "Package Manifest", 1063 Description: "An apiservice that exists", 1064 }, 1065 }, 1066 }, 1067 }, 1068 } 1069 1070 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 1071 Expect(err).ShouldNot(HaveOccurred()) 1072 defer cleanupCSV() 1073 1074 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvPendingChecker) 1075 Expect(err).ShouldNot(HaveOccurred()) 1076 1077 By("Shouldn't create deployment") 1078 Consistently(func() bool { 1079 _, err := c.GetDeployment(generatedNamespace.GetName(), depName) 1080 return apierrors.IsNotFound(err) 1081 }).Should(BeTrue()) 1082 }) 1083 It("create with unmet requirements native API", func() { 1084 1085 depName := genName("dep-") 1086 csv := operatorsv1alpha1.ClusterServiceVersion{ 1087 TypeMeta: metav1.TypeMeta{ 1088 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 1089 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 1090 }, 1091 ObjectMeta: metav1.ObjectMeta{ 1092 Name: genName("csv"), 1093 }, 1094 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 1095 MinKubeVersion: "0.0.0", 1096 InstallModes: []operatorsv1alpha1.InstallMode{ 1097 { 1098 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 1099 Supported: true, 1100 }, 1101 { 1102 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 1103 Supported: true, 1104 }, 1105 { 1106 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 1107 Supported: true, 1108 }, 1109 { 1110 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 1111 Supported: true, 1112 }, 1113 }, 1114 InstallStrategy: newNginxInstallStrategy(depName, nil, nil), 1115 NativeAPIs: []metav1.GroupVersionKind{{Group: "kubenative.io", Version: "v1", Kind: "Native"}}, 1116 }, 1117 } 1118 1119 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 1120 Expect(err).ShouldNot(HaveOccurred()) 1121 defer cleanupCSV() 1122 1123 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvPendingChecker) 1124 Expect(err).ShouldNot(HaveOccurred()) 1125 1126 By("Shouldn't create deployment") 1127 Consistently(func() bool { 1128 _, err := c.GetDeployment(generatedNamespace.GetName(), depName) 1129 return apierrors.IsNotFound(err) 1130 }).Should(BeTrue()) 1131 }) 1132 // TODO: same test but create serviceaccount instead 1133 It("create requirements met CRD", func() { 1134 1135 saName := genName("sa-") 1136 permissions := []operatorsv1alpha1.StrategyDeploymentPermissions{ 1137 { 1138 ServiceAccountName: saName, 1139 Rules: []rbacv1.PolicyRule{ 1140 { 1141 Verbs: []string{"create"}, 1142 APIGroups: []string{""}, 1143 Resources: []string{"deployment"}, 1144 }, 1145 }, 1146 }, 1147 } 1148 1149 clusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{ 1150 { 1151 ServiceAccountName: saName, 1152 Rules: []rbacv1.PolicyRule{ 1153 { 1154 Verbs: []string{"get"}, 1155 APIGroups: []string{""}, 1156 Resources: []string{"deployment"}, 1157 }, 1158 { 1159 Verbs: []string{"put", "post", "get"}, 1160 NonResourceURLs: []string{"/osb", "/osb/*"}, 1161 }, 1162 }, 1163 }, 1164 } 1165 1166 crdPlural := genName("ins") 1167 crdName := crdPlural + ".cluster.com" 1168 depName := genName("dep-") 1169 csv := operatorsv1alpha1.ClusterServiceVersion{ 1170 TypeMeta: metav1.TypeMeta{ 1171 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 1172 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 1173 }, 1174 ObjectMeta: metav1.ObjectMeta{ 1175 Name: genName("csv"), 1176 }, 1177 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 1178 MinKubeVersion: "0.0.0", 1179 InstallModes: []operatorsv1alpha1.InstallMode{ 1180 { 1181 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 1182 Supported: true, 1183 }, 1184 { 1185 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 1186 Supported: true, 1187 }, 1188 { 1189 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 1190 Supported: true, 1191 }, 1192 { 1193 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 1194 Supported: true, 1195 }, 1196 }, 1197 InstallStrategy: newNginxInstallStrategy(depName, permissions, clusterPermissions), 1198 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 1199 Owned: []operatorsv1alpha1.CRDDescription{ 1200 { 1201 Name: crdName, 1202 Version: "v1alpha1", 1203 Kind: crdPlural, 1204 DisplayName: crdName, 1205 Description: crdName, 1206 }, 1207 }, 1208 }, 1209 }, 1210 } 1211 1212 By("Create CSV first, knowing it will fail") 1213 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), true, false) 1214 Expect(err).ShouldNot(HaveOccurred()) 1215 defer cleanupCSV() 1216 1217 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvPendingChecker) 1218 Expect(err).ShouldNot(HaveOccurred()) 1219 1220 sa := corev1.ServiceAccount{} 1221 sa.SetName(saName) 1222 sa.SetNamespace(generatedNamespace.GetName()) 1223 sa.SetOwnerReferences([]metav1.OwnerReference{{ 1224 Name: fetchedCSV.GetName(), 1225 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 1226 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 1227 UID: fetchedCSV.GetUID(), 1228 }}) 1229 _, err = c.CreateServiceAccount(&sa) 1230 Expect(err).ShouldNot(HaveOccurred(), "could not create ServiceAccount %#v", sa) 1231 1232 crd := apiextensionsv1.CustomResourceDefinition{ 1233 ObjectMeta: metav1.ObjectMeta{ 1234 Name: crdName, 1235 }, 1236 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 1237 Group: "cluster.com", 1238 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 1239 { 1240 Name: "v1alpha1", 1241 Served: true, 1242 Storage: true, 1243 Schema: &apiextensionsv1.CustomResourceValidation{ 1244 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 1245 Type: "object", 1246 Description: "my crd schema", 1247 }, 1248 }, 1249 }, 1250 }, 1251 Names: apiextensionsv1.CustomResourceDefinitionNames{ 1252 Plural: crdPlural, 1253 Singular: crdPlural, 1254 Kind: crdPlural, 1255 ListKind: "list" + crdPlural, 1256 }, 1257 Scope: apiextensionsv1.NamespaceScoped, 1258 }, 1259 } 1260 crd.SetOwnerReferences([]metav1.OwnerReference{{ 1261 Name: fetchedCSV.GetName(), 1262 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 1263 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 1264 UID: fetchedCSV.GetUID(), 1265 }}) 1266 cleanupCRD, err := createCRD(c, crd) 1267 defer cleanupCRD() 1268 Expect(err).ShouldNot(HaveOccurred()) 1269 1270 By("Create Role/Cluster Roles and RoleBindings") 1271 role := rbacv1.Role{ 1272 Rules: []rbacv1.PolicyRule{ 1273 { 1274 Verbs: []string{"create"}, 1275 APIGroups: []string{""}, 1276 Resources: []string{"deployment"}, 1277 }, 1278 }, 1279 } 1280 role.SetName(genName("dep-")) 1281 role.SetNamespace(generatedNamespace.GetName()) 1282 _, err = c.CreateRole(&role) 1283 Expect(err).ShouldNot(HaveOccurred(), "could not create Role") 1284 1285 roleBinding := rbacv1.RoleBinding{ 1286 Subjects: []rbacv1.Subject{ 1287 { 1288 Kind: "ServiceAccount", 1289 APIGroup: "", 1290 Name: sa.GetName(), 1291 Namespace: sa.GetNamespace(), 1292 }, 1293 }, 1294 RoleRef: rbacv1.RoleRef{ 1295 APIGroup: "rbac.authorization.k8s.io", 1296 Kind: "Role", 1297 Name: role.GetName(), 1298 }, 1299 } 1300 roleBinding.SetName(genName("dep-")) 1301 roleBinding.SetNamespace(generatedNamespace.GetName()) 1302 _, err = c.CreateRoleBinding(&roleBinding) 1303 Expect(err).ShouldNot(HaveOccurred(), "could not create RoleBinding") 1304 1305 clusterRole := rbacv1.ClusterRole{ 1306 Rules: []rbacv1.PolicyRule{ 1307 { 1308 Verbs: []string{"get"}, 1309 APIGroups: []string{""}, 1310 Resources: []string{"deployment"}, 1311 }, 1312 }, 1313 } 1314 clusterRole.SetName(genName("dep-")) 1315 _, err = c.CreateClusterRole(&clusterRole) 1316 Expect(err).ShouldNot(HaveOccurred(), "could not create ClusterRole") 1317 1318 nonResourceClusterRole := rbacv1.ClusterRole{ 1319 Rules: []rbacv1.PolicyRule{ 1320 { 1321 Verbs: []string{"put", "post", "get"}, 1322 NonResourceURLs: []string{"/osb", "/osb/*"}, 1323 }, 1324 }, 1325 } 1326 nonResourceClusterRole.SetName(genName("dep-")) 1327 _, err = c.CreateClusterRole(&nonResourceClusterRole) 1328 Expect(err).ShouldNot(HaveOccurred(), "could not create ClusterRole") 1329 1330 clusterRoleBinding := rbacv1.ClusterRoleBinding{ 1331 Subjects: []rbacv1.Subject{ 1332 { 1333 Kind: "ServiceAccount", 1334 APIGroup: "", 1335 Name: sa.GetName(), 1336 Namespace: sa.GetNamespace(), 1337 }, 1338 }, 1339 RoleRef: rbacv1.RoleRef{ 1340 APIGroup: "rbac.authorization.k8s.io", 1341 Kind: "ClusterRole", 1342 Name: clusterRole.GetName(), 1343 }, 1344 } 1345 clusterRoleBinding.SetName(genName("dep-")) 1346 _, err = c.CreateClusterRoleBinding(&clusterRoleBinding) 1347 Expect(err).ShouldNot(HaveOccurred(), "could not create ClusterRoleBinding") 1348 1349 nonResourceClusterRoleBinding := rbacv1.ClusterRoleBinding{ 1350 Subjects: []rbacv1.Subject{ 1351 { 1352 Kind: "ServiceAccount", 1353 APIGroup: "", 1354 Name: sa.GetName(), 1355 Namespace: sa.GetNamespace(), 1356 }, 1357 }, 1358 RoleRef: rbacv1.RoleRef{ 1359 APIGroup: "rbac.authorization.k8s.io", 1360 Kind: "ClusterRole", 1361 Name: nonResourceClusterRole.GetName(), 1362 }, 1363 } 1364 nonResourceClusterRoleBinding.SetName(genName("dep-")) 1365 _, err = c.CreateClusterRoleBinding(&nonResourceClusterRoleBinding) 1366 Expect(err).ShouldNot(HaveOccurred(), "could not create ClusterRoleBinding") 1367 1368 ctx.Ctx().Logf("checking for deployment") 1369 By("Poll for deployment to be ready") 1370 Eventually(func() (bool, error) { 1371 dep, err := c.GetDeployment(generatedNamespace.GetName(), depName) 1372 if apierrors.IsNotFound(err) { 1373 ctx.Ctx().Logf("deployment %s not found\n", depName) 1374 return false, nil 1375 } else if err != nil { 1376 ctx.Ctx().Logf("unexpected error fetching deployment %s\n", depName) 1377 return false, err 1378 } 1379 if dep.Status.UpdatedReplicas == *(dep.Spec.Replicas) && 1380 dep.Status.Replicas == *(dep.Spec.Replicas) && 1381 dep.Status.AvailableReplicas == *(dep.Spec.Replicas) { 1382 ctx.Ctx().Logf("deployment ready") 1383 return true, nil 1384 } 1385 ctx.Ctx().Logf("deployment not ready") 1386 return false, nil 1387 }).Should(BeTrue()) 1388 1389 fetchedCSV, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 1390 Expect(err).ShouldNot(HaveOccurred()) 1391 1392 By("Delete CRD") 1393 cleanupCRD() 1394 1395 By("Wait for CSV failure") 1396 fetchedCSV, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvPendingChecker) 1397 Expect(err).ShouldNot(HaveOccurred()) 1398 1399 By("Recreate the CRD") 1400 cleanupCRD, err = createCRD(c, crd) 1401 Expect(err).ShouldNot(HaveOccurred()) 1402 defer cleanupCRD() 1403 1404 By("Wait for CSV success again") 1405 fetchedCSV, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 1406 Expect(err).ShouldNot(HaveOccurred()) 1407 }) 1408 It("create requirements met API service", func() { 1409 1410 sa := corev1.ServiceAccount{} 1411 sa.SetName(genName("sa-")) 1412 sa.SetNamespace(generatedNamespace.GetName()) 1413 _, err := c.CreateServiceAccount(&sa) 1414 Expect(err).ShouldNot(HaveOccurred(), "could not create ServiceAccount") 1415 1416 permissions := []operatorsv1alpha1.StrategyDeploymentPermissions{ 1417 { 1418 ServiceAccountName: sa.GetName(), 1419 Rules: []rbacv1.PolicyRule{ 1420 { 1421 Verbs: []string{"create"}, 1422 APIGroups: []string{""}, 1423 Resources: []string{"deployment"}, 1424 }, 1425 }, 1426 }, 1427 } 1428 1429 clusterPermissions := []operatorsv1alpha1.StrategyDeploymentPermissions{ 1430 { 1431 ServiceAccountName: sa.GetName(), 1432 Rules: []rbacv1.PolicyRule{ 1433 { 1434 Verbs: []string{"get"}, 1435 APIGroups: []string{""}, 1436 Resources: []string{"deployment"}, 1437 }, 1438 }, 1439 }, 1440 } 1441 1442 depName := genName("dep-") 1443 csv := operatorsv1alpha1.ClusterServiceVersion{ 1444 TypeMeta: metav1.TypeMeta{ 1445 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 1446 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 1447 }, 1448 ObjectMeta: metav1.ObjectMeta{ 1449 Name: genName("csv"), 1450 }, 1451 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 1452 MinKubeVersion: "0.0.0", 1453 InstallModes: []operatorsv1alpha1.InstallMode{ 1454 { 1455 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 1456 Supported: true, 1457 }, 1458 { 1459 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 1460 Supported: true, 1461 }, 1462 { 1463 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 1464 Supported: true, 1465 }, 1466 { 1467 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 1468 Supported: true, 1469 }, 1470 }, 1471 InstallStrategy: newNginxInstallStrategy(depName, permissions, clusterPermissions), 1472 // "Cheating a little; this is an APIservice that will exist for the e2e tests" 1473 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 1474 Required: []operatorsv1alpha1.APIServiceDescription{ 1475 { 1476 Group: "packages.operators.coreos.com", 1477 Version: "v1", 1478 Kind: "PackageManifest", 1479 DisplayName: "Package Manifest", 1480 Description: "An apiservice that exists", 1481 }, 1482 }, 1483 }, 1484 }, 1485 } 1486 1487 By("Create Role/Cluster Roles and RoleBindings") 1488 role := rbacv1.Role{ 1489 Rules: []rbacv1.PolicyRule{ 1490 { 1491 Verbs: []string{"create"}, 1492 APIGroups: []string{""}, 1493 Resources: []string{"deployment"}, 1494 }, 1495 }, 1496 } 1497 role.SetName(genName("dep-")) 1498 role.SetNamespace(generatedNamespace.GetName()) 1499 _, err = c.CreateRole(&role) 1500 Expect(err).ShouldNot(HaveOccurred(), "could not create Role") 1501 1502 roleBinding := rbacv1.RoleBinding{ 1503 Subjects: []rbacv1.Subject{ 1504 { 1505 Kind: "ServiceAccount", 1506 APIGroup: "", 1507 Name: sa.GetName(), 1508 Namespace: sa.GetNamespace(), 1509 }, 1510 }, 1511 RoleRef: rbacv1.RoleRef{ 1512 APIGroup: "rbac.authorization.k8s.io", 1513 Kind: "Role", 1514 Name: role.GetName(), 1515 }, 1516 } 1517 roleBinding.SetName(genName("dep-")) 1518 roleBinding.SetNamespace(generatedNamespace.GetName()) 1519 _, err = c.CreateRoleBinding(&roleBinding) 1520 Expect(err).ShouldNot(HaveOccurred(), "could not create RoleBinding") 1521 1522 clusterRole := rbacv1.ClusterRole{ 1523 Rules: []rbacv1.PolicyRule{ 1524 { 1525 Verbs: []string{"get"}, 1526 APIGroups: []string{""}, 1527 Resources: []string{"deployment"}, 1528 }, 1529 }, 1530 } 1531 clusterRole.SetName(genName("dep-")) 1532 _, err = c.CreateClusterRole(&clusterRole) 1533 Expect(err).ShouldNot(HaveOccurred(), "could not create ClusterRole") 1534 1535 clusterRoleBinding := rbacv1.ClusterRoleBinding{ 1536 Subjects: []rbacv1.Subject{ 1537 { 1538 Kind: "ServiceAccount", 1539 APIGroup: "", 1540 Name: sa.GetName(), 1541 Namespace: sa.GetNamespace(), 1542 }, 1543 }, 1544 RoleRef: rbacv1.RoleRef{ 1545 APIGroup: "rbac.authorization.k8s.io", 1546 Kind: "ClusterRole", 1547 Name: clusterRole.GetName(), 1548 }, 1549 } 1550 clusterRoleBinding.SetName(genName("dep-")) 1551 _, err = c.CreateClusterRoleBinding(&clusterRoleBinding) 1552 Expect(err).ShouldNot(HaveOccurred(), "could not create ClusterRoleBinding") 1553 1554 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 1555 Expect(err).ShouldNot(HaveOccurred()) 1556 defer cleanupCSV() 1557 1558 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 1559 Expect(err).ShouldNot(HaveOccurred()) 1560 1561 By("Fetch cluster service version again to check for unnecessary control loops") 1562 sameCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 1563 Expect(err).ShouldNot(HaveOccurred()) 1564 Expect(equality.Semantic.DeepEqual(fetchedCSV, sameCSV)).Should(BeTrue(), diff.ObjectDiff(fetchedCSV, sameCSV)) 1565 }) 1566 It("create with owned API service", func() { 1567 1568 depName := genName("hat-server") 1569 mockGroup := fmt.Sprintf("hats.%s.redhat.com", genName("")) 1570 version := "v1alpha1" 1571 mockGroupVersion := strings.Join([]string{mockGroup, version}, "/") 1572 mockKinds := []string{"fez", "fedora"} 1573 depSpec := newMockExtServerDeployment(depName, []mockGroupVersionKind{{depName, mockGroupVersion, mockKinds, 5443}}) 1574 apiServiceName := strings.Join([]string{version, mockGroup}, ".") 1575 1576 By("Create CSV for the package-server") 1577 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 1578 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 1579 { 1580 Name: depName, 1581 Spec: depSpec, 1582 }, 1583 }, 1584 } 1585 1586 owned := make([]operatorsv1alpha1.APIServiceDescription, len(mockKinds)) 1587 for i, kind := range mockKinds { 1588 owned[i] = operatorsv1alpha1.APIServiceDescription{ 1589 Name: apiServiceName, 1590 Group: mockGroup, 1591 Version: version, 1592 Kind: kind, 1593 DeploymentName: depName, 1594 ContainerPort: int32(5443), 1595 DisplayName: kind, 1596 Description: fmt.Sprintf("A %s", kind), 1597 } 1598 } 1599 1600 csv := operatorsv1alpha1.ClusterServiceVersion{ 1601 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 1602 MinKubeVersion: "0.0.0", 1603 InstallModes: []operatorsv1alpha1.InstallMode{ 1604 { 1605 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 1606 Supported: true, 1607 }, 1608 { 1609 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 1610 Supported: true, 1611 }, 1612 { 1613 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 1614 Supported: true, 1615 }, 1616 { 1617 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 1618 Supported: true, 1619 }, 1620 }, 1621 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 1622 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 1623 StrategySpec: strategy, 1624 }, 1625 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 1626 Owned: owned, 1627 }, 1628 }, 1629 } 1630 csv.SetName(depName) 1631 1632 By("Create the APIService CSV") 1633 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 1634 Expect(err).ShouldNot(HaveOccurred()) 1635 defer func() { 1636 watcher, err := c.ApiregistrationV1Interface().ApiregistrationV1().APIServices().Watch(context.TODO(), metav1.ListOptions{FieldSelector: "metadata.name=" + apiServiceName}) 1637 Expect(err).ShouldNot(HaveOccurred()) 1638 1639 deleted := make(chan struct{}) 1640 go func() { 1641 defer GinkgoRecover() 1642 events := watcher.ResultChan() 1643 for { 1644 select { 1645 case evt := <-events: 1646 if evt.Type == watch.Deleted { 1647 deleted <- struct{}{} 1648 return 1649 } 1650 case <-time.After(pollDuration): 1651 Fail("API service not cleaned up after CSV deleted") 1652 } 1653 } 1654 }() 1655 1656 cleanupCSV() 1657 <-deleted 1658 }() 1659 1660 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 1661 Expect(err).ShouldNot(HaveOccurred()) 1662 1663 By("Should create Deployment") 1664 dep, err := c.GetDeployment(generatedNamespace.GetName(), depName) 1665 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Deployment") 1666 1667 By("Should create APIService") 1668 apiService, err := c.GetAPIService(apiServiceName) 1669 Expect(err).ShouldNot(HaveOccurred(), "error getting expected APIService") 1670 1671 By("Should create Service") 1672 serviceName := fmt.Sprintf("%s-service", depName) 1673 _, err = c.GetService(generatedNamespace.GetName(), serviceName) 1674 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Service") 1675 1676 By("Should create certificate Secret") 1677 secretName := fmt.Sprintf("%s-cert", serviceName) 1678 _, err = c.GetSecret(generatedNamespace.GetName(), secretName) 1679 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Secret") 1680 1681 By("Should create a Role for the Secret") 1682 _, err = c.GetRole(generatedNamespace.GetName(), secretName) 1683 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Secret Role") 1684 1685 By("Should create a RoleBinding for the Secret") 1686 _, err = c.GetRoleBinding(generatedNamespace.GetName(), secretName) 1687 Expect(err).ShouldNot(HaveOccurred(), "error getting exptected Secret RoleBinding") 1688 1689 By("Should create a system:auth-delegator Cluster RoleBinding") 1690 _, err = c.GetClusterRoleBinding(fmt.Sprintf("%s-system:auth-delegator", serviceName)) 1691 Expect(err).ShouldNot(HaveOccurred(), "error getting expected system:auth-delegator ClusterRoleBinding") 1692 1693 By("Should create an extension-apiserver-authentication-reader RoleBinding in kube-system") 1694 _, err = c.GetRoleBinding("kube-system", fmt.Sprintf("%s-auth-reader", serviceName)) 1695 Expect(err).ShouldNot(HaveOccurred(), "error getting expected extension-apiserver-authentication-reader RoleBinding") 1696 1697 By("Store the ca sha annotation") 1698 oldCAAnnotation, ok := dep.Spec.Template.GetAnnotations()[install.OLMCAHashAnnotationKey] 1699 Expect(ok).Should(BeTrue(), "expected olm sha annotation not present on existing pod template") 1700 1701 caSecret, err := c.KubernetesInterface().CoreV1().Secrets(generatedNamespace.GetName()).Get(context.TODO(), secretName, metav1.GetOptions{}) 1702 Expect(err).Should(BeNil()) 1703 1704 caPEM, certPEM, privPEM := generateExpiredCerts(install.HostnamesForService(serviceName, generatedNamespace.GetName())) 1705 By(`Induce a cert rotation`) 1706 Eventually(Apply(caSecret, func(caSecret *corev1.Secret) error { 1707 caSecret.Data[install.OLMCAPEMKey] = caPEM 1708 caSecret.Data["tls.crt"] = certPEM 1709 caSecret.Data["tls.key"] = privPEM 1710 return nil 1711 })).Should(Succeed()) 1712 1713 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, func(csv *operatorsv1alpha1.ClusterServiceVersion) bool { 1714 By("Should create deployment") 1715 dep, err = c.GetDeployment(generatedNamespace.GetName(), depName) 1716 if err != nil { 1717 return false 1718 } 1719 1720 By("Should have a new ca hash annotation") 1721 newCAAnnotation, ok := dep.Spec.Template.GetAnnotations()[install.OLMCAHashAnnotationKey] 1722 if !ok { 1723 ctx.Ctx().Logf("expected olm sha annotation not present in new pod template") 1724 return false 1725 } 1726 1727 if newCAAnnotation != oldCAAnnotation { 1728 By("Check for success") 1729 return csvSucceededChecker(csv) 1730 } 1731 1732 return false 1733 }) 1734 Expect(err).ShouldNot(HaveOccurred(), "failed to rotate cert") 1735 1736 By("Get the APIService UID") 1737 oldAPIServiceUID := apiService.GetUID() 1738 1739 By("Delete the APIService") 1740 err = c.DeleteAPIService(apiServiceName, &metav1.DeleteOptions{}) 1741 Expect(err).ShouldNot(HaveOccurred()) 1742 1743 By("Wait for CSV success") 1744 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.GetName(), func(csv *operatorsv1alpha1.ClusterServiceVersion) bool { 1745 By("Should create an APIService") 1746 apiService, err := c.GetAPIService(apiServiceName) 1747 if err != nil { 1748 return false 1749 } 1750 1751 if csvSucceededChecker(csv) { 1752 return apiService.GetUID() != oldAPIServiceUID 1753 } 1754 return false 1755 }) 1756 Expect(err).ShouldNot(HaveOccurred()) 1757 }) 1758 It("update with owned API service", func() { 1759 1760 depName := genName("hat-server") 1761 mockGroup := fmt.Sprintf("hats.%s.redhat.com", genName("")) 1762 version := "v1alpha1" 1763 mockGroupVersion := strings.Join([]string{mockGroup, version}, "/") 1764 mockKinds := []string{"fedora"} 1765 depSpec := newMockExtServerDeployment(depName, []mockGroupVersionKind{{depName, mockGroupVersion, mockKinds, 5443}}) 1766 apiServiceName := strings.Join([]string{version, mockGroup}, ".") 1767 1768 By("Create CSVs for the hat-server") 1769 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 1770 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 1771 { 1772 Name: depName, 1773 Spec: depSpec, 1774 }, 1775 }, 1776 } 1777 1778 owned := make([]operatorsv1alpha1.APIServiceDescription, len(mockKinds)) 1779 for i, kind := range mockKinds { 1780 owned[i] = operatorsv1alpha1.APIServiceDescription{ 1781 Name: apiServiceName, 1782 Group: mockGroup, 1783 Version: version, 1784 Kind: kind, 1785 DeploymentName: depName, 1786 ContainerPort: int32(5443), 1787 DisplayName: kind, 1788 Description: fmt.Sprintf("A %s", kind), 1789 } 1790 } 1791 1792 csv := operatorsv1alpha1.ClusterServiceVersion{ 1793 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 1794 MinKubeVersion: "0.0.0", 1795 InstallModes: []operatorsv1alpha1.InstallMode{ 1796 { 1797 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 1798 Supported: true, 1799 }, 1800 { 1801 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 1802 Supported: true, 1803 }, 1804 { 1805 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 1806 Supported: true, 1807 }, 1808 { 1809 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 1810 Supported: true, 1811 }, 1812 }, 1813 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 1814 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 1815 StrategySpec: strategy, 1816 }, 1817 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 1818 Owned: owned, 1819 }, 1820 }, 1821 } 1822 csv.SetName("csv-hat-1") 1823 1824 By("Create the APIService CSV") 1825 _, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 1826 Expect(err).ShouldNot(HaveOccurred()) 1827 1828 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 1829 Expect(err).ShouldNot(HaveOccurred()) 1830 1831 By("Should create Deployment") 1832 _, err = c.GetDeployment(generatedNamespace.GetName(), depName) 1833 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Deployment") 1834 1835 By("Should create APIService") 1836 _, err = c.GetAPIService(apiServiceName) 1837 Expect(err).ShouldNot(HaveOccurred(), "error getting expected APIService") 1838 1839 By("Should create Service") 1840 serviceName := fmt.Sprintf("%s-service", depName) 1841 _, err = c.GetService(generatedNamespace.GetName(), serviceName) 1842 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Service") 1843 1844 By("Should create certificate Secret") 1845 secretName := fmt.Sprintf("%s-cert", serviceName) 1846 _, err = c.GetSecret(generatedNamespace.GetName(), secretName) 1847 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Secret") 1848 1849 By("Should create a Role for the Secret") 1850 _, err = c.GetRole(generatedNamespace.GetName(), secretName) 1851 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Secret Role") 1852 1853 By("Should create a RoleBinding for the Secret") 1854 _, err = c.GetRoleBinding(generatedNamespace.GetName(), secretName) 1855 Expect(err).ShouldNot(HaveOccurred(), "error getting exptected Secret RoleBinding") 1856 1857 By("Should create a system:auth-delegator Cluster RoleBinding") 1858 _, err = c.GetClusterRoleBinding(fmt.Sprintf("%s-system:auth-delegator", serviceName)) 1859 Expect(err).ShouldNot(HaveOccurred(), "error getting expected system:auth-delegator ClusterRoleBinding") 1860 1861 By("Should create an extension-apiserver-authentication-reader RoleBinding in kube-system") 1862 _, err = c.GetRoleBinding("kube-system", fmt.Sprintf("%s-auth-reader", serviceName)) 1863 Expect(err).ShouldNot(HaveOccurred(), "error getting expected extension-apiserver-authentication-reader RoleBinding") 1864 1865 By("Create a new CSV that owns the same API Service and replace the old CSV") 1866 csv2 := operatorsv1alpha1.ClusterServiceVersion{ 1867 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 1868 Replaces: csv.Name, 1869 MinKubeVersion: "0.0.0", 1870 InstallModes: []operatorsv1alpha1.InstallMode{ 1871 { 1872 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 1873 Supported: true, 1874 }, 1875 { 1876 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 1877 Supported: true, 1878 }, 1879 { 1880 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 1881 Supported: true, 1882 }, 1883 { 1884 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 1885 Supported: true, 1886 }, 1887 }, 1888 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 1889 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 1890 StrategySpec: strategy, 1891 }, 1892 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 1893 Owned: owned, 1894 }, 1895 }, 1896 } 1897 csv2.SetName("csv-hat-2") 1898 1899 By("Create CSV2 to replace CSV") 1900 cleanupCSV2, err := createCSV(c, crc, csv2, generatedNamespace.GetName(), false, true) 1901 Expect(err).ShouldNot(HaveOccurred()) 1902 defer cleanupCSV2() 1903 1904 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv2.Name, csvSucceededChecker) 1905 Expect(err).ShouldNot(HaveOccurred()) 1906 1907 By("Should create Deployment") 1908 _, err = c.GetDeployment(generatedNamespace.GetName(), depName) 1909 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Deployment") 1910 1911 By("Should create APIService") 1912 _, err = c.GetAPIService(apiServiceName) 1913 Expect(err).ShouldNot(HaveOccurred(), "error getting expected APIService") 1914 1915 By("Should create Service") 1916 Eventually(func() error { 1917 _, err := c.GetService(generatedNamespace.GetName(), serviceName) 1918 return err 1919 }, timeout, interval).ShouldNot(HaveOccurred()) 1920 1921 By("Should create certificate Secret") 1922 secretName = fmt.Sprintf("%s-cert", serviceName) 1923 Eventually(func() error { 1924 _, err = c.GetSecret(generatedNamespace.GetName(), secretName) 1925 return err 1926 }, timeout, interval).ShouldNot(HaveOccurred()) 1927 1928 By("Should create a Role for the Secret") 1929 _, err = c.GetRole(generatedNamespace.GetName(), secretName) 1930 Eventually(func() error { 1931 _, err = c.GetRole(generatedNamespace.GetName(), secretName) 1932 return err 1933 }, timeout, interval).ShouldNot(HaveOccurred()) 1934 1935 By("Should create a RoleBinding for the Secret") 1936 Eventually(func() error { 1937 _, err = c.GetRoleBinding(generatedNamespace.GetName(), secretName) 1938 return err 1939 }, timeout, interval).ShouldNot(HaveOccurred()) 1940 1941 By("Should create a system:auth-delegator Cluster RoleBinding") 1942 Eventually(func() error { 1943 _, err = c.GetClusterRoleBinding(fmt.Sprintf("%s-system:auth-delegator", serviceName)) 1944 return err 1945 }, timeout, interval).ShouldNot(HaveOccurred()) 1946 1947 By("Should create an extension-apiserver-authentication-reader RoleBinding in kube-system") 1948 Eventually(func() error { 1949 _, err = c.GetRoleBinding("kube-system", fmt.Sprintf("%s-auth-reader", serviceName)) 1950 return err 1951 }, timeout, interval).ShouldNot(HaveOccurred()) 1952 Expect(err).ShouldNot(HaveOccurred(), "error getting expected extension-apiserver-authentication-reader RoleBinding") 1953 1954 By("Should eventually GC the CSV") 1955 err = waitForCsvToDelete(generatedNamespace.GetName(), csv.Name, crc) 1956 Expect(err).ShouldNot(HaveOccurred()) 1957 1958 By("Rename the initial CSV") 1959 csv.SetName("csv-hat-3") 1960 1961 By("Recreate the old CSV") 1962 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, true) 1963 Expect(err).ShouldNot(HaveOccurred()) 1964 defer cleanupCSV() 1965 1966 fetched, err := fetchCSV(crc, generatedNamespace.GetName(), csv.Name, buildCSVReasonChecker(operatorsv1alpha1.CSVReasonOwnerConflict)) 1967 Expect(err).ShouldNot(HaveOccurred()) 1968 Expect(fetched.Status.Phase).Should(Equal(operatorsv1alpha1.CSVPhaseFailed)) 1969 }) 1970 It("create same CSV with owned API service multi namespace", func() { 1971 1972 By("Create new namespace in a new operator group") 1973 secondNamespaceName := genName(generatedNamespace.GetName() + "-") 1974 matchingLabel := map[string]string{"inGroup": secondNamespaceName} 1975 1976 _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 1977 ObjectMeta: metav1.ObjectMeta{ 1978 Name: secondNamespaceName, 1979 Labels: matchingLabel, 1980 }, 1981 }, metav1.CreateOptions{}) 1982 Expect(err).ShouldNot(HaveOccurred()) 1983 defer func() { 1984 err = c.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), secondNamespaceName, metav1.DeleteOptions{}) 1985 Expect(err).ShouldNot(HaveOccurred()) 1986 }() 1987 1988 By("Create a new operator group for the new namespace") 1989 operatorGroup := operatorsv1.OperatorGroup{ 1990 ObjectMeta: metav1.ObjectMeta{ 1991 Name: genName("e2e-operator-group-"), 1992 Namespace: secondNamespaceName, 1993 }, 1994 Spec: operatorsv1.OperatorGroupSpec{ 1995 Selector: &metav1.LabelSelector{ 1996 MatchLabels: matchingLabel, 1997 }, 1998 }, 1999 } 2000 _, err = crc.OperatorsV1().OperatorGroups(secondNamespaceName).Create(context.TODO(), &operatorGroup, metav1.CreateOptions{}) 2001 Expect(err).ShouldNot(HaveOccurred()) 2002 defer func() { 2003 err = crc.OperatorsV1().OperatorGroups(secondNamespaceName).Delete(context.TODO(), operatorGroup.Name, metav1.DeleteOptions{}) 2004 Expect(err).ShouldNot(HaveOccurred()) 2005 }() 2006 2007 ctx.Ctx().Logf("Waiting on new operator group to have correct status") 2008 Eventually(func() ([]string, error) { 2009 og, err := crc.OperatorsV1().OperatorGroups(secondNamespaceName).Get(context.TODO(), operatorGroup.Name, metav1.GetOptions{}) 2010 if err != nil { 2011 return nil, err 2012 } 2013 return og.Status.Namespaces, nil 2014 }).Should(ConsistOf([]string{secondNamespaceName})) 2015 2016 depName := genName("hat-server") 2017 mockGroup := fmt.Sprintf("hats.%s.redhat.com", genName("")) 2018 version := "v1alpha1" 2019 mockGroupVersion := strings.Join([]string{mockGroup, version}, "/") 2020 mockKinds := []string{"fedora"} 2021 depSpec := newMockExtServerDeployment(depName, []mockGroupVersionKind{{depName, mockGroupVersion, mockKinds, 5443}}) 2022 apiServiceName := strings.Join([]string{version, mockGroup}, ".") 2023 2024 By("Create CSVs for the hat-server") 2025 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 2026 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2027 { 2028 Name: depName, 2029 Spec: depSpec, 2030 }, 2031 }, 2032 } 2033 2034 owned := make([]operatorsv1alpha1.APIServiceDescription, len(mockKinds)) 2035 for i, kind := range mockKinds { 2036 owned[i] = operatorsv1alpha1.APIServiceDescription{ 2037 Name: apiServiceName, 2038 Group: mockGroup, 2039 Version: version, 2040 Kind: kind, 2041 DeploymentName: depName, 2042 ContainerPort: int32(5443), 2043 DisplayName: kind, 2044 Description: fmt.Sprintf("A %s", kind), 2045 } 2046 } 2047 2048 csv := operatorsv1alpha1.ClusterServiceVersion{ 2049 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2050 MinKubeVersion: "0.0.0", 2051 InstallModes: []operatorsv1alpha1.InstallMode{ 2052 { 2053 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2054 Supported: true, 2055 }, 2056 { 2057 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2058 Supported: true, 2059 }, 2060 { 2061 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2062 Supported: true, 2063 }, 2064 { 2065 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2066 Supported: true, 2067 }, 2068 }, 2069 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2070 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2071 StrategySpec: strategy, 2072 }, 2073 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 2074 Owned: owned, 2075 }, 2076 }, 2077 } 2078 csv.SetName("csv-hat-1") 2079 2080 By("Create the initial CSV") 2081 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 2082 Expect(err).ShouldNot(HaveOccurred()) 2083 defer cleanupCSV() 2084 2085 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 2086 Expect(err).ShouldNot(HaveOccurred()) 2087 2088 By("Should create Deployment") 2089 _, err = c.GetDeployment(generatedNamespace.GetName(), depName) 2090 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Deployment") 2091 2092 By("Should create APIService") 2093 _, err = c.GetAPIService(apiServiceName) 2094 Expect(err).ShouldNot(HaveOccurred(), "error getting expected APIService") 2095 2096 By("Should create Service") 2097 serviceName := fmt.Sprintf("%s-service", depName) 2098 _, err = c.GetService(generatedNamespace.GetName(), serviceName) 2099 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Service") 2100 2101 By("Should create certificate Secret") 2102 secretName := fmt.Sprintf("%s-cert", serviceName) 2103 _, err = c.GetSecret(generatedNamespace.GetName(), secretName) 2104 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Secret") 2105 2106 By("Should create a Role for the Secret") 2107 _, err = c.GetRole(generatedNamespace.GetName(), secretName) 2108 Expect(err).ShouldNot(HaveOccurred(), "error getting expected Secret Role") 2109 2110 By("Should create a RoleBinding for the Secret") 2111 _, err = c.GetRoleBinding(generatedNamespace.GetName(), secretName) 2112 Expect(err).ShouldNot(HaveOccurred(), "error getting exptected Secret RoleBinding") 2113 2114 By("Should create a system:auth-delegator Cluster RoleBinding") 2115 _, err = c.GetClusterRoleBinding(fmt.Sprintf("%s-system:auth-delegator", serviceName)) 2116 Expect(err).ShouldNot(HaveOccurred(), "error getting expected system:auth-delegator ClusterRoleBinding") 2117 2118 By("Should create an extension-apiserver-authentication-reader RoleBinding in kube-system") 2119 _, err = c.GetRoleBinding("kube-system", fmt.Sprintf("%s-auth-reader", serviceName)) 2120 Expect(err).ShouldNot(HaveOccurred(), "error getting expected extension-apiserver-authentication-reader RoleBinding") 2121 2122 By("Create a new CSV that owns the same API Service but in a different namespace") 2123 csv2 := operatorsv1alpha1.ClusterServiceVersion{ 2124 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2125 MinKubeVersion: "0.0.0", 2126 InstallModes: []operatorsv1alpha1.InstallMode{ 2127 { 2128 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2129 Supported: true, 2130 }, 2131 { 2132 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2133 Supported: true, 2134 }, 2135 { 2136 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2137 Supported: true, 2138 }, 2139 { 2140 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2141 Supported: true, 2142 }, 2143 }, 2144 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2145 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2146 StrategySpec: strategy, 2147 }, 2148 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 2149 Owned: owned, 2150 }, 2151 }, 2152 } 2153 csv2.SetName("csv-hat-2") 2154 2155 By("Create CSV2 to replace CSV") 2156 _, err = createCSV(c, crc, csv2, secondNamespaceName, false, true) 2157 Expect(err).ShouldNot(HaveOccurred()) 2158 2159 _, err = fetchCSV(crc, secondNamespaceName, csv2.Name, csvFailedChecker) 2160 Expect(err).ShouldNot(HaveOccurred()) 2161 }) 2162 It("orphaned API service clean up", func() { 2163 2164 mockGroup := fmt.Sprintf("hats.%s.redhat.com", genName("")) 2165 version := "v1alpha1" 2166 apiServiceName := strings.Join([]string{version, mockGroup}, ".") 2167 2168 apiService := &apiregistrationv1.APIService{ 2169 ObjectMeta: metav1.ObjectMeta{ 2170 Name: apiServiceName, 2171 }, 2172 Spec: apiregistrationv1.APIServiceSpec{ 2173 Group: mockGroup, 2174 Version: version, 2175 GroupPriorityMinimum: 100, 2176 VersionPriority: 100, 2177 }, 2178 } 2179 2180 watcher, err := c.ApiregistrationV1Interface().ApiregistrationV1().APIServices().Watch(context.TODO(), metav1.ListOptions{FieldSelector: "metadata.name=" + apiServiceName}) 2181 Expect(err).ShouldNot(HaveOccurred()) 2182 2183 deleted := make(chan struct{}) 2184 quit := make(chan struct{}) 2185 defer close(quit) 2186 go func() { 2187 defer GinkgoRecover() 2188 events := watcher.ResultChan() 2189 for { 2190 select { 2191 case <-quit: 2192 return 2193 case evt := <-events: 2194 if evt.Type == watch.Deleted { 2195 deleted <- struct{}{} 2196 } 2197 case <-time.After(pollDuration): 2198 Fail("orphaned apiservice not cleaned up as expected") 2199 } 2200 } 2201 }() 2202 2203 _, err = c.CreateAPIService(apiService) 2204 Expect(err).ShouldNot(HaveOccurred(), "error creating expected APIService") 2205 orphanedAPISvc, err := c.GetAPIService(apiServiceName) 2206 Expect(err).ShouldNot(HaveOccurred(), "error getting expected APIService") 2207 2208 newLabels := map[string]string{"olm.owner": "hat-serverfd4r5", "olm.owner.kind": "ClusterServiceVersion", "olm.owner.namespace": "nonexistent-namespace"} 2209 orphanedAPISvc.SetLabels(newLabels) 2210 _, err = c.UpdateAPIService(orphanedAPISvc) 2211 Expect(err).ShouldNot(HaveOccurred(), "error updating APIService") 2212 <-deleted 2213 2214 _, err = c.CreateAPIService(apiService) 2215 Expect(err).ShouldNot(HaveOccurred(), "error creating expected APIService") 2216 orphanedAPISvc, err = c.GetAPIService(apiServiceName) 2217 Expect(err).ShouldNot(HaveOccurred(), "error getting expected APIService") 2218 2219 newLabels = map[string]string{"olm.owner": "hat-serverfd4r5", "olm.owner.kind": "ClusterServiceVersion", "olm.owner.namespace": generatedNamespace.GetName()} 2220 orphanedAPISvc.SetLabels(newLabels) 2221 _, err = c.UpdateAPIService(orphanedAPISvc) 2222 Expect(err).ShouldNot(HaveOccurred(), "error updating APIService") 2223 <-deleted 2224 }) 2225 It("CSV annotations overwrite pod template annotations defined in a StrategyDetailsDeployment", func() { 2226 By("Create a StrategyDetailsDeployment that defines the `foo1` and `foo2` annotations on a pod template") 2227 nginxName := genName("nginx-") 2228 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 2229 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2230 { 2231 Name: genName("dep-"), 2232 Spec: newNginxDeployment(nginxName), 2233 }, 2234 }, 2235 } 2236 strategy.DeploymentSpecs[0].Spec.Template.Annotations = map[string]string{ 2237 "foo1": "notBar1", 2238 "foo2": "bar2", 2239 } 2240 2241 By("Create a CSV that defines the `foo1` and `foo3` annotations") 2242 csv := operatorsv1alpha1.ClusterServiceVersion{ 2243 TypeMeta: metav1.TypeMeta{ 2244 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2245 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2246 }, 2247 ObjectMeta: metav1.ObjectMeta{ 2248 Name: genName("csv"), 2249 Annotations: map[string]string{ 2250 "foo1": "bar1", 2251 "foo3": "bar3", 2252 }, 2253 }, 2254 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2255 MinKubeVersion: "0.0.0", 2256 InstallModes: []operatorsv1alpha1.InstallMode{ 2257 { 2258 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2259 Supported: true, 2260 }, 2261 { 2262 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2263 Supported: true, 2264 }, 2265 { 2266 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2267 Supported: true, 2268 }, 2269 { 2270 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2271 Supported: true, 2272 }, 2273 }, 2274 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2275 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2276 StrategySpec: strategy, 2277 }, 2278 }, 2279 } 2280 2281 By("Create the CSV and make sure to clean it up") 2282 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 2283 Expect(err).ShouldNot(HaveOccurred()) 2284 defer cleanupCSV() 2285 2286 By("Wait for current CSV to succeed") 2287 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 2288 Expect(err).ShouldNot(HaveOccurred()) 2289 2290 By("Should have created deployment") 2291 dep, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name) 2292 Expect(err).ShouldNot(HaveOccurred()) 2293 Expect(dep).ShouldNot(BeNil()) 2294 2295 By("Make sure that the pods annotations are correct") 2296 annotations := dep.Spec.Template.Annotations 2297 Expect(annotations["foo1"]).Should(Equal("bar1")) 2298 Expect(annotations["foo2"]).Should(Equal("bar2")) 2299 Expect(annotations["foo3"]).Should(Equal("bar3")) 2300 }) 2301 It("Set labels for the Deployment created via the ClusterServiceVersion", func() { 2302 By("Create a StrategyDetailsDeployment that defines labels for Deployment inside") 2303 nginxName := genName("nginx-") 2304 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 2305 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2306 { 2307 Name: genName("dep-"), 2308 Spec: newNginxDeployment(nginxName), 2309 Label: k8slabels.Set{ 2310 "application": "nginx", 2311 "application.type": "proxy", 2312 }, 2313 }, 2314 }, 2315 } 2316 2317 csv := operatorsv1alpha1.ClusterServiceVersion{ 2318 TypeMeta: metav1.TypeMeta{ 2319 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2320 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2321 }, 2322 ObjectMeta: metav1.ObjectMeta{ 2323 Name: genName("csv"), 2324 }, 2325 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2326 MinKubeVersion: "0.0.0", 2327 InstallModes: []operatorsv1alpha1.InstallMode{ 2328 { 2329 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2330 Supported: true, 2331 }, 2332 { 2333 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2334 Supported: true, 2335 }, 2336 { 2337 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2338 Supported: true, 2339 }, 2340 { 2341 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2342 Supported: true, 2343 }, 2344 }, 2345 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2346 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2347 StrategySpec: strategy, 2348 }, 2349 }, 2350 } 2351 2352 By("Create the CSV and make sure to clean it up") 2353 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 2354 Expect(err).ShouldNot(HaveOccurred()) 2355 defer cleanupCSV() 2356 2357 By("Wait for current CSV to succeed") 2358 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 2359 Expect(err).ShouldNot(HaveOccurred()) 2360 2361 By("Should have created deployment") 2362 dep, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name) 2363 Expect(err).ShouldNot(HaveOccurred()) 2364 Expect(dep).ShouldNot(BeNil()) 2365 2366 By("Make sure that the deployment labels are correct") 2367 labels := dep.GetLabels() 2368 Expect(labels["olm.owner"]).Should(Equal(csv.GetName())) 2369 Expect(labels["olm.owner.namespace"]).Should(Equal(generatedNamespace.GetName())) 2370 Expect(labels["application"]).Should(Equal("nginx")) 2371 Expect(labels["application.type"]).Should(Equal("proxy")) 2372 }) 2373 It("update same deployment name", func() { 2374 2375 By("Create dependency first (CRD)") 2376 crdPlural := genName("ins") 2377 crdName := crdPlural + ".cluster.com" 2378 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 2379 ObjectMeta: metav1.ObjectMeta{ 2380 Name: crdName, 2381 }, 2382 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 2383 Group: "cluster.com", 2384 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 2385 { 2386 Name: "v1alpha1", 2387 Served: true, 2388 Storage: true, 2389 Schema: &apiextensionsv1.CustomResourceValidation{ 2390 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 2391 Type: "object", 2392 Description: "my crd schema", 2393 }, 2394 }, 2395 }, 2396 }, 2397 Names: apiextensionsv1.CustomResourceDefinitionNames{ 2398 Plural: crdPlural, 2399 Singular: crdPlural, 2400 Kind: crdPlural, 2401 ListKind: "list" + crdPlural, 2402 }, 2403 Scope: apiextensionsv1.NamespaceScoped, 2404 }, 2405 }) 2406 2407 By("Create current CSV") 2408 nginxName := genName("nginx-") 2409 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 2410 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2411 { 2412 Name: genName("dep-"), 2413 Spec: newNginxDeployment(nginxName), 2414 }, 2415 }, 2416 } 2417 2418 Expect(err).ShouldNot(HaveOccurred()) 2419 2420 Expect(err).ShouldNot(HaveOccurred()) 2421 defer cleanupCRD() 2422 csv := operatorsv1alpha1.ClusterServiceVersion{ 2423 TypeMeta: metav1.TypeMeta{ 2424 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2425 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2426 }, 2427 ObjectMeta: metav1.ObjectMeta{ 2428 Name: genName("csv"), 2429 }, 2430 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2431 MinKubeVersion: "0.0.0", 2432 InstallModes: []operatorsv1alpha1.InstallMode{ 2433 { 2434 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2435 Supported: true, 2436 }, 2437 { 2438 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2439 Supported: true, 2440 }, 2441 { 2442 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2443 Supported: true, 2444 }, 2445 { 2446 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2447 Supported: true, 2448 }, 2449 }, 2450 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2451 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2452 StrategySpec: strategy, 2453 }, 2454 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 2455 Owned: []operatorsv1alpha1.CRDDescription{ 2456 { 2457 Name: crdName, 2458 Version: "v1alpha1", 2459 Kind: crdPlural, 2460 DisplayName: crdName, 2461 Description: "In the cluster", 2462 }, 2463 }, 2464 }, 2465 }, 2466 } 2467 2468 By("Don't need to cleanup this CSV, it will be deleted by the upgrade process") 2469 _, err = createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 2470 Expect(err).ShouldNot(HaveOccurred()) 2471 2472 By("Wait for current CSV to succeed") 2473 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 2474 Expect(err).ShouldNot(HaveOccurred()) 2475 2476 By("Should have created deployment") 2477 dep, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name) 2478 Expect(err).ShouldNot(HaveOccurred()) 2479 Expect(dep).ShouldNot(BeNil()) 2480 2481 By("Create updated CSV with the same name but a different spec") 2482 strategyNew := operatorsv1alpha1.StrategyDetailsDeployment{ 2483 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2484 { 2485 Name: strategy.DeploymentSpecs[0].Name, 2486 Spec: newNginxDeployment(nginxName), 2487 }, 2488 }, 2489 } 2490 2491 Expect(err).ShouldNot(HaveOccurred()) 2492 2493 csvNew := operatorsv1alpha1.ClusterServiceVersion{ 2494 TypeMeta: metav1.TypeMeta{ 2495 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2496 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2497 }, 2498 ObjectMeta: metav1.ObjectMeta{ 2499 Name: genName("csv"), 2500 }, 2501 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2502 Replaces: csv.Name, 2503 InstallModes: []operatorsv1alpha1.InstallMode{ 2504 { 2505 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2506 Supported: true, 2507 }, 2508 { 2509 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2510 Supported: true, 2511 }, 2512 { 2513 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2514 Supported: true, 2515 }, 2516 { 2517 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2518 Supported: true, 2519 }, 2520 }, 2521 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2522 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2523 StrategySpec: strategyNew, 2524 }, 2525 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 2526 Owned: []operatorsv1alpha1.CRDDescription{ 2527 { 2528 Name: crdName, 2529 Version: "v1alpha1", 2530 Kind: crdPlural, 2531 DisplayName: crdName, 2532 Description: "In the cluster", 2533 }, 2534 }, 2535 }, 2536 }, 2537 } 2538 2539 cleanupNewCSV, err := createCSV(c, crc, csvNew, generatedNamespace.GetName(), true, false) 2540 Expect(err).ShouldNot(HaveOccurred()) 2541 defer cleanupNewCSV() 2542 2543 By("Wait for updated CSV to succeed") 2544 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvNew.Name, csvSucceededChecker) 2545 Expect(err).ShouldNot(HaveOccurred()) 2546 2547 By("Should have updated existing deployment") 2548 depUpdated, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name) 2549 Expect(err).ShouldNot(HaveOccurred()) 2550 Expect(depUpdated).ShouldNot(BeNil()) 2551 Expect(strategyNew.DeploymentSpecs[0].Spec.Template.Spec.Containers[0].Name).Should(Equal(depUpdated.Spec.Template.Spec.Containers[0].Name)) 2552 2553 By("Should eventually GC the CSV") 2554 err = waitForCsvToDelete(generatedNamespace.GetName(), csv.Name, crc) 2555 Expect(err).ShouldNot(HaveOccurred()) 2556 2557 By("Fetch cluster service version again to check for unnecessary control loops") 2558 sameCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvNew.Name, csvSucceededChecker) 2559 Expect(err).ShouldNot(HaveOccurred()) 2560 Expect(equality.Semantic.DeepEqual(fetchedCSV, sameCSV)).Should(BeTrue(), diff.ObjectDiff(fetchedCSV, sameCSV)) 2561 }) 2562 It("update different deployment name", func() { 2563 2564 By("Create dependency first (CRD)") 2565 crdPlural := genName("ins2") 2566 crdName := crdPlural + ".cluster.com" 2567 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 2568 ObjectMeta: metav1.ObjectMeta{ 2569 Name: crdName, 2570 }, 2571 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 2572 Group: "cluster.com", 2573 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 2574 { 2575 Name: "v1alpha1", 2576 Served: true, 2577 Storage: true, 2578 Schema: &apiextensionsv1.CustomResourceValidation{ 2579 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 2580 Type: "object", 2581 Description: "my crd schema", 2582 }, 2583 }, 2584 }, 2585 }, 2586 Names: apiextensionsv1.CustomResourceDefinitionNames{ 2587 Plural: crdPlural, 2588 Singular: crdPlural, 2589 Kind: crdPlural, 2590 ListKind: "list" + crdPlural, 2591 }, 2592 Scope: apiextensionsv1.NamespaceScoped, 2593 }, 2594 }) 2595 Expect(err).ShouldNot(HaveOccurred()) 2596 defer cleanupCRD() 2597 2598 By("create current CSV") 2599 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 2600 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2601 { 2602 Name: genName("dep-"), 2603 Spec: newNginxDeployment(genName("nginx-")), 2604 }, 2605 }, 2606 } 2607 2608 Expect(err).ShouldNot(HaveOccurred()) 2609 2610 csv := operatorsv1alpha1.ClusterServiceVersion{ 2611 TypeMeta: metav1.TypeMeta{ 2612 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2613 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2614 }, 2615 ObjectMeta: metav1.ObjectMeta{ 2616 Name: genName("csv"), 2617 }, 2618 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2619 MinKubeVersion: "0.0.0", 2620 InstallModes: []operatorsv1alpha1.InstallMode{ 2621 { 2622 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2623 Supported: true, 2624 }, 2625 { 2626 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2627 Supported: true, 2628 }, 2629 { 2630 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2631 Supported: true, 2632 }, 2633 { 2634 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2635 Supported: true, 2636 }, 2637 }, 2638 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2639 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2640 StrategySpec: strategy, 2641 }, 2642 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 2643 Owned: []operatorsv1alpha1.CRDDescription{ 2644 { 2645 Name: crdName, 2646 Version: "v1alpha1", 2647 Kind: crdPlural, 2648 DisplayName: crdName, 2649 Description: "In the cluster2", 2650 }, 2651 }, 2652 }, 2653 }, 2654 } 2655 2656 By("don't need to clean up this CSV, it will be deleted by the upgrade process") 2657 _, err = createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 2658 Expect(err).ShouldNot(HaveOccurred()) 2659 2660 By("Wait for current CSV to succeed") 2661 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 2662 Expect(err).ShouldNot(HaveOccurred()) 2663 2664 By("Should have created deployment") 2665 dep, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name) 2666 Expect(err).ShouldNot(HaveOccurred()) 2667 Expect(dep).ShouldNot(BeNil()) 2668 2669 By("Create updated CSV") 2670 strategyNew := operatorsv1alpha1.StrategyDetailsDeployment{ 2671 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2672 { 2673 Name: genName("dep2"), 2674 Spec: newNginxDeployment(genName("nginx-")), 2675 }, 2676 }, 2677 } 2678 2679 Expect(err).ShouldNot(HaveOccurred()) 2680 2681 csvNew := operatorsv1alpha1.ClusterServiceVersion{ 2682 TypeMeta: metav1.TypeMeta{ 2683 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2684 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2685 }, 2686 ObjectMeta: metav1.ObjectMeta{ 2687 Name: genName("csv2"), 2688 }, 2689 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2690 Replaces: csv.Name, 2691 InstallModes: []operatorsv1alpha1.InstallMode{ 2692 { 2693 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2694 Supported: true, 2695 }, 2696 { 2697 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2698 Supported: true, 2699 }, 2700 { 2701 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2702 Supported: true, 2703 }, 2704 { 2705 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2706 Supported: true, 2707 }, 2708 }, 2709 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2710 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2711 StrategySpec: strategyNew, 2712 }, 2713 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 2714 Owned: []operatorsv1alpha1.CRDDescription{ 2715 { 2716 Name: crdName, 2717 Version: "v1alpha1", 2718 Kind: crdPlural, 2719 DisplayName: crdName, 2720 Description: "In the cluster2", 2721 }, 2722 }, 2723 }, 2724 }, 2725 } 2726 2727 cleanupNewCSV, err := createCSV(c, crc, csvNew, generatedNamespace.GetName(), true, false) 2728 Expect(err).ShouldNot(HaveOccurred()) 2729 defer cleanupNewCSV() 2730 2731 By("Wait for updated CSV to succeed") 2732 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvNew.Name, csvSucceededChecker) 2733 Expect(err).ShouldNot(HaveOccurred()) 2734 2735 By("Fetch cluster service version again to check for unnecessary control loops") 2736 sameCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvNew.Name, csvSucceededChecker) 2737 Expect(err).ShouldNot(HaveOccurred()) 2738 Expect(equality.Semantic.DeepEqual(fetchedCSV, sameCSV)).Should(BeTrue(), diff.ObjectDiff(fetchedCSV, sameCSV)) 2739 2740 By("Should have created new deployment and deleted old") 2741 depNew, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name) 2742 Expect(err).ShouldNot(HaveOccurred()) 2743 Expect(depNew).ShouldNot(BeNil()) 2744 err = waitForDeploymentToDelete(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name, c) 2745 Expect(err).ShouldNot(HaveOccurred()) 2746 2747 By("Should eventually GC the CSV") 2748 err = waitForCsvToDelete(generatedNamespace.GetName(), csv.Name, crc) 2749 Expect(err).ShouldNot(HaveOccurred()) 2750 }) 2751 It("update multiple intermediates", func() { 2752 2753 By("Create dependency first (CRD)") 2754 crdPlural := genName("ins3") 2755 crdName := crdPlural + ".cluster.com" 2756 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 2757 ObjectMeta: metav1.ObjectMeta{ 2758 Name: crdName, 2759 }, 2760 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 2761 Group: "cluster.com", 2762 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 2763 { 2764 Name: "v1alpha1", 2765 Served: true, 2766 Storage: true, 2767 Schema: &apiextensionsv1.CustomResourceValidation{ 2768 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 2769 Type: "object", 2770 Description: "my crd schema", 2771 }, 2772 }, 2773 }, 2774 }, 2775 Names: apiextensionsv1.CustomResourceDefinitionNames{ 2776 Plural: crdPlural, 2777 Singular: crdPlural, 2778 Kind: crdPlural, 2779 ListKind: "list" + crdPlural, 2780 }, 2781 Scope: apiextensionsv1.NamespaceScoped, 2782 }, 2783 }) 2784 Expect(err).ShouldNot(HaveOccurred()) 2785 defer cleanupCRD() 2786 2787 By("create current CSV") 2788 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 2789 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2790 { 2791 Name: genName("dep-"), 2792 Spec: newNginxDeployment(genName("nginx-")), 2793 }, 2794 }, 2795 } 2796 2797 Expect(err).ShouldNot(HaveOccurred()) 2798 2799 csv := operatorsv1alpha1.ClusterServiceVersion{ 2800 TypeMeta: metav1.TypeMeta{ 2801 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2802 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2803 }, 2804 ObjectMeta: metav1.ObjectMeta{ 2805 Name: genName("csv"), 2806 }, 2807 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2808 MinKubeVersion: "0.0.0", 2809 InstallModes: []operatorsv1alpha1.InstallMode{ 2810 { 2811 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2812 Supported: true, 2813 }, 2814 { 2815 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2816 Supported: true, 2817 }, 2818 { 2819 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2820 Supported: true, 2821 }, 2822 { 2823 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2824 Supported: true, 2825 }, 2826 }, 2827 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2828 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2829 StrategySpec: strategy, 2830 }, 2831 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 2832 Owned: []operatorsv1alpha1.CRDDescription{ 2833 { 2834 Name: crdName, 2835 Version: "v1alpha1", 2836 Kind: crdPlural, 2837 DisplayName: crdName, 2838 Description: "In the cluster3", 2839 }, 2840 }, 2841 }, 2842 }, 2843 } 2844 2845 By("don't need to clean up this CSV, it will be deleted by the upgrade process") 2846 _, err = createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 2847 Expect(err).ShouldNot(HaveOccurred()) 2848 2849 By("Wait for current CSV to succeed") 2850 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 2851 Expect(err).ShouldNot(HaveOccurred()) 2852 2853 By("Should have created deployment") 2854 dep, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name) 2855 Expect(err).ShouldNot(HaveOccurred()) 2856 Expect(dep).ShouldNot(BeNil()) 2857 2858 By("Create updated CSV") 2859 strategyNew := operatorsv1alpha1.StrategyDetailsDeployment{ 2860 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2861 { 2862 Name: genName("dep2"), 2863 Spec: newNginxDeployment(genName("nginx-")), 2864 }, 2865 }, 2866 } 2867 2868 Expect(err).ShouldNot(HaveOccurred()) 2869 2870 csvNew := operatorsv1alpha1.ClusterServiceVersion{ 2871 TypeMeta: metav1.TypeMeta{ 2872 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2873 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2874 }, 2875 ObjectMeta: metav1.ObjectMeta{ 2876 Name: genName("csv2"), 2877 }, 2878 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2879 Replaces: csv.Name, 2880 InstallModes: []operatorsv1alpha1.InstallMode{ 2881 { 2882 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 2883 Supported: true, 2884 }, 2885 { 2886 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 2887 Supported: true, 2888 }, 2889 { 2890 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 2891 Supported: true, 2892 }, 2893 { 2894 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 2895 Supported: true, 2896 }, 2897 }, 2898 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 2899 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 2900 StrategySpec: strategyNew, 2901 }, 2902 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 2903 Owned: []operatorsv1alpha1.CRDDescription{ 2904 { 2905 Name: crdName, 2906 Version: "v1alpha1", 2907 Kind: crdPlural, 2908 DisplayName: crdName, 2909 Description: "In the cluster3", 2910 }, 2911 }, 2912 }, 2913 }, 2914 } 2915 2916 cleanupNewCSV, err := createCSV(c, crc, csvNew, generatedNamespace.GetName(), true, false) 2917 Expect(err).ShouldNot(HaveOccurred()) 2918 defer cleanupNewCSV() 2919 2920 By("Wait for updated CSV to succeed") 2921 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvNew.Name, csvSucceededChecker) 2922 Expect(err).ShouldNot(HaveOccurred()) 2923 2924 By("Fetch cluster service version again to check for unnecessary control loops") 2925 sameCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvNew.Name, csvSucceededChecker) 2926 Expect(err).ShouldNot(HaveOccurred()) 2927 Expect(equality.Semantic.DeepEqual(fetchedCSV, sameCSV)).Should(BeTrue(), diff.ObjectDiff(fetchedCSV, sameCSV)) 2928 2929 By("Should have created new deployment and deleted old") 2930 depNew, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name) 2931 Expect(err).ShouldNot(HaveOccurred()) 2932 Expect(depNew).ShouldNot(BeNil()) 2933 err = waitForDeploymentToDelete(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name, c) 2934 Expect(err).ShouldNot(HaveOccurred()) 2935 2936 By("Should eventually GC the CSV") 2937 err = waitForCsvToDelete(generatedNamespace.GetName(), csv.Name, crc) 2938 Expect(err).ShouldNot(HaveOccurred()) 2939 }) 2940 It("update in place", func() { 2941 2942 By("Create dependency first (CRD)") 2943 crdPlural := genName("ins") 2944 crdName := crdPlural + ".cluster.com" 2945 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 2946 ObjectMeta: metav1.ObjectMeta{ 2947 Name: crdName, 2948 }, 2949 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 2950 Group: "cluster.com", 2951 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 2952 { 2953 Name: "v1alpha1", 2954 Served: true, 2955 Storage: true, 2956 Schema: &apiextensionsv1.CustomResourceValidation{ 2957 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 2958 Type: "object", 2959 Description: "my crd schema", 2960 }, 2961 }, 2962 }, 2963 }, 2964 Names: apiextensionsv1.CustomResourceDefinitionNames{ 2965 Plural: crdPlural, 2966 Singular: crdPlural, 2967 Kind: crdPlural, 2968 ListKind: "list" + crdPlural, 2969 }, 2970 Scope: apiextensionsv1.NamespaceScoped, 2971 }, 2972 }) 2973 2974 By("Create current CSV") 2975 nginxName := genName("nginx-") 2976 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 2977 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 2978 { 2979 Name: genName("dep-"), 2980 Spec: newNginxDeployment(nginxName), 2981 }, 2982 }, 2983 } 2984 2985 Expect(err).ShouldNot(HaveOccurred()) 2986 2987 Expect(err).ShouldNot(HaveOccurred()) 2988 defer cleanupCRD() 2989 csv := operatorsv1alpha1.ClusterServiceVersion{ 2990 TypeMeta: metav1.TypeMeta{ 2991 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 2992 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 2993 }, 2994 ObjectMeta: metav1.ObjectMeta{ 2995 Name: genName("csv"), 2996 }, 2997 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 2998 MinKubeVersion: "0.0.0", 2999 InstallModes: []operatorsv1alpha1.InstallMode{ 3000 { 3001 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3002 Supported: true, 3003 }, 3004 { 3005 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3006 Supported: true, 3007 }, 3008 { 3009 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3010 Supported: true, 3011 }, 3012 { 3013 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3014 Supported: true, 3015 }, 3016 }, 3017 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 3018 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 3019 StrategySpec: strategy, 3020 }, 3021 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 3022 Owned: []operatorsv1alpha1.CRDDescription{ 3023 { 3024 Name: crdName, 3025 Version: "v1alpha1", 3026 Kind: crdPlural, 3027 DisplayName: crdName, 3028 Description: "In the cluster", 3029 }, 3030 }, 3031 }, 3032 }, 3033 } 3034 3035 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, true) 3036 Expect(err).ShouldNot(HaveOccurred()) 3037 defer cleanupCSV() 3038 3039 By("Wait for current CSV to succeed") 3040 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3041 Expect(err).ShouldNot(HaveOccurred()) 3042 3043 By("Should have created deployment") 3044 dep, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name) 3045 Expect(err).ShouldNot(HaveOccurred()) 3046 Expect(dep).ShouldNot(BeNil()) 3047 3048 By("Create updated CSV") 3049 strategyNew := strategy 3050 strategyNew.DeploymentSpecs[0].Spec.Template.Spec.Containers = []corev1.Container{ 3051 { 3052 Name: genName("nginx-"), 3053 Image: *dummyImage, 3054 Ports: []corev1.ContainerPort{ 3055 { 3056 ContainerPort: 80, 3057 }, 3058 }, 3059 ImagePullPolicy: corev1.PullIfNotPresent, 3060 }, 3061 } 3062 3063 By("Also set something outside the spec template - this should be ignored") 3064 var five int32 = 5 3065 strategyNew.DeploymentSpecs[0].Spec.Replicas = &five 3066 3067 Expect(err).ShouldNot(HaveOccurred()) 3068 3069 fetchedCSV.Spec.InstallStrategy.StrategySpec = strategyNew 3070 3071 By("Update CSV directly") 3072 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Update(context.TODO(), fetchedCSV, metav1.UpdateOptions{}) 3073 Expect(err).ShouldNot(HaveOccurred()) 3074 3075 By("wait for deployment spec to be updated") 3076 Eventually(func() (string, error) { 3077 fetched, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name) 3078 if err != nil { 3079 return "", err 3080 } 3081 ctx.Ctx().Logf("waiting for deployment to update...") 3082 return fetched.Spec.Template.Spec.Containers[0].Name, nil 3083 }).Should(Equal(strategyNew.DeploymentSpecs[0].Spec.Template.Spec.Containers[0].Name)) 3084 3085 By("Wait for updated CSV to succeed") 3086 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3087 Expect(err).ShouldNot(HaveOccurred()) 3088 3089 depUpdated, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name) 3090 Expect(err).ShouldNot(HaveOccurred()) 3091 Expect(depUpdated).ShouldNot(BeNil()) 3092 3093 By("Deployment should have changed even though the CSV is otherwise the same") 3094 Expect(strategyNew.DeploymentSpecs[0].Spec.Template.Spec.Containers[0].Name).Should(Equal(depUpdated.Spec.Template.Spec.Containers[0].Name)) 3095 Expect(strategyNew.DeploymentSpecs[0].Spec.Template.Spec.Containers[0].Image).Should(Equal(depUpdated.Spec.Template.Spec.Containers[0].Image)) 3096 3097 By("Field updated even though template spec didn't change, because it was part of a template spec change as well") 3098 Expect(*strategyNew.DeploymentSpecs[0].Spec.Replicas).Should(Equal(*depUpdated.Spec.Replicas)) 3099 }) 3100 It("update multiple version CRD", func() { 3101 3102 By("Create initial CRD which has 2 versions: v1alpha1 & v1alpha2") 3103 crdPlural := genName("ins4") 3104 crdName := crdPlural + ".cluster.com" 3105 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 3106 ObjectMeta: metav1.ObjectMeta{ 3107 Name: crdName, 3108 }, 3109 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 3110 Group: "cluster.com", 3111 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 3112 { 3113 Name: "v1alpha1", 3114 Served: true, 3115 Storage: true, 3116 Schema: &apiextensionsv1.CustomResourceValidation{ 3117 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 3118 Type: "object", 3119 Description: "my crd schema", 3120 }, 3121 }, 3122 }, 3123 { 3124 Name: "v1alpha2", 3125 Served: true, 3126 Storage: false, 3127 Schema: &apiextensionsv1.CustomResourceValidation{ 3128 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 3129 Type: "object", 3130 Description: "my crd schema", 3131 }, 3132 }, 3133 }, 3134 }, 3135 Names: apiextensionsv1.CustomResourceDefinitionNames{ 3136 Plural: crdPlural, 3137 Singular: crdPlural, 3138 Kind: crdPlural, 3139 ListKind: "list" + crdPlural, 3140 }, 3141 Scope: apiextensionsv1.NamespaceScoped, 3142 }, 3143 }) 3144 Expect(err).ShouldNot(HaveOccurred()) 3145 defer cleanupCRD() 3146 3147 By("create initial deployment strategy") 3148 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 3149 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3150 { 3151 Name: genName("dep1-"), 3152 Spec: newNginxDeployment(genName("nginx-")), 3153 }, 3154 }, 3155 } 3156 3157 Expect(err).ShouldNot(HaveOccurred()) 3158 3159 By("First CSV with owning CRD v1alpha1") 3160 csv := operatorsv1alpha1.ClusterServiceVersion{ 3161 TypeMeta: metav1.TypeMeta{ 3162 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 3163 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 3164 }, 3165 ObjectMeta: metav1.ObjectMeta{ 3166 Name: genName("csv"), 3167 }, 3168 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 3169 MinKubeVersion: "0.0.0", 3170 InstallModes: []operatorsv1alpha1.InstallMode{ 3171 { 3172 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3173 Supported: true, 3174 }, 3175 { 3176 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3177 Supported: true, 3178 }, 3179 { 3180 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3181 Supported: true, 3182 }, 3183 { 3184 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3185 Supported: true, 3186 }, 3187 }, 3188 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 3189 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 3190 StrategySpec: strategy, 3191 }, 3192 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 3193 Owned: []operatorsv1alpha1.CRDDescription{ 3194 { 3195 Name: crdName, 3196 Version: "v1alpha1", 3197 Kind: crdPlural, 3198 DisplayName: crdName, 3199 Description: "In the cluster4", 3200 }, 3201 }, 3202 }, 3203 }, 3204 } 3205 3206 By("CSV will be deleted by the upgrade process later") 3207 _, err = createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 3208 Expect(err).ShouldNot(HaveOccurred()) 3209 3210 By("Wait for current CSV to succeed") 3211 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3212 Expect(err).ShouldNot(HaveOccurred()) 3213 3214 By("Should have created deployment") 3215 dep, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name) 3216 Expect(err).ShouldNot(HaveOccurred()) 3217 Expect(dep).ShouldNot(BeNil()) 3218 3219 By("Create updated deployment strategy") 3220 strategyNew := operatorsv1alpha1.StrategyDetailsDeployment{ 3221 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3222 { 3223 Name: genName("dep2-"), 3224 Spec: newNginxDeployment(genName("nginx-")), 3225 }, 3226 }, 3227 } 3228 3229 Expect(err).ShouldNot(HaveOccurred()) 3230 3231 By("Second CSV with owning CRD v1alpha1 and v1alpha2") 3232 csvNew := operatorsv1alpha1.ClusterServiceVersion{ 3233 TypeMeta: metav1.TypeMeta{ 3234 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 3235 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 3236 }, 3237 ObjectMeta: metav1.ObjectMeta{ 3238 Name: genName("csv2"), 3239 }, 3240 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 3241 Replaces: csv.Name, 3242 InstallModes: []operatorsv1alpha1.InstallMode{ 3243 { 3244 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3245 Supported: true, 3246 }, 3247 { 3248 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3249 Supported: true, 3250 }, 3251 { 3252 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3253 Supported: true, 3254 }, 3255 { 3256 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3257 Supported: true, 3258 }, 3259 }, 3260 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 3261 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 3262 StrategySpec: strategyNew, 3263 }, 3264 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 3265 Owned: []operatorsv1alpha1.CRDDescription{ 3266 { 3267 Name: crdName, 3268 Version: "v1alpha1", 3269 Kind: crdPlural, 3270 DisplayName: crdName, 3271 Description: "In the cluster4", 3272 }, 3273 { 3274 Name: crdName, 3275 Version: "v1alpha2", 3276 Kind: crdPlural, 3277 DisplayName: crdName, 3278 Description: "In the cluster4", 3279 }, 3280 }, 3281 }, 3282 }, 3283 } 3284 3285 By("Create newly updated CSV") 3286 _, err = createCSV(c, crc, csvNew, generatedNamespace.GetName(), false, false) 3287 Expect(err).ShouldNot(HaveOccurred()) 3288 3289 By("Wait for updated CSV to succeed") 3290 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvNew.Name, csvSucceededChecker) 3291 Expect(err).ShouldNot(HaveOccurred()) 3292 3293 By("Fetch cluster service version again to check for unnecessary control loops") 3294 sameCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csvNew.Name, csvSucceededChecker) 3295 Expect(err).ShouldNot(HaveOccurred()) 3296 Expect(equality.Semantic.DeepEqual(fetchedCSV, sameCSV)).Should(BeTrue(), diff.ObjectDiff(fetchedCSV, sameCSV)) 3297 3298 By("Should have created new deployment and deleted old one") 3299 depNew, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name) 3300 Expect(err).ShouldNot(HaveOccurred()) 3301 Expect(depNew).ShouldNot(BeNil()) 3302 err = waitForDeploymentToDelete(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name, c) 3303 Expect(err).ShouldNot(HaveOccurred()) 3304 3305 By("Create updated deployment strategy") 3306 strategyNew2 := operatorsv1alpha1.StrategyDetailsDeployment{ 3307 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3308 { 3309 Name: genName("dep3-"), 3310 Spec: newNginxDeployment(genName("nginx-")), 3311 }, 3312 }, 3313 } 3314 Expect(err).ShouldNot(HaveOccurred()) 3315 3316 By("Third CSV with owning CRD v1alpha2") 3317 csvNew2 := operatorsv1alpha1.ClusterServiceVersion{ 3318 TypeMeta: metav1.TypeMeta{ 3319 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 3320 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 3321 }, 3322 ObjectMeta: metav1.ObjectMeta{ 3323 Name: genName("csv3"), 3324 }, 3325 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 3326 Replaces: csvNew.Name, 3327 InstallModes: []operatorsv1alpha1.InstallMode{ 3328 { 3329 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3330 Supported: true, 3331 }, 3332 { 3333 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3334 Supported: true, 3335 }, 3336 { 3337 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3338 Supported: true, 3339 }, 3340 { 3341 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3342 Supported: true, 3343 }, 3344 }, 3345 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 3346 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 3347 StrategySpec: strategyNew2, 3348 }, 3349 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 3350 Owned: []operatorsv1alpha1.CRDDescription{ 3351 { 3352 Name: crdName, 3353 Version: "v1alpha2", 3354 Kind: crdPlural, 3355 DisplayName: crdName, 3356 Description: "In the cluster4", 3357 }, 3358 }, 3359 }, 3360 }, 3361 } 3362 3363 By("Create newly updated CSV") 3364 cleanupNewCSV, err := createCSV(c, crc, csvNew2, generatedNamespace.GetName(), true, false) 3365 Expect(err).ShouldNot(HaveOccurred()) 3366 defer cleanupNewCSV() 3367 3368 By("Wait for updated CSV to succeed") 3369 fetchedCSV, err = fetchCSV(crc, generatedNamespace.GetName(), csvNew2.Name, csvSucceededChecker) 3370 Expect(err).ShouldNot(HaveOccurred()) 3371 3372 By("Fetch cluster service version again to check for unnecessary control loops") 3373 sameCSV, err = fetchCSV(crc, generatedNamespace.GetName(), csvNew2.Name, csvSucceededChecker) 3374 Expect(err).ShouldNot(HaveOccurred()) 3375 Expect(equality.Semantic.DeepEqual(fetchedCSV, sameCSV)).Should(BeTrue(), diff.ObjectDiff(fetchedCSV, sameCSV)) 3376 3377 By("Should have created new deployment and deleted old one") 3378 depNew, err = c.GetDeployment(generatedNamespace.GetName(), strategyNew2.DeploymentSpecs[0].Name) 3379 Expect(err).ShouldNot(HaveOccurred()) 3380 Expect(depNew).ShouldNot(BeNil()) 3381 err = waitForDeploymentToDelete(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name, c) 3382 Expect(err).ShouldNot(HaveOccurred()) 3383 3384 By("Should clean up the CSV") 3385 err = waitForCsvToDelete(generatedNamespace.GetName(), csvNew.Name, crc) 3386 Expect(err).ShouldNot(HaveOccurred()) 3387 }) 3388 3389 It("update modify deployment name", func() { 3390 3391 By("Create dependency first (CRD)") 3392 crdPlural := genName("ins2") 3393 crdName := crdPlural + ".cluster.com" 3394 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 3395 ObjectMeta: metav1.ObjectMeta{ 3396 Name: crdName, 3397 }, 3398 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 3399 Group: "cluster.com", 3400 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 3401 { 3402 Name: "v1alpha1", 3403 Served: true, 3404 Storage: true, 3405 Schema: &apiextensionsv1.CustomResourceValidation{ 3406 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 3407 Type: "object", 3408 Description: "my crd schema", 3409 }, 3410 }, 3411 }, 3412 }, 3413 Names: apiextensionsv1.CustomResourceDefinitionNames{ 3414 Plural: crdPlural, 3415 Singular: crdPlural, 3416 Kind: crdPlural, 3417 ListKind: "list" + crdPlural, 3418 }, 3419 Scope: apiextensionsv1.NamespaceScoped, 3420 }, 3421 }) 3422 Expect(err).ShouldNot(HaveOccurred()) 3423 defer cleanupCRD() 3424 3425 By("create current CSV") 3426 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 3427 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3428 { 3429 Name: genName("dep-"), 3430 Spec: newNginxDeployment(genName("nginx-")), 3431 }, 3432 { 3433 Name: "dep2-test", 3434 Spec: newNginxDeployment("nginx2"), 3435 }, 3436 }, 3437 } 3438 3439 Expect(err).ShouldNot(HaveOccurred()) 3440 3441 csv := operatorsv1alpha1.ClusterServiceVersion{ 3442 TypeMeta: metav1.TypeMeta{ 3443 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 3444 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 3445 }, 3446 ObjectMeta: metav1.ObjectMeta{ 3447 Name: genName("csv"), 3448 }, 3449 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 3450 InstallModes: []operatorsv1alpha1.InstallMode{ 3451 { 3452 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3453 Supported: true, 3454 }, 3455 { 3456 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3457 Supported: true, 3458 }, 3459 { 3460 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3461 Supported: true, 3462 }, 3463 { 3464 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3465 Supported: true, 3466 }, 3467 }, 3468 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 3469 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 3470 StrategySpec: strategy, 3471 }, 3472 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 3473 Owned: []operatorsv1alpha1.CRDDescription{ 3474 { 3475 Name: crdName, 3476 Version: "v1alpha1", 3477 Kind: crdPlural, 3478 DisplayName: crdName, 3479 Description: "In the cluster2", 3480 }, 3481 }, 3482 }, 3483 }, 3484 } 3485 3486 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), true, false) 3487 Expect(err).ShouldNot(HaveOccurred()) 3488 defer cleanupCSV() 3489 3490 By("Wait for current CSV to succeed") 3491 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3492 Expect(err).ShouldNot(HaveOccurred()) 3493 3494 By("Should have created deployments") 3495 dep, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name) 3496 Expect(err).ShouldNot(HaveOccurred()) 3497 Expect(dep).ShouldNot(BeNil()) 3498 dep2, err := c.GetDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[1].Name) 3499 Expect(err).ShouldNot(HaveOccurred()) 3500 Expect(dep2).ShouldNot(BeNil()) 3501 3502 By("Create updated CSV") 3503 strategyNew := operatorsv1alpha1.StrategyDetailsDeployment{ 3504 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3505 { 3506 Name: genName("dep3-"), 3507 Spec: newNginxDeployment(genName("nginx3-")), 3508 }, 3509 { 3510 Name: "dep2-test", 3511 Spec: newNginxDeployment("nginx2"), 3512 }, 3513 }, 3514 } 3515 3516 Expect(err).ShouldNot(HaveOccurred()) 3517 3518 By("Fetch the current csv") 3519 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3520 Expect(err).ShouldNot(HaveOccurred()) 3521 3522 By("Update csv with same strategy with different deployment's name") 3523 fetchedCSV.Spec.InstallStrategy.StrategySpec = strategyNew 3524 3525 By("Update the current csv with the new csv") 3526 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Update(context.TODO(), fetchedCSV, metav1.UpdateOptions{}) 3527 Expect(err).ShouldNot(HaveOccurred()) 3528 3529 By("Wait for new deployment to exist") 3530 _, err = waitForDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name, c) 3531 Expect(err).ShouldNot(HaveOccurred()) 3532 3533 By("Wait for updated CSV to succeed") 3534 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3535 Expect(err).ShouldNot(HaveOccurred()) 3536 3537 By("Should have created new deployment and deleted old") 3538 depNew, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name) 3539 Expect(err).ShouldNot(HaveOccurred()) 3540 Expect(depNew).ShouldNot(BeNil()) 3541 3542 By("Make sure the unchanged deployment still exists") 3543 depNew2, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[1].Name) 3544 Expect(err).ShouldNot(HaveOccurred()) 3545 Expect(depNew2).ShouldNot(BeNil()) 3546 3547 err = waitForDeploymentToDelete(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name, c) 3548 Expect(err).ShouldNot(HaveOccurred()) 3549 }) 3550 It("update deployment spec in an existing CSV for a hotfix", func() { 3551 3552 c := newKubeClient() 3553 crc := newCRClient() 3554 3555 By("Creating dependency first (CRD)") 3556 crdPlural := genName("ins") 3557 crdName := crdPlural + ".cluster.com" 3558 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 3559 ObjectMeta: metav1.ObjectMeta{ 3560 Name: crdName, 3561 }, 3562 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 3563 Group: "cluster.com", 3564 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 3565 { 3566 Name: "v1alpha1", 3567 Served: true, 3568 Storage: true, 3569 Schema: &apiextensionsv1.CustomResourceValidation{ 3570 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 3571 Type: "object", 3572 Description: "my crd schema", 3573 }, 3574 }, 3575 }, 3576 }, 3577 Names: apiextensionsv1.CustomResourceDefinitionNames{ 3578 Plural: crdPlural, 3579 Singular: crdPlural, 3580 Kind: crdPlural, 3581 ListKind: "list" + crdPlural, 3582 }, 3583 Scope: apiextensionsv1.NamespaceScoped, 3584 }, 3585 }) 3586 defer cleanupCRD() 3587 Expect(err).ShouldNot(HaveOccurred()) 3588 3589 By("Creating 'current' CSV") 3590 nginxName := genName("nginx-") 3591 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 3592 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3593 { 3594 Name: genName("dep-"), 3595 Spec: newNginxDeployment(nginxName), 3596 }, 3597 }, 3598 } 3599 3600 csv := operatorsv1alpha1.ClusterServiceVersion{ 3601 TypeMeta: metav1.TypeMeta{ 3602 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 3603 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 3604 }, 3605 ObjectMeta: metav1.ObjectMeta{ 3606 Name: genName("csv"), 3607 }, 3608 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 3609 MinKubeVersion: "0.0.0", 3610 InstallModes: []operatorsv1alpha1.InstallMode{ 3611 { 3612 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3613 Supported: true, 3614 }, 3615 { 3616 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3617 Supported: true, 3618 }, 3619 { 3620 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3621 Supported: true, 3622 }, 3623 { 3624 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3625 Supported: true, 3626 }, 3627 }, 3628 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 3629 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 3630 StrategySpec: strategy, 3631 }, 3632 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 3633 Owned: []operatorsv1alpha1.CRDDescription{ 3634 { 3635 Name: crdName, 3636 Version: "v1alpha1", 3637 Kind: crdPlural, 3638 DisplayName: crdName, 3639 Description: "In the cluster", 3640 }, 3641 }, 3642 }, 3643 }, 3644 } 3645 3646 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), true, false) 3647 Expect(err).ShouldNot(HaveOccurred()) 3648 defer cleanupCSV() 3649 3650 By("Waiting for current CSV to succeed") 3651 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3652 Expect(err).ShouldNot(HaveOccurred()) 3653 3654 By("Waiting for deployment to be created") 3655 dep, err := waitForDeployment(generatedNamespace.GetName(), strategy.DeploymentSpecs[0].Name, c) 3656 Expect(err).ShouldNot(HaveOccurred()) 3657 Expect(dep).ShouldNot(BeNil()) 3658 3659 GinkgoT().Logf("Deployment container name: %v", dep.Spec.Template.Spec.Containers[0].Name) 3660 3661 By("Creating 'updated' CSV with the same name but a different spec") 3662 strategyNew := operatorsv1alpha1.StrategyDetailsDeployment{ 3663 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3664 { 3665 Name: strategy.DeploymentSpecs[0].Name, 3666 Spec: newNginxDeployment(nginxName), 3667 }, 3668 }, 3669 } 3670 3671 By("Fetching the current csv") 3672 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3673 Expect(err).ShouldNot(HaveOccurred()) 3674 3675 By("Updating the CSV") 3676 fetchedCSV.Spec.InstallStrategy.StrategySpec = strategyNew 3677 Eventually(func() error { 3678 _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(generatedNamespace.GetName()).Update(context.TODO(), fetchedCSV, metav1.UpdateOptions{}) 3679 return err 3680 }).Should(Succeed()) 3681 3682 By(fmt.Sprintf("Waiting for the updated CSV to succeed with deplpoyment container name: %s", strategyNew.DeploymentSpecs[0].Spec.Template.Spec.Containers[0].Name)) 3683 nameMatchesPrinted := false 3684 Eventually(func() error { 3685 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, func(csv *operatorsv1alpha1.ClusterServiceVersion) bool { 3686 3687 By("Should have updated existing deployment") 3688 depUpdated, err := c.GetDeployment(generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name) 3689 if err != nil { 3690 GinkgoT().Logf("error getting deployment %s/%s: %v", generatedNamespace.GetName(), strategyNew.DeploymentSpecs[0].Name, err) 3691 return false 3692 } 3693 if depUpdated == nil { 3694 return false 3695 } 3696 3697 By("container name has been updated and differs from initial CSV spec and updated CSV spec") 3698 if depUpdated.Spec.Template.Spec.Containers[0].Name != strategyNew.DeploymentSpecs[0].Spec.Template.Spec.Containers[0].Name { 3699 return false 3700 } 3701 if !nameMatchesPrinted { 3702 GinkgoT().Logf("deployments: dep container name matches: %v", strategyNew.DeploymentSpecs[0].Spec.Template.Spec.Containers[0].Name) 3703 nameMatchesPrinted = true 3704 } 3705 3706 By("Check for success") 3707 return csvSucceededChecker(csv) 3708 }) 3709 return err 3710 }, pollDuration, pollInterval).Should(Succeed()) 3711 }) 3712 It("emits CSV requirement events", func() { 3713 3714 By("Require an API that we know won't exist under our domain") 3715 csv := &operatorsv1alpha1.ClusterServiceVersion{ 3716 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 3717 MinKubeVersion: "0.0.0", 3718 InstallModes: []operatorsv1alpha1.InstallMode{ 3719 { 3720 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3721 Supported: true, 3722 }, 3723 { 3724 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3725 Supported: true, 3726 }, 3727 { 3728 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3729 Supported: true, 3730 }, 3731 { 3732 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3733 Supported: true, 3734 }, 3735 }, 3736 InstallStrategy: newNginxInstallStrategy(genName("dep-"), nil, nil), 3737 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 3738 Required: []operatorsv1alpha1.APIServiceDescription{ 3739 { 3740 Group: "bad.packages.operators.coreos.com", 3741 Version: "v1", 3742 Kind: "PackageManifest", 3743 }, 3744 }, 3745 }, 3746 }, 3747 } 3748 csv.SetNamespace(generatedNamespace.GetName()) 3749 csv.SetName(genName("csv-")) 3750 3751 clientCtx := context.Background() 3752 listOpts := metav1.ListOptions{ 3753 FieldSelector: "involvedObject.kind=ClusterServiceVersion", 3754 } 3755 events, err := c.KubernetesInterface().CoreV1().Events(csv.GetNamespace()).List(clientCtx, listOpts) 3756 Expect(err).ToNot(HaveOccurred()) 3757 3758 By("Watch latest events from test namespace for CSV") 3759 listOpts.ResourceVersion = events.ResourceVersion 3760 w, err := c.KubernetesInterface().CoreV1().Events(generatedNamespace.GetName()).Watch(context.Background(), listOpts) 3761 Expect(err).ToNot(HaveOccurred()) 3762 defer w.Stop() 3763 3764 cleanupCSV, err := createCSV(c, crc, *csv, csv.GetNamespace(), false, false) 3765 Expect(err).ToNot(HaveOccurred()) 3766 defer cleanupCSV() 3767 3768 By("emitting when requirements are not met") 3769 nextReason := func() string { 3770 if e := <-w.ResultChan(); e.Object != nil { 3771 return e.Object.(*corev1.Event).Reason 3772 } 3773 return "" 3774 } 3775 Eventually(nextReason).Should(Equal("RequirementsNotMet")) 3776 3777 By("Patch the CSV to require an API that we know exists") 3778 Eventually(ctx.Ctx().SSAClient().Apply(clientCtx, csv, func(c *operatorsv1alpha1.ClusterServiceVersion) error { 3779 c.Spec.APIServiceDefinitions.Required[0].Group = "packages.operators.coreos.com" 3780 return nil 3781 })).Should(Succeed()) 3782 3783 By("emitting when requirements are met") 3784 Eventually(nextReason).Should(Equal("AllRequirementsMet")) 3785 }) 3786 3787 // TODO: test behavior when replaces field doesn't point to existing CSV 3788 It("status invalid CSV", func() { 3789 3790 By("Create CRD") 3791 crdPlural := genName("ins") 3792 crdName := crdPlural + ".cluster.com" 3793 cleanupCRD, err := createCRD(c, apiextensionsv1.CustomResourceDefinition{ 3794 ObjectMeta: metav1.ObjectMeta{ 3795 Name: crdName, 3796 }, 3797 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 3798 Group: "cluster.com", 3799 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 3800 { 3801 Name: "v1alpha1", 3802 Served: true, 3803 Storage: true, 3804 Schema: &apiextensionsv1.CustomResourceValidation{ 3805 OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ 3806 Type: "object", 3807 Description: "my crd schema", 3808 }, 3809 }, 3810 }, 3811 }, 3812 Names: apiextensionsv1.CustomResourceDefinitionNames{ 3813 Plural: crdPlural, 3814 Singular: crdPlural, 3815 Kind: crdPlural, 3816 ListKind: "list" + crdPlural, 3817 }, 3818 Scope: apiextensionsv1.NamespaceScoped, 3819 }, 3820 }) 3821 Expect(err).ShouldNot(HaveOccurred()) 3822 defer cleanupCRD() 3823 3824 By("create CSV") 3825 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 3826 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3827 { 3828 Name: genName("dep-"), 3829 Spec: newNginxDeployment(genName("nginx-")), 3830 }, 3831 }, 3832 } 3833 Expect(err).ShouldNot(HaveOccurred()) 3834 3835 csv := operatorsv1alpha1.ClusterServiceVersion{ 3836 TypeMeta: metav1.TypeMeta{ 3837 Kind: operatorsv1alpha1.ClusterServiceVersionKind, 3838 APIVersion: operatorsv1alpha1.ClusterServiceVersionAPIVersion, 3839 }, 3840 ObjectMeta: metav1.ObjectMeta{ 3841 Name: genName("csv"), 3842 }, 3843 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 3844 InstallModes: []operatorsv1alpha1.InstallMode{ 3845 { 3846 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3847 Supported: true, 3848 }, 3849 { 3850 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3851 Supported: true, 3852 }, 3853 { 3854 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3855 Supported: true, 3856 }, 3857 { 3858 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3859 Supported: true, 3860 }, 3861 }, 3862 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 3863 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 3864 StrategySpec: strategy, 3865 }, 3866 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 3867 Owned: []operatorsv1alpha1.CRDDescription{ 3868 { 3869 Name: crdName, 3870 Version: "apiextensions.k8s.io/v1alpha1", // purposely invalid, should be just v1alpha1 to match CRD 3871 Kind: crdPlural, 3872 DisplayName: crdName, 3873 Description: "In the cluster2", 3874 }, 3875 }, 3876 }, 3877 }, 3878 } 3879 3880 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), true, false) 3881 Expect(err).ShouldNot(HaveOccurred()) 3882 defer cleanupCSV() 3883 3884 notServedStatus := operatorsv1alpha1.RequirementStatus{ 3885 Group: "apiextensions.k8s.io", 3886 Version: "v1", 3887 Kind: "CustomResourceDefinition", 3888 Name: crdName, 3889 Status: operatorsv1alpha1.RequirementStatusReasonNotPresent, 3890 Message: "CRD version not served", 3891 } 3892 csvCheckPhaseAndRequirementStatus := func(csv *operatorsv1alpha1.ClusterServiceVersion) bool { 3893 if csv.Status.Phase == operatorsv1alpha1.CSVPhasePending { 3894 for _, status := range csv.Status.RequirementStatus { 3895 if status.Message == notServedStatus.Message { 3896 return true 3897 } 3898 } 3899 } 3900 return false 3901 } 3902 3903 fetchedCSV, err := fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvCheckPhaseAndRequirementStatus) 3904 Expect(err).ShouldNot(HaveOccurred()) 3905 3906 Expect(fetchedCSV.Status.RequirementStatus).Should(ContainElement(notServedStatus)) 3907 }) 3908 3909 It("api service resource migrated if adoptable", func() { 3910 3911 depName := genName("hat-server") 3912 mockGroup := fmt.Sprintf("hats.%s.redhat.com", genName("")) 3913 version := "v1alpha1" 3914 mockGroupVersion := strings.Join([]string{mockGroup, version}, "/") 3915 mockKinds := []string{"fedora"} 3916 depSpec := newMockExtServerDeployment(depName, []mockGroupVersionKind{{depName, mockGroupVersion, mockKinds, 5443}}) 3917 apiServiceName := strings.Join([]string{version, mockGroup}, ".") 3918 3919 By("Create CSVs for the hat-server") 3920 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 3921 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 3922 { 3923 Name: depName, 3924 Spec: depSpec, 3925 }, 3926 }, 3927 } 3928 3929 owned := make([]operatorsv1alpha1.APIServiceDescription, len(mockKinds)) 3930 for i, kind := range mockKinds { 3931 owned[i] = operatorsv1alpha1.APIServiceDescription{ 3932 Name: apiServiceName, 3933 Group: mockGroup, 3934 Version: version, 3935 Kind: kind, 3936 DeploymentName: depName, 3937 ContainerPort: int32(5443), 3938 DisplayName: kind, 3939 Description: fmt.Sprintf("A %s", kind), 3940 } 3941 } 3942 3943 csv := operatorsv1alpha1.ClusterServiceVersion{ 3944 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 3945 MinKubeVersion: "0.0.0", 3946 InstallModes: []operatorsv1alpha1.InstallMode{ 3947 { 3948 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 3949 Supported: true, 3950 }, 3951 { 3952 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 3953 Supported: true, 3954 }, 3955 { 3956 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 3957 Supported: true, 3958 }, 3959 { 3960 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 3961 Supported: true, 3962 }, 3963 }, 3964 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 3965 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 3966 StrategySpec: strategy, 3967 }, 3968 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 3969 Owned: owned, 3970 }, 3971 }, 3972 } 3973 csv.SetName("csv-hat-1") 3974 csv.SetNamespace(generatedNamespace.GetName()) 3975 3976 createLegacyAPIResources(generatedNamespace.GetName(), &csv, owned[0], c) 3977 3978 By("Create the APIService CSV") 3979 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 3980 Expect(err).ShouldNot(HaveOccurred()) 3981 defer cleanupCSV() 3982 3983 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 3984 Expect(err).ShouldNot(HaveOccurred()) 3985 3986 checkLegacyAPIResources(generatedNamespace.GetName(), owned[0], true, c) 3987 }) 3988 3989 It("API service resource not migrated if not adoptable", func() { 3990 3991 depName := genName("hat-server") 3992 mockGroup := fmt.Sprintf("hats.%s.redhat.com", genName("")) 3993 version := "v1alpha1" 3994 mockGroupVersion := strings.Join([]string{mockGroup, version}, "/") 3995 mockKinds := []string{"fedora"} 3996 depSpec := newMockExtServerDeployment(depName, []mockGroupVersionKind{{depName, mockGroupVersion, mockKinds, 5443}}) 3997 apiServiceName := strings.Join([]string{version, mockGroup}, ".") 3998 3999 By("Create CSVs for the hat-server") 4000 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 4001 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 4002 { 4003 Name: depName, 4004 Spec: depSpec, 4005 }, 4006 }, 4007 } 4008 4009 owned := make([]operatorsv1alpha1.APIServiceDescription, len(mockKinds)) 4010 for i, kind := range mockKinds { 4011 owned[i] = operatorsv1alpha1.APIServiceDescription{ 4012 Name: apiServiceName, 4013 Group: mockGroup, 4014 Version: version, 4015 Kind: kind, 4016 DeploymentName: depName, 4017 ContainerPort: int32(5443), 4018 DisplayName: kind, 4019 Description: fmt.Sprintf("A %s", kind), 4020 } 4021 } 4022 4023 csv := operatorsv1alpha1.ClusterServiceVersion{ 4024 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 4025 MinKubeVersion: "0.0.0", 4026 InstallModes: []operatorsv1alpha1.InstallMode{ 4027 { 4028 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 4029 Supported: true, 4030 }, 4031 { 4032 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 4033 Supported: true, 4034 }, 4035 { 4036 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 4037 Supported: true, 4038 }, 4039 { 4040 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 4041 Supported: true, 4042 }, 4043 }, 4044 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 4045 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 4046 StrategySpec: strategy, 4047 }, 4048 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 4049 Owned: owned, 4050 }, 4051 }, 4052 } 4053 csv.SetName("csv-hat-1") 4054 csv.SetNamespace(generatedNamespace.GetName()) 4055 4056 createLegacyAPIResources(generatedNamespace.GetName(), nil, owned[0], c) 4057 4058 By("Create the APIService CSV") 4059 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 4060 Expect(err).ShouldNot(HaveOccurred()) 4061 defer cleanupCSV() 4062 4063 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 4064 Expect(err).ShouldNot(HaveOccurred()) 4065 4066 checkLegacyAPIResources(generatedNamespace.GetName(), owned[0], false, c) 4067 4068 By("Cleanup the resources created for this test that were not cleaned up.") 4069 deleteLegacyAPIResources(generatedNamespace.GetName(), owned[0], c) 4070 }) 4071 4072 It("multiple API services on a single pod", func() { 4073 4074 By("Create the deployment that both APIServices will be deployed to.") 4075 depName := genName("hat-server") 4076 4077 By("Define the expected mock APIService settings.") 4078 version := "v1alpha1" 4079 apiService1Group := fmt.Sprintf("hats.%s.redhat.com", genName("")) 4080 apiService1GroupVersion := strings.Join([]string{apiService1Group, version}, "/") 4081 apiService1Kinds := []string{"fedora"} 4082 apiService1Name := strings.Join([]string{version, apiService1Group}, ".") 4083 4084 apiService2Group := fmt.Sprintf("hats.%s.redhat.com", genName("")) 4085 apiService2GroupVersion := strings.Join([]string{apiService2Group, version}, "/") 4086 apiService2Kinds := []string{"fez"} 4087 apiService2Name := strings.Join([]string{version, apiService2Group}, ".") 4088 4089 By("Create the deployment spec with the two APIServices.") 4090 mockGroupVersionKinds := []mockGroupVersionKind{ 4091 { 4092 depName, 4093 apiService1GroupVersion, 4094 apiService1Kinds, 4095 5443, 4096 }, 4097 { 4098 depName, 4099 apiService2GroupVersion, 4100 apiService2Kinds, 4101 5444, 4102 }, 4103 } 4104 depSpec := newMockExtServerDeployment(depName, mockGroupVersionKinds) 4105 4106 By("Create the CSV.") 4107 strategy := operatorsv1alpha1.StrategyDetailsDeployment{ 4108 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{ 4109 { 4110 Name: depName, 4111 Spec: depSpec, 4112 }, 4113 }, 4114 } 4115 4116 By("Update the owned APIServices two include the two APIServices.") 4117 owned := []operatorsv1alpha1.APIServiceDescription{ 4118 { 4119 Name: apiService1Name, 4120 Group: apiService1Group, 4121 Version: version, 4122 Kind: apiService1Kinds[0], 4123 DeploymentName: depName, 4124 ContainerPort: int32(5443), 4125 DisplayName: apiService1Kinds[0], 4126 Description: fmt.Sprintf("A %s", apiService1Kinds[0]), 4127 }, 4128 { 4129 Name: apiService2Name, 4130 Group: apiService2Group, 4131 Version: version, 4132 Kind: apiService2Kinds[0], 4133 DeploymentName: depName, 4134 ContainerPort: int32(5444), 4135 DisplayName: apiService2Kinds[0], 4136 Description: fmt.Sprintf("A %s", apiService2Kinds[0]), 4137 }, 4138 } 4139 4140 csv := operatorsv1alpha1.ClusterServiceVersion{ 4141 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 4142 MinKubeVersion: "0.0.0", 4143 InstallModes: []operatorsv1alpha1.InstallMode{ 4144 { 4145 Type: operatorsv1alpha1.InstallModeTypeOwnNamespace, 4146 Supported: true, 4147 }, 4148 { 4149 Type: operatorsv1alpha1.InstallModeTypeSingleNamespace, 4150 Supported: true, 4151 }, 4152 { 4153 Type: operatorsv1alpha1.InstallModeTypeMultiNamespace, 4154 Supported: true, 4155 }, 4156 { 4157 Type: operatorsv1alpha1.InstallModeTypeAllNamespaces, 4158 Supported: true, 4159 }, 4160 }, 4161 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 4162 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 4163 StrategySpec: strategy, 4164 }, 4165 APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{ 4166 Owned: owned, 4167 }, 4168 }, 4169 } 4170 csv.SetName("csv-hat-1") 4171 csv.SetNamespace(generatedNamespace.GetName()) 4172 4173 By("Create the APIService CSV") 4174 cleanupCSV, err := createCSV(c, crc, csv, generatedNamespace.GetName(), false, false) 4175 Expect(err).ShouldNot(HaveOccurred()) 4176 defer cleanupCSV() 4177 4178 _, err = fetchCSV(crc, generatedNamespace.GetName(), csv.Name, csvSucceededChecker) 4179 Expect(err).ShouldNot(HaveOccurred()) 4180 4181 By("Check that the APIService caBundles are equal") 4182 apiService1, err := c.GetAPIService(apiService1Name) 4183 Expect(err).ShouldNot(HaveOccurred()) 4184 4185 apiService2, err := c.GetAPIService(apiService2Name) 4186 Expect(err).ShouldNot(HaveOccurred()) 4187 4188 Expect(apiService2.Spec.CABundle).Should(Equal(apiService1.Spec.CABundle)) 4189 }) 4190 }) 4191 }) 4192 4193 var singleInstance = int32(1) 4194 4195 var immediateDeleteGracePeriod int64 = 0 4196 4197 func findLastEvent(events *corev1.EventList) (event corev1.Event) { 4198 var latestTime metav1.Time 4199 var latestInd int 4200 for i, item := range events.Items { 4201 if i != 0 { 4202 if latestTime.Before(&item.LastTimestamp) { 4203 latestTime = item.LastTimestamp 4204 latestInd = i 4205 } 4206 } else { 4207 latestTime = item.LastTimestamp 4208 } 4209 } 4210 return events.Items[latestInd] 4211 } 4212 4213 func buildCSVCleanupFunc(c operatorclient.ClientInterface, crc versioned.Interface, csv operatorsv1alpha1.ClusterServiceVersion, namespace string, deleteCRDs, deleteAPIServices bool) cleanupFunc { 4214 return func() { 4215 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 4216 fmt.Printf("Skipping deletion of CSV %s/%s...\n", namespace, csv.Name) 4217 return 4218 } 4219 4220 err := crc.OperatorsV1alpha1().ClusterServiceVersions(namespace).Delete(context.TODO(), csv.GetName(), metav1.DeleteOptions{}) 4221 if err != nil && apierrors.IsNotFound(err) { 4222 err = nil 4223 } 4224 Expect(err).ShouldNot(HaveOccurred()) 4225 4226 if deleteCRDs { 4227 for _, crd := range csv.Spec.CustomResourceDefinitions.Owned { 4228 buildCRDCleanupFunc(c, crd.Name)() 4229 } 4230 } 4231 4232 if deleteAPIServices { 4233 for _, desc := range csv.GetOwnedAPIServiceDescriptions() { 4234 buildAPIServiceCleanupFunc(c, desc.Name)() 4235 } 4236 } 4237 4238 err = waitForDelete(func() error { 4239 _, err := crc.OperatorsV1alpha1().ClusterServiceVersions(namespace).Get(context.TODO(), csv.GetName(), metav1.GetOptions{}) 4240 return err 4241 }) 4242 Expect(err).ShouldNot(HaveOccurred()) 4243 } 4244 } 4245 4246 func getPointer(b bool) *bool { 4247 return &b 4248 } 4249 4250 func createCSV(c operatorclient.ClientInterface, crc versioned.Interface, csv operatorsv1alpha1.ClusterServiceVersion, namespace string, cleanupCRDs, cleanupAPIServices bool) (cleanupFunc, error) { 4251 csv.Kind = operatorsv1alpha1.ClusterServiceVersionKind 4252 csv.APIVersion = operatorsv1alpha1.SchemeGroupVersion.String() 4253 Eventually(func() error { 4254 _, err := crc.OperatorsV1alpha1().ClusterServiceVersions(namespace).Create(context.TODO(), &csv, metav1.CreateOptions{}) 4255 return err 4256 }).Should(Succeed()) 4257 4258 return buildCSVCleanupFunc(c, crc, csv, namespace, cleanupCRDs, cleanupAPIServices), nil 4259 } 4260 4261 func buildCRDCleanupFunc(c operatorclient.ClientInterface, crdName string) cleanupFunc { 4262 return func() { 4263 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 4264 fmt.Printf("Skipping deletion of CRD %s...\n", crdName) 4265 return 4266 } 4267 4268 err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.TODO(), crdName, *metav1.NewDeleteOptions(immediateDeleteGracePeriod)) 4269 if err != nil { 4270 fmt.Println(err) 4271 } 4272 4273 waitForDelete(func() error { 4274 _, err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crdName, metav1.GetOptions{}) 4275 return err 4276 }) 4277 } 4278 } 4279 4280 func buildAPIServiceCleanupFunc(c operatorclient.ClientInterface, apiServiceName string) cleanupFunc { 4281 return func() { 4282 if env := os.Getenv("SKIP_CLEANUP"); env != "" { 4283 fmt.Printf("Skipping deletion of APIService %s...\n", apiServiceName) 4284 return 4285 } 4286 4287 err := c.ApiregistrationV1Interface().ApiregistrationV1().APIServices().Delete(context.TODO(), apiServiceName, *metav1.NewDeleteOptions(immediateDeleteGracePeriod)) 4288 if err != nil { 4289 fmt.Println(err) 4290 } 4291 4292 waitForDelete(func() error { 4293 _, err := c.ApiregistrationV1Interface().ApiregistrationV1().APIServices().Get(context.TODO(), apiServiceName, metav1.GetOptions{}) 4294 return err 4295 }) 4296 } 4297 } 4298 4299 func createCRD(c operatorclient.ClientInterface, crd apiextensionsv1.CustomResourceDefinition) (cleanupFunc, error) { 4300 _, err := c.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Create(context.TODO(), &crd, metav1.CreateOptions{}) 4301 if err != nil { 4302 return nil, err 4303 } 4304 4305 return buildCRDCleanupFunc(c, crd.GetName()), nil 4306 } 4307 4308 func newNginxDeployment(name string) appsv1.DeploymentSpec { 4309 return appsv1.DeploymentSpec{ 4310 Selector: &metav1.LabelSelector{ 4311 MatchLabels: map[string]string{ 4312 "app": name, 4313 }, 4314 }, 4315 Replicas: &singleInstance, 4316 Template: corev1.PodTemplateSpec{ 4317 ObjectMeta: metav1.ObjectMeta{ 4318 Labels: map[string]string{ 4319 "app": name, 4320 }, 4321 }, 4322 Spec: corev1.PodSpec{ 4323 Containers: []corev1.Container{ 4324 { 4325 Name: genName("nginx"), 4326 Image: *dummyImage, 4327 Ports: []corev1.ContainerPort{ 4328 { 4329 ContainerPort: 80, 4330 }, 4331 }, 4332 ImagePullPolicy: corev1.PullIfNotPresent, 4333 }, 4334 }, 4335 }, 4336 }, 4337 } 4338 } 4339 4340 type mockGroupVersionKind struct { 4341 Name string 4342 MockGroupVersion string 4343 MockKinds []string 4344 Port int 4345 } 4346 4347 func newMockExtServerDeployment(labelName string, mGVKs []mockGroupVersionKind) appsv1.DeploymentSpec { 4348 // Create the list of containers 4349 containers := []corev1.Container{} 4350 for _, mGVK := range mGVKs { 4351 containers = append(containers, corev1.Container{ 4352 Name: genName(mGVK.Name), 4353 Image: "quay.io/operator-framework/mock-extension-apiserver:master", 4354 Command: []string{"/bin/mock-extension-apiserver"}, 4355 Args: []string{ 4356 "-v=4", 4357 "--mock-kinds", 4358 strings.Join(mGVK.MockKinds, ","), 4359 "--mock-group-version", 4360 mGVK.MockGroupVersion, 4361 "--secure-port", 4362 strconv.Itoa(mGVK.Port), 4363 "--debug", 4364 }, 4365 Ports: []corev1.ContainerPort{ 4366 { 4367 ContainerPort: int32(mGVK.Port), 4368 }, 4369 }, 4370 ImagePullPolicy: corev1.PullIfNotPresent, 4371 }) 4372 } 4373 return appsv1.DeploymentSpec{ 4374 Selector: &metav1.LabelSelector{ 4375 MatchLabels: map[string]string{ 4376 "app": labelName, 4377 }, 4378 }, 4379 Replicas: &singleInstance, 4380 Template: corev1.PodTemplateSpec{ 4381 ObjectMeta: metav1.ObjectMeta{ 4382 Labels: map[string]string{ 4383 "app": labelName, 4384 }, 4385 }, 4386 Spec: corev1.PodSpec{ 4387 Containers: containers, 4388 }, 4389 }, 4390 } 4391 } 4392 4393 type csvConditionChecker func(csv *operatorsv1alpha1.ClusterServiceVersion) bool 4394 4395 func buildCSVConditionChecker(phases ...operatorsv1alpha1.ClusterServiceVersionPhase) csvConditionChecker { 4396 var lastPhase operatorsv1alpha1.ClusterServiceVersionPhase 4397 var lastReason operatorsv1alpha1.ConditionReason 4398 var lastMessage string 4399 lastTime := time.Now() 4400 4401 return func(csv *operatorsv1alpha1.ClusterServiceVersion) bool { 4402 conditionMet := false 4403 for _, phase := range phases { 4404 conditionMet = conditionMet || csv.Status.Phase == phase 4405 } 4406 phase, reason, message := csv.Status.Phase, csv.Status.Reason, csv.Status.Message 4407 if phase != lastPhase || reason != lastReason || message != lastMessage { 4408 ctx.Ctx().Logf("waited %s for CSV %s/%s: to be in phases %s, in phase %s (%s): %s", time.Since(lastTime), csv.Namespace, csv.Name, phases, phase, reason, message) 4409 lastPhase, lastReason, lastMessage = phase, reason, message 4410 lastTime = time.Now() 4411 } 4412 return conditionMet 4413 } 4414 } 4415 4416 func buildCSVReasonChecker(reasons ...operatorsv1alpha1.ConditionReason) csvConditionChecker { 4417 var lastPhase operatorsv1alpha1.ClusterServiceVersionPhase 4418 var lastReason operatorsv1alpha1.ConditionReason 4419 var lastMessage string 4420 lastTime := time.Now() 4421 4422 return func(csv *operatorsv1alpha1.ClusterServiceVersion) bool { 4423 conditionMet := false 4424 for _, reason := range reasons { 4425 conditionMet = conditionMet || csv.Status.Reason == reason 4426 } 4427 phase, reason, message := csv.Status.Phase, csv.Status.Reason, csv.Status.Message 4428 if phase != lastPhase || reason != lastReason || message != lastMessage { 4429 ctx.Ctx().Logf("waited %s for CSV %s/%s: to have reasons %s, in phase %s (%s): %s", time.Since(lastTime), csv.Namespace, csv.Name, reasons, phase, reason, message) 4430 lastPhase, lastReason, lastMessage = phase, reason, message 4431 lastTime = time.Now() 4432 } 4433 return conditionMet 4434 } 4435 } 4436 4437 var csvPendingChecker = buildCSVConditionChecker(operatorsv1alpha1.CSVPhasePending) 4438 var csvSucceededChecker = buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded) 4439 var csvReplacingChecker = buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseReplacing, operatorsv1alpha1.CSVPhaseDeleting) 4440 var csvFailedChecker = buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseFailed) 4441 var csvAnyChecker = buildCSVConditionChecker(operatorsv1alpha1.CSVPhasePending, operatorsv1alpha1.CSVPhaseSucceeded, operatorsv1alpha1.CSVPhaseReplacing, operatorsv1alpha1.CSVPhaseDeleting, operatorsv1alpha1.CSVPhaseFailed) 4442 var csvCopiedChecker = buildCSVReasonChecker(operatorsv1alpha1.CSVReasonCopied) 4443 4444 func fetchCSV(c versioned.Interface, namespace, name string, checker csvConditionChecker) (*operatorsv1alpha1.ClusterServiceVersion, error) { 4445 var lastPhase operatorsv1alpha1.ClusterServiceVersionPhase 4446 var lastReason operatorsv1alpha1.ConditionReason 4447 var lastMessage string 4448 var lastError string 4449 lastTime := time.Now() 4450 var csv *operatorsv1alpha1.ClusterServiceVersion 4451 4452 ctx.Ctx().Logf("waiting for CSV %s/%s to reach condition", namespace, name) 4453 err := wait.Poll(pollInterval, pollDuration, func() (bool, error) { 4454 var err error 4455 csv, err = c.OperatorsV1alpha1().ClusterServiceVersions(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 4456 if err != nil || csv == nil { 4457 if lastError != err.Error() { 4458 ctx.Ctx().Logf("error getting csv %s/%s: %v", namespace, name, err) 4459 lastError = err.Error() 4460 } 4461 return false, nil 4462 } 4463 phase, reason, message := csv.Status.Phase, csv.Status.Reason, csv.Status.Message 4464 if phase != lastPhase || reason != lastReason || message != lastMessage { 4465 ctx.Ctx().Logf("waited %s for csv %s/%s - %s (%s): %s", time.Since(lastTime), namespace, name, phase, reason, message) 4466 lastPhase, lastReason, lastMessage = phase, reason, message 4467 lastTime = time.Now() 4468 } 4469 return checker(csv), nil 4470 }) 4471 4472 // Only want to return `csv` if there was no (timeout) error 4473 if err == nil { 4474 return csv, nil 4475 } 4476 return nil, err 4477 } 4478 4479 func waitForDeployment(namespace, name string, c operatorclient.ClientInterface) (*appsv1.Deployment, error) { 4480 var fetched *appsv1.Deployment 4481 4482 ctx.Ctx().Logf("waiting for deployment %s/%s to be created", namespace, name) 4483 err := wait.Poll(pollInterval, pollDuration, func() (bool, error) { 4484 var err error 4485 fetched, err = c.GetDeployment(namespace, name) 4486 if err != nil { 4487 ctx.Ctx().Logf("error getting deployment %s/%s: %v", namespace, name, err) 4488 return false, nil 4489 } 4490 return true, nil 4491 }) 4492 4493 return fetched, err 4494 } 4495 4496 func waitForDeploymentToDelete(namespace, name string, c operatorclient.ClientInterface) error { 4497 var lastReplicas, lastUpdated, lastReady, lastAvailable, lastUnavailable int32 4498 lastTime := time.Now() 4499 4500 ctx.Ctx().Logf("waiting for deployment %s/%s to delete", namespace, name) 4501 err := wait.Poll(pollInterval, pollDuration, func() (bool, error) { 4502 dep, err := c.GetDeployment(namespace, name) 4503 if apierrors.IsNotFound(err) { 4504 ctx.Ctx().Logf("deployment %s/%s deleted", namespace, name) 4505 return true, nil 4506 } 4507 if err != nil { 4508 ctx.Ctx().Logf("error getting deployment %s/%s: %v", namespace, name, err) 4509 } 4510 if dep != nil { 4511 replicas, updated, ready, available, unavailable := dep.Status.Replicas, dep.Status.UpdatedReplicas, dep.Status.ReadyReplicas, dep.Status.AvailableReplicas, dep.Status.UnavailableReplicas 4512 if replicas != lastReplicas || updated != lastUpdated || ready != lastReady || available != lastAvailable || unavailable != lastUnavailable { 4513 ctx.Ctx().Logf("waited %s for deployment %s/%s status: rep: %v upd: %v rdy: %v ava: %v una: %v", time.Since(lastTime), replicas, updated, ready, available, unavailable) 4514 lastReplicas, lastUpdated, lastReady, lastAvailable, lastUnavailable = replicas, updated, ready, available, unavailable 4515 lastTime = time.Now() 4516 } 4517 } 4518 return false, nil 4519 }) 4520 4521 return err 4522 } 4523 4524 func waitForCsvToDelete(namespace, name string, c versioned.Interface) error { 4525 var lastPhase operatorsv1alpha1.ClusterServiceVersionPhase 4526 var lastReason operatorsv1alpha1.ConditionReason 4527 var lastMessage string 4528 lastTime := time.Now() 4529 4530 ctx.Ctx().Logf("waiting for csv %s/%s to delete", namespace, name) 4531 err := wait.Poll(pollInterval, pollDuration, func() (bool, error) { 4532 csv, err := c.OperatorsV1alpha1().ClusterServiceVersions(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 4533 if apierrors.IsNotFound(err) { 4534 ctx.Ctx().Logf("csv %s/%s deleted", namespace, name) 4535 return true, nil 4536 } 4537 if err != nil { 4538 ctx.Ctx().Logf("error getting csv %s/%s: %v", namespace, name, err) 4539 } 4540 if csv != nil { 4541 phase, reason, message := csv.Status.Phase, csv.Status.Reason, csv.Status.Message 4542 if phase != lastPhase || reason != lastReason || message != lastMessage { 4543 ctx.Ctx().Logf("waited %s for csv %s/%s status: %s (%s): %s", time.Since(lastTime), namespace, name, phase, reason, message) 4544 lastPhase, lastReason, lastMessage = phase, reason, message 4545 lastTime = time.Now() 4546 } 4547 } 4548 return false, nil 4549 }) 4550 4551 return err 4552 } 4553 4554 func deleteLegacyAPIResources(namespace string, desc operatorsv1alpha1.APIServiceDescription, c operatorclient.ClientInterface) { 4555 apiServiceName := fmt.Sprintf("%s.%s", desc.Version, desc.Group) 4556 4557 err := c.DeleteService(namespace, strings.Replace(apiServiceName, ".", "-", -1), &metav1.DeleteOptions{}) 4558 Expect(err).ShouldNot(HaveOccurred()) 4559 4560 err = c.DeleteSecret(namespace, apiServiceName+"-cert", &metav1.DeleteOptions{}) 4561 Expect(err).ShouldNot(HaveOccurred()) 4562 4563 err = c.DeleteRole(namespace, apiServiceName+"-cert", &metav1.DeleteOptions{}) 4564 Expect(err).ShouldNot(HaveOccurred()) 4565 4566 err = c.DeleteRoleBinding(namespace, apiServiceName+"-cert", &metav1.DeleteOptions{}) 4567 Expect(err).ShouldNot(HaveOccurred()) 4568 4569 err = c.DeleteClusterRoleBinding(apiServiceName+"-system:auth-delegator", &metav1.DeleteOptions{}) 4570 Expect(err).ShouldNot(HaveOccurred()) 4571 4572 err = c.DeleteRoleBinding("kube-system", apiServiceName+"-auth-reader", &metav1.DeleteOptions{}) 4573 Expect(err).ShouldNot(HaveOccurred()) 4574 } 4575 4576 func createLegacyAPIResources(namespace string, csv *operatorsv1alpha1.ClusterServiceVersion, desc operatorsv1alpha1.APIServiceDescription, c operatorclient.ClientInterface) { 4577 4578 apiServiceName := fmt.Sprintf("%s.%s", desc.Version, desc.Group) 4579 4580 // Attempt to create the legacy service 4581 service := corev1.Service{} 4582 service.SetName(strings.Replace(apiServiceName, ".", "-", -1)) 4583 service.SetNamespace(namespace) 4584 if csv != nil { 4585 err := ownerutil.AddOwnerLabels(&service, csv) 4586 Expect(err).ShouldNot(HaveOccurred()) 4587 } 4588 4589 service.Spec.Ports = []corev1.ServicePort{{Port: 433, TargetPort: intstr.FromInt(443)}} 4590 _, err := c.CreateService(&service) 4591 Expect(err).ShouldNot(HaveOccurred()) 4592 4593 // Attempt to create the legacy secret 4594 secret := corev1.Secret{} 4595 secret.SetName(apiServiceName + "-cert") 4596 secret.SetNamespace(namespace) 4597 if csv != nil { 4598 err = ownerutil.AddOwnerLabels(&secret, csv) 4599 Expect(err).ShouldNot(HaveOccurred()) 4600 } 4601 4602 _, err = c.CreateSecret(&secret) 4603 if err != nil && !apierrors.IsAlreadyExists(err) { 4604 Expect(err).ShouldNot(HaveOccurred()) 4605 } 4606 4607 // Attempt to create the legacy secret role 4608 role := rbacv1.Role{} 4609 role.SetName(apiServiceName + "-cert") 4610 role.SetNamespace(namespace) 4611 if csv != nil { 4612 err = ownerutil.AddOwnerLabels(&role, csv) 4613 Expect(err).ShouldNot(HaveOccurred()) 4614 } 4615 _, err = c.CreateRole(&role) 4616 Expect(err).ShouldNot(HaveOccurred()) 4617 4618 // Attempt to create the legacy secret role binding 4619 roleBinding := rbacv1.RoleBinding{} 4620 roleBinding.SetName(apiServiceName + "-cert") 4621 roleBinding.SetNamespace(namespace) 4622 roleBinding.RoleRef = rbacv1.RoleRef{ 4623 APIGroup: "rbac.authorization.k8s.io", 4624 Kind: "Role", 4625 Name: role.GetName(), 4626 } 4627 if csv != nil { 4628 err = ownerutil.AddOwnerLabels(&roleBinding, csv) 4629 Expect(err).ShouldNot(HaveOccurred()) 4630 } 4631 4632 _, err = c.CreateRoleBinding(&roleBinding) 4633 Expect(err).ShouldNot(HaveOccurred()) 4634 4635 // Attempt to create the legacy authDelegatorClusterRoleBinding 4636 clusterRoleBinding := rbacv1.ClusterRoleBinding{} 4637 clusterRoleBinding.SetName(apiServiceName + "-system:auth-delegator") 4638 clusterRoleBinding.RoleRef = rbacv1.RoleRef{ 4639 APIGroup: "rbac.authorization.k8s.io", 4640 Kind: "ClusterRole", 4641 Name: "system:auth-delegator", 4642 } 4643 if csv != nil { 4644 err = ownerutil.AddOwnerLabels(&clusterRoleBinding, csv) 4645 Expect(err).ShouldNot(HaveOccurred()) 4646 } 4647 _, err = c.CreateClusterRoleBinding(&clusterRoleBinding) 4648 Expect(err).ShouldNot(HaveOccurred()) 4649 4650 // Attempt to create the legacy authReadingRoleBinding 4651 roleBinding.SetName(apiServiceName + "-auth-reader") 4652 roleBinding.SetNamespace("kube-system") 4653 roleBinding.RoleRef = rbacv1.RoleRef{ 4654 APIGroup: "rbac.authorization.k8s.io", 4655 Kind: "Role", 4656 Name: "extension-apiserver-authentication-reader", 4657 } 4658 _, err = c.CreateRoleBinding(&roleBinding) 4659 Expect(err).ShouldNot(HaveOccurred()) 4660 } 4661 4662 func checkLegacyAPIResources(namespace string, desc operatorsv1alpha1.APIServiceDescription, expectedIsNotFound bool, c operatorclient.ClientInterface) { 4663 apiServiceName := fmt.Sprintf("%s.%s", desc.Version, desc.Group) 4664 4665 // Attempt to create the legacy service 4666 _, err := c.GetService(namespace, strings.Replace(apiServiceName, ".", "-", -1)) 4667 Expect(apierrors.IsNotFound(err)).Should(Equal(expectedIsNotFound)) 4668 4669 // Attempt to create the legacy secret 4670 _, err = c.GetSecret(namespace, apiServiceName+"-cert") 4671 Expect(apierrors.IsNotFound(err)).Should(Equal(expectedIsNotFound)) 4672 4673 // Attempt to create the legacy secret role 4674 _, err = c.GetRole(namespace, apiServiceName+"-cert") 4675 Expect(apierrors.IsNotFound(err)).Should(Equal(expectedIsNotFound)) 4676 4677 // Attempt to create the legacy secret role binding 4678 _, err = c.GetRoleBinding(namespace, apiServiceName+"-cert") 4679 Expect(apierrors.IsNotFound(err)).Should(Equal(expectedIsNotFound)) 4680 4681 // Attempt to create the legacy authDelegatorClusterRoleBinding 4682 _, err = c.GetClusterRoleBinding(apiServiceName + "-system:auth-delegator") 4683 Expect(apierrors.IsNotFound(err)).Should(Equal(expectedIsNotFound)) 4684 4685 // Attempt to create the legacy authReadingRoleBinding 4686 _, err = c.GetRoleBinding("kube-system", apiServiceName+"-auth-reader") 4687 Expect(apierrors.IsNotFound(err)).Should(Equal(expectedIsNotFound)) 4688 }