github.com/operator-framework/operator-lifecycle-manager@v0.30.0/test/e2e/catalog_e2e_test.go (about) 1 package e2e 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net" 8 "path/filepath" 9 "strconv" 10 "strings" 11 "time" 12 13 operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" 14 packageserverclientset "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/client/clientset/versioned" 15 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 16 k8serror "k8s.io/apimachinery/pkg/api/errors" 17 "sigs.k8s.io/controller-runtime/pkg/client" 18 19 "github.com/blang/semver/v4" 20 . "github.com/onsi/ginkgo/v2" 21 . "github.com/onsi/gomega" 22 appsv1 "k8s.io/api/apps/v1" 23 corev1 "k8s.io/api/core/v1" 24 "k8s.io/apimachinery/pkg/api/meta" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/labels" 27 "k8s.io/apimachinery/pkg/types" 28 29 "github.com/operator-framework/api/pkg/lib/version" 30 "github.com/operator-framework/api/pkg/operators/v1alpha1" 31 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" 32 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalogtemplate" 33 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" 34 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/catalogsource" 35 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" 36 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 37 "github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx" 38 ) 39 40 const ( 41 openshiftregistryFQDN = "image-registry.openshift-image-registry.svc:5000" 42 catsrcImage = "docker://quay.io/olmtest/catsrc-update-test:" 43 badCSVDir = "bad-csv" 44 ) 45 46 var _ = Describe("Starting CatalogSource e2e tests", func() { 47 var ( 48 generatedNamespace corev1.Namespace 49 c operatorclient.ClientInterface 50 crc versioned.Interface 51 packageserverClient *packageserverclientset.Clientset 52 ) 53 54 BeforeEach(func() { 55 // In OCP, PSA labels for any namespace created that is not prefixed with "openshift-" is overridden to enforce 56 // PSA restricted. This test namespace needs to prefixed with openshift- so that baseline/privileged enforcement 57 // for the PSA specific tests are not overridden, 58 // Change it only after https://github.com/operator-framework/operator-lifecycle-manager/issues/2859 is closed. 59 namespaceName := genName("openshift-catsrc-e2e-") 60 og := operatorsv1.OperatorGroup{ 61 ObjectMeta: metav1.ObjectMeta{ 62 Name: fmt.Sprintf("%s-operatorgroup", namespaceName), 63 Namespace: namespaceName, 64 }, 65 } 66 generatedNamespace = SetupGeneratedTestNamespaceWithOperatorGroup(namespaceName, og) 67 c = ctx.Ctx().KubeClient() 68 crc = ctx.Ctx().OperatorClient() 69 packageserverClient = packageserverclientset.NewForConfigOrDie(ctx.Ctx().RESTConfig()) 70 }) 71 72 AfterEach(func() { 73 TeardownNamespace(generatedNamespace.GetName()) 74 }) 75 76 It("loading between restarts", func() { 77 By("create a simple catalogsource") 78 packageName := genName("nginx") 79 stableChannel := "stable" 80 packageStable := packageName + "-stable" 81 manifests := []registry.PackageManifest{ 82 { 83 PackageName: packageName, 84 Channels: []registry.PackageChannel{ 85 {Name: stableChannel, CurrentCSVName: packageStable}, 86 }, 87 DefaultChannelName: stableChannel, 88 }, 89 } 90 91 crd := newCRD(genName("ins-")) 92 csv := newCSV(packageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil) 93 94 defer func() { 95 Eventually(func() error { 96 return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{}) 97 }).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue()))) 98 Eventually(func() error { 99 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &csv)) 100 }).Should(Succeed()) 101 }() 102 103 catalogSourceName := genName("mock-ocs-") 104 _, cleanupSource := createInternalCatalogSource(c, crc, catalogSourceName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []v1alpha1.ClusterServiceVersion{csv}) 105 defer cleanupSource() 106 107 By("ensure the mock catalog exists and has been synced by the catalog operator") 108 catalogSource, err := fetchCatalogSourceOnStatus(crc, catalogSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced()) 109 Expect(err).ShouldNot(HaveOccurred()) 110 111 By("get catalog operator deployment") 112 deployment, err := getOperatorDeployment(c, operatorNamespace, labels.Set{"app": "catalog-operator"}) 113 Expect(err).ShouldNot(HaveOccurred()) 114 Expect(deployment).ToNot(BeNil(), "Could not find catalog operator deployment") 115 116 By("rescale catalog operator") 117 By("Rescaling catalog operator...") 118 err = rescaleDeployment(c, deployment) 119 Expect(err).ShouldNot(HaveOccurred(), "Could not rescale catalog operator") 120 By("Catalog operator rescaled") 121 122 By("check for last synced update to catalogsource") 123 By("Checking for catalogsource lastSync updates") 124 _, err = fetchCatalogSourceOnStatus(crc, catalogSourceName, generatedNamespace.GetName(), func(cs *v1alpha1.CatalogSource) bool { 125 before := catalogSource.Status.GRPCConnectionState 126 after := cs.Status.GRPCConnectionState 127 if after != nil && after.LastConnectTime.After(before.LastConnectTime.Time) { 128 ctx.Ctx().Logf("lastSync updated: %s -> %s", before.LastConnectTime, after.LastConnectTime) 129 return true 130 } 131 return false 132 }) 133 Expect(err).ShouldNot(HaveOccurred(), "Catalog source changed after rescale") 134 By("Catalog source successfully loaded after rescale") 135 }) 136 137 It("global update triggers subscription sync", func() { 138 mainPackageName := genName("nginx-") 139 140 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName) 141 mainPackageReplacement := fmt.Sprintf("%s-replacement", mainPackageStable) 142 143 stableChannel := "stable" 144 145 mainCRD := newCRD(genName("ins-")) 146 mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, nil) 147 replacementCSV := newCSV(mainPackageReplacement, generatedNamespace.GetName(), mainPackageStable, semver.MustParse("0.2.0"), []apiextensionsv1.CustomResourceDefinition{mainCRD}, nil, nil) 148 149 defer func() { 150 Eventually(func() error { 151 return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), mainCRD.GetName(), metav1.DeleteOptions{}) 152 }).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue()))) 153 Eventually(func() error { 154 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCSV)) 155 }).Should(Succeed()) 156 Eventually(func() error { 157 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &replacementCSV)) 158 }).Should(Succeed()) 159 }() 160 161 mainCatalogName := genName("mock-ocs-main-") 162 163 By("Create separate manifests for each CatalogSource") 164 mainManifests := []registry.PackageManifest{ 165 { 166 PackageName: mainPackageName, 167 Channels: []registry.PackageChannel{ 168 {Name: stableChannel, CurrentCSVName: mainPackageStable}, 169 }, 170 DefaultChannelName: stableChannel, 171 }, 172 } 173 174 By("Create the initial catalog source") 175 cs, cleanup := createInternalCatalogSource(c, crc, mainCatalogName, globalCatalogNamespace, mainManifests, []apiextensionsv1.CustomResourceDefinition{mainCRD}, []v1alpha1.ClusterServiceVersion{mainCSV}) 176 defer cleanup() 177 178 By("Attempt to get the catalog source before creating install plan") 179 _, err := fetchCatalogSourceOnStatus(crc, cs.GetName(), cs.GetNamespace(), catalogSourceRegistryPodSynced()) 180 Expect(err).ToNot(HaveOccurred()) 181 182 subscriptionSpec := &v1alpha1.SubscriptionSpec{ 183 CatalogSource: cs.GetName(), 184 CatalogSourceNamespace: cs.GetNamespace(), 185 Package: mainPackageName, 186 Channel: stableChannel, 187 StartingCSV: mainCSV.GetName(), 188 InstallPlanApproval: v1alpha1.ApprovalManual, 189 } 190 191 By("Create Subscription") 192 subscriptionName := genName("sub-") 193 createSubscriptionForCatalogWithSpec(GinkgoT(), crc, generatedNamespace.GetName(), subscriptionName, subscriptionSpec) 194 195 subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker()) 196 Expect(err).ShouldNot(HaveOccurred()) 197 Expect(subscription).ToNot(BeNil()) 198 199 installPlanName := subscription.Status.Install.Name 200 requiresApprovalChecker := buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseRequiresApproval) 201 202 Eventually(func() error { 203 fetchedInstallPlan, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, installPlanName, generatedNamespace.GetName(), requiresApprovalChecker) 204 if err != nil { 205 return err 206 } 207 208 fetchedInstallPlan.Spec.Approved = true 209 _, err = crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).Update(context.Background(), fetchedInstallPlan, metav1.UpdateOptions{}) 210 return err 211 }).Should(Succeed()) 212 213 _, err = fetchCSV(crc, generatedNamespace.GetName(), mainCSV.GetName(), csvSucceededChecker) 214 Expect(err).ShouldNot(HaveOccurred()) 215 216 By("Update manifest") 217 mainManifests = []registry.PackageManifest{ 218 { 219 PackageName: mainPackageName, 220 Channels: []registry.PackageChannel{ 221 {Name: stableChannel, CurrentCSVName: replacementCSV.GetName()}, 222 }, 223 DefaultChannelName: stableChannel, 224 }, 225 } 226 227 By("Update catalog configmap") 228 updateInternalCatalog(GinkgoT(), c, crc, cs.GetName(), cs.GetNamespace(), []apiextensionsv1.CustomResourceDefinition{mainCRD}, []v1alpha1.ClusterServiceVersion{mainCSV, replacementCSV}, mainManifests) 229 230 By("Get updated catalogsource") 231 fetchedUpdatedCatalog, err := fetchCatalogSourceOnStatus(crc, cs.GetName(), cs.GetNamespace(), catalogSourceRegistryPodSynced()) 232 Expect(err).ShouldNot(HaveOccurred()) 233 234 subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateUpgradePendingChecker()) 235 Expect(err).ShouldNot(HaveOccurred()) 236 Expect(subscription).ShouldNot(BeNil()) 237 238 By("Ensure the timing") 239 catalogConnState := fetchedUpdatedCatalog.Status.GRPCConnectionState 240 subUpdatedTime := subscription.Status.LastUpdated 241 Expect(subUpdatedTime.Time).Should(BeTemporally("<", catalogConnState.LastConnectTime.Add(60*time.Second))) 242 }) 243 244 It("config map update triggers registry pod rollout", func() { 245 246 mainPackageName := genName("nginx-") 247 dependentPackageName := genName("nginxdep-") 248 249 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName) 250 dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName) 251 252 stableChannel := "stable" 253 254 dependentCRD := newCRD(genName("ins-")) 255 mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil) 256 dependentCSV := newCSV(dependentPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil, nil) 257 258 defer func() { 259 Eventually(func() error { 260 return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{}) 261 }).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue()))) 262 Eventually(func() error { 263 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCSV)) 264 }).Should(Succeed()) 265 Eventually(func() error { 266 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &dependentCSV)) 267 }).Should(Succeed()) 268 }() 269 270 mainCatalogName := genName("mock-ocs-main-") 271 272 By("Create separate manifests for each CatalogSource") 273 mainManifests := []registry.PackageManifest{ 274 { 275 PackageName: mainPackageName, 276 Channels: []registry.PackageChannel{ 277 {Name: stableChannel, CurrentCSVName: mainPackageStable}, 278 }, 279 DefaultChannelName: stableChannel, 280 }, 281 } 282 283 dependentManifests := []registry.PackageManifest{ 284 { 285 PackageName: dependentPackageName, 286 Channels: []registry.PackageChannel{ 287 {Name: stableChannel, CurrentCSVName: dependentPackageStable}, 288 }, 289 DefaultChannelName: stableChannel, 290 }, 291 } 292 293 By("Create the initial catalogsource") 294 createInternalCatalogSource(c, crc, mainCatalogName, generatedNamespace.GetName(), mainManifests, nil, []v1alpha1.ClusterServiceVersion{mainCSV}) 295 296 By("Attempt to get the catalog source before creating install plan") 297 fetchedInitialCatalog, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced()) 298 Expect(err).ShouldNot(HaveOccurred()) 299 300 By("Get initial configmap") 301 configMap, err := c.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Get(context.Background(), fetchedInitialCatalog.Spec.ConfigMap, metav1.GetOptions{}) 302 Expect(err).ShouldNot(HaveOccurred()) 303 304 By("Check pod created") 305 initialPods, err := c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).List(context.Background(), metav1.ListOptions{LabelSelector: "olm.configMapResourceVersion=" + configMap.ResourceVersion}) 306 Expect(err).ShouldNot(HaveOccurred()) 307 Expect(initialPods.Items).To(HaveLen(1)) 308 309 By("Update catalog configmap") 310 updateInternalCatalog(GinkgoT(), c, crc, mainCatalogName, generatedNamespace.GetName(), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, []v1alpha1.ClusterServiceVersion{mainCSV, dependentCSV}, append(mainManifests, dependentManifests...)) 311 312 fetchedUpdatedCatalog, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, generatedNamespace.GetName(), func(catalog *v1alpha1.CatalogSource) bool { 313 before := fetchedInitialCatalog.Status.ConfigMapResource 314 after := catalog.Status.ConfigMapResource 315 if after != nil && before.LastUpdateTime.Before(&after.LastUpdateTime) && 316 after.ResourceVersion != before.ResourceVersion { 317 ctx.Ctx().Logf("catalog updated") 318 return true 319 } 320 ctx.Ctx().Logf("waiting for catalog pod to be available") 321 return false 322 }) 323 Expect(err).ShouldNot(HaveOccurred()) 324 325 var updatedConfigMap *corev1.ConfigMap 326 Eventually(func() (types.UID, error) { 327 var err error 328 By("Get updated configmap") 329 updatedConfigMap, err = c.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Get(context.Background(), fetchedInitialCatalog.Spec.ConfigMap, metav1.GetOptions{}) 330 if err != nil { 331 return "", err 332 } 333 if len(updatedConfigMap.ObjectMeta.OwnerReferences) == 0 { 334 return "", nil 335 } 336 return updatedConfigMap.ObjectMeta.OwnerReferences[0].UID, nil 337 }).Should(Equal(fetchedUpdatedCatalog.ObjectMeta.UID)) 338 339 Expect(configMap.ResourceVersion).ShouldNot(Equal(updatedConfigMap.ResourceVersion)) 340 Expect(fetchedInitialCatalog.Status.ConfigMapResource.ResourceVersion).ShouldNot(Equal(fetchedUpdatedCatalog.Status.ConfigMapResource.ResourceVersion)) 341 Expect(fetchedUpdatedCatalog.Status.ConfigMapResource.ResourceVersion).Should(Equal(updatedConfigMap.GetResourceVersion())) 342 343 By("Await 1 CatalogSource registry pod matching the updated labels") 344 singlePod := podCount(1) 345 selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": mainCatalogName, "olm.configMapResourceVersion": updatedConfigMap.GetResourceVersion()}) 346 podList, err := awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), singlePod) 347 Expect(err).ShouldNot(HaveOccurred()) 348 Expect(podList.Items).To(HaveLen(1), "expected pod list not of length 1") 349 350 By("Await 1 CatalogSource registry pod matching the updated labels") 351 selector = labels.SelectorFromSet(map[string]string{"olm.catalogSource": mainCatalogName}) 352 podList, err = awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), singlePod) 353 Expect(err).ShouldNot(HaveOccurred()) 354 Expect(podList.Items).To(HaveLen(1), "expected pod list not of length 1") 355 356 By("Create Subscription") 357 subscriptionName := genName("sub-") 358 createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, fetchedUpdatedCatalog.GetName(), mainPackageName, stableChannel, "", v1alpha1.ApprovalAutomatic) 359 360 subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker()) 361 Expect(err).ShouldNot(HaveOccurred()) 362 Expect(subscription).ShouldNot(BeNil()) 363 _, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(v1alpha1.CSVPhaseSucceeded)) 364 Expect(err).ShouldNot(HaveOccurred()) 365 366 ipList, err := crc.OperatorsV1alpha1().InstallPlans(generatedNamespace.GetName()).List(context.Background(), metav1.ListOptions{}) 367 ipCount := 0 368 for _, ip := range ipList.Items { 369 if ownerutil.IsOwnedBy(&ip, subscription) { 370 ipCount += 1 371 } 372 } 373 Expect(err).ShouldNot(HaveOccurred()) 374 }) 375 376 It("config map replace triggers registry pod rollout", func() { 377 378 mainPackageName := genName("nginx-") 379 dependentPackageName := genName("nginxdep-") 380 381 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName) 382 383 dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName) 384 385 stableChannel := "stable" 386 387 dependentCRD := newCRD(genName("ins-")) 388 mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil) 389 dependentCSV := newCSV(dependentPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil, nil) 390 391 defer func() { 392 Eventually(func() error { 393 return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{}) 394 }).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue()))) 395 Eventually(func() error { 396 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCSV)) 397 }).Should(Succeed()) 398 Eventually(func() error { 399 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &dependentCSV)) 400 }).Should(Succeed()) 401 }() 402 403 mainCatalogName := genName("mock-ocs-main-") 404 405 By("Create separate manifests for each CatalogSource") 406 mainManifests := []registry.PackageManifest{ 407 { 408 PackageName: mainPackageName, 409 Channels: []registry.PackageChannel{ 410 {Name: stableChannel, CurrentCSVName: mainPackageStable}, 411 }, 412 DefaultChannelName: stableChannel, 413 }, 414 } 415 416 dependentManifests := []registry.PackageManifest{ 417 { 418 PackageName: dependentPackageName, 419 Channels: []registry.PackageChannel{ 420 {Name: stableChannel, CurrentCSVName: dependentPackageStable}, 421 }, 422 DefaultChannelName: stableChannel, 423 }, 424 } 425 426 By("Create the initial catalogsource") 427 _, cleanupSource := createInternalCatalogSource(c, crc, mainCatalogName, generatedNamespace.GetName(), mainManifests, nil, []v1alpha1.ClusterServiceVersion{mainCSV}) 428 429 By("Attempt to get the catalog source before creating install plan") 430 fetchedInitialCatalog, err := fetchCatalogSourceOnStatus(crc, mainCatalogName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced()) 431 Expect(err).ShouldNot(HaveOccurred()) 432 By("Get initial configmap") 433 configMap, err := c.KubernetesInterface().CoreV1().ConfigMaps(generatedNamespace.GetName()).Get(context.Background(), fetchedInitialCatalog.Spec.ConfigMap, metav1.GetOptions{}) 434 Expect(err).ShouldNot(HaveOccurred()) 435 436 By("Check pod created") 437 initialPods, err := c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).List(context.Background(), metav1.ListOptions{LabelSelector: "olm.configMapResourceVersion=" + configMap.ResourceVersion}) 438 Expect(err).ShouldNot(HaveOccurred()) 439 Expect(initialPods.Items).To(HaveLen(1)) 440 441 By("delete the first catalog") 442 cleanupSource() 443 444 By("create a catalog with the same name") 445 createInternalCatalogSource(c, crc, mainCatalogName, generatedNamespace.GetName(), append(mainManifests, dependentManifests...), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, []v1alpha1.ClusterServiceVersion{mainCSV, dependentCSV}) 446 447 By("Create Subscription") 448 subscriptionName := genName("sub-") 449 createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", v1alpha1.ApprovalAutomatic) 450 451 subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker()) 452 Expect(err).ShouldNot(HaveOccurred()) 453 Expect(subscription).ToNot(BeNil()) 454 _, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, buildCSVConditionChecker(v1alpha1.CSVPhaseSucceeded)) 455 Expect(err).ShouldNot(HaveOccurred()) 456 }) 457 458 It("gRPC address catalog source", func() { 459 460 By("Create an internal (configmap) CatalogSource with stable and dependency csv") 461 By("Create an internal (configmap) replacement CatalogSource with a stable, stable-replacement, and dependency csv") 462 By("Copy both configmap-server pods to the test namespace") 463 By("Delete both CatalogSources") 464 By("Create an \"address\" CatalogSource with a Spec.Address field set to the stable copied pod's PodIP") 465 By("Create a Subscription to the stable package") 466 By("Wait for the stable Subscription to be Successful") 467 By("Wait for the stable CSV to be Successful") 468 By("Update the \"address\" CatalogSources's Spec.Address field with the PodIP of the replacement copied pod's PodIP") 469 By("Wait for the replacement CSV to be Successful") 470 471 mainPackageName := genName("nginx-") 472 dependentPackageName := genName("nginxdep-") 473 474 mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName) 475 mainPackageReplacement := fmt.Sprintf("%s-replacement", mainPackageStable) 476 dependentPackageStable := fmt.Sprintf("%s-stable", dependentPackageName) 477 478 stableChannel := "stable" 479 480 dependentCRD := newCRD(genName("ins-")) 481 mainCSV := newCSV(mainPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil) 482 replacementCSV := newCSV(mainPackageReplacement, generatedNamespace.GetName(), mainPackageStable, semver.MustParse("0.2.0"), nil, []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil) 483 dependentCSV := newCSV(dependentPackageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, nil, nil) 484 485 defer func() { 486 Eventually(func() error { 487 return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), dependentCRD.GetName(), metav1.DeleteOptions{}) 488 }).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue()))) 489 Eventually(func() error { 490 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &mainCSV)) 491 }).Should(Succeed()) 492 Eventually(func() error { 493 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &dependentCSV)) 494 }).Should(Succeed()) 495 Eventually(func() error { 496 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &replacementCSV)) 497 }).Should(Succeed()) 498 }() 499 500 mainSourceName := genName("mock-ocs-main-") 501 replacementSourceName := genName("mock-ocs-main-with-replacement-") 502 503 By("Create separate manifests for each CatalogSource") 504 mainManifests := []registry.PackageManifest{ 505 { 506 PackageName: mainPackageName, 507 Channels: []registry.PackageChannel{ 508 {Name: stableChannel, CurrentCSVName: mainPackageStable}, 509 }, 510 DefaultChannelName: stableChannel, 511 }, 512 } 513 514 replacementManifests := []registry.PackageManifest{ 515 { 516 PackageName: mainPackageName, 517 Channels: []registry.PackageChannel{ 518 {Name: stableChannel, CurrentCSVName: mainPackageReplacement}, 519 }, 520 DefaultChannelName: stableChannel, 521 }, 522 } 523 524 dependentManifests := []registry.PackageManifest{ 525 { 526 PackageName: dependentPackageName, 527 Channels: []registry.PackageChannel{ 528 {Name: stableChannel, CurrentCSVName: dependentPackageStable}, 529 }, 530 DefaultChannelName: stableChannel, 531 }, 532 } 533 534 By("Create ConfigMap CatalogSources") 535 createInternalCatalogSource(c, crc, mainSourceName, generatedNamespace.GetName(), append(mainManifests, dependentManifests...), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, []v1alpha1.ClusterServiceVersion{mainCSV, dependentCSV}) 536 createInternalCatalogSource(c, crc, replacementSourceName, generatedNamespace.GetName(), append(replacementManifests, dependentManifests...), []apiextensionsv1.CustomResourceDefinition{dependentCRD}, []v1alpha1.ClusterServiceVersion{replacementCSV, mainCSV, dependentCSV}) 537 538 By("Wait for ConfigMap CatalogSources to be ready") 539 mainSource, err := fetchCatalogSourceOnStatus(crc, mainSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced()) 540 Expect(err).ShouldNot(HaveOccurred()) 541 replacementSource, err := fetchCatalogSourceOnStatus(crc, replacementSourceName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced()) 542 Expect(err).ShouldNot(HaveOccurred()) 543 544 By("Replicate catalog pods with no OwnerReferences") 545 mainCopy := replicateCatalogPod(c, mainSource) 546 mainCopy = awaitPod(GinkgoT(), c, mainCopy.GetNamespace(), mainCopy.GetName(), hasPodIP) 547 replacementCopy := replicateCatalogPod(c, replacementSource) 548 replacementCopy = awaitPod(GinkgoT(), c, replacementCopy.GetNamespace(), replacementCopy.GetName(), hasPodIP) 549 550 addressSourceName := genName("address-catalog-") 551 552 By("Create a CatalogSource pointing to the grpc pod") 553 addressSource := &v1alpha1.CatalogSource{ 554 TypeMeta: metav1.TypeMeta{ 555 Kind: v1alpha1.CatalogSourceKind, 556 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 557 }, 558 ObjectMeta: metav1.ObjectMeta{ 559 Name: addressSourceName, 560 Namespace: generatedNamespace.GetName(), 561 }, 562 Spec: v1alpha1.CatalogSourceSpec{ 563 SourceType: v1alpha1.SourceTypeGrpc, 564 Address: net.JoinHostPort(mainCopy.Status.PodIP, "50051"), 565 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 566 SecurityContextConfig: v1alpha1.Restricted, 567 }, 568 }, 569 } 570 571 addressSource, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Create(context.Background(), addressSource, metav1.CreateOptions{}) 572 Expect(err).ShouldNot(HaveOccurred()) 573 defer func() { 574 err := crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Delete(context.Background(), addressSourceName, metav1.DeleteOptions{}) 575 Expect(err).ShouldNot(HaveOccurred()) 576 }() 577 578 By("Wait for the CatalogSource to be ready") 579 _, err = fetchCatalogSourceOnStatus(crc, addressSource.GetName(), addressSource.GetNamespace(), catalogSourceRegistryPodSynced()) 580 Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready") 581 582 By("Delete CatalogSources") 583 err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Delete(context.Background(), mainSourceName, metav1.DeleteOptions{}) 584 Expect(err).ShouldNot(HaveOccurred()) 585 err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Delete(context.Background(), replacementSourceName, metav1.DeleteOptions{}) 586 Expect(err).ShouldNot(HaveOccurred()) 587 588 By("Create Subscription") 589 subscriptionName := genName("sub-") 590 cleanupSubscription := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, addressSourceName, mainPackageName, stableChannel, "", v1alpha1.ApprovalAutomatic) 591 defer cleanupSubscription() 592 593 subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker()) 594 Expect(err).ShouldNot(HaveOccurred()) 595 Expect(subscription).ShouldNot(BeNil()) 596 _, err = fetchCSV(crc, generatedNamespace.GetName(), subscription.Status.CurrentCSV, csvSucceededChecker) 597 Expect(err).ShouldNot(HaveOccurred()) 598 599 By("Update the catalog's address to point at the other registry pod's cluster ip") 600 Eventually(func() error { 601 addressSource, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Get(context.Background(), addressSourceName, metav1.GetOptions{}) 602 if err != nil { 603 return err 604 } 605 606 addressSource.Spec.Address = net.JoinHostPort(replacementCopy.Status.PodIP, "50051") 607 _, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Update(context.Background(), addressSource, metav1.UpdateOptions{}) 608 return err 609 }).Should(Succeed()) 610 611 By("Wait for the replacement CSV to be installed") 612 _, err = fetchCSV(crc, generatedNamespace.GetName(), replacementCSV.GetName(), csvSucceededChecker) 613 Expect(err).ShouldNot(HaveOccurred()) 614 }) 615 616 It("delete internal registry pod triggers recreation", func() { 617 618 By("Create internal CatalogSource containing csv in package") 619 By("Wait for a registry pod to be created") 620 By("Delete the registry pod") 621 By("Wait for a new registry pod to be created") 622 623 By("Create internal CatalogSource containing csv in package") 624 packageName := genName("nginx-") 625 packageStable := fmt.Sprintf("%s-stable", packageName) 626 stableChannel := "stable" 627 sourceName := genName("catalog-") 628 crd := newCRD(genName("ins-")) 629 csv := newCSV(packageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil) 630 manifests := []registry.PackageManifest{ 631 { 632 PackageName: packageName, 633 Channels: []registry.PackageChannel{ 634 {Name: stableChannel, CurrentCSVName: packageStable}, 635 }, 636 DefaultChannelName: stableChannel, 637 }, 638 } 639 640 defer func() { 641 Eventually(func() error { 642 return ctx.Ctx().KubeClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crd.GetName(), metav1.DeleteOptions{}) 643 }).Should(Or(Succeed(), WithTransform(k8serror.IsNotFound, BeTrue()))) 644 Eventually(func() error { 645 return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &csv)) 646 }).Should(Succeed()) 647 }() 648 649 _, cleanupSource := createInternalCatalogSource(c, crc, sourceName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []v1alpha1.ClusterServiceVersion{csv}) 650 defer cleanupSource() 651 652 By("Wait for a new registry pod to be created") 653 selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": sourceName}) 654 singlePod := podCount(1) 655 registryPods, err := awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), singlePod) 656 Expect(err).ShouldNot(HaveOccurred(), "error awaiting registry pod") 657 Expect(registryPods).ToNot(BeNil(), "nil registry pods") 658 Expect(registryPods.Items).To(HaveLen(1), "unexpected number of registry pods found") 659 660 By("Store the UID for later comparison") 661 uid := registryPods.Items[0].GetUID() 662 663 By("Delete the registry pod") 664 Eventually(func() error { 665 backgroundDeletion := metav1.DeletePropagationBackground 666 return c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{PropagationPolicy: &backgroundDeletion}, metav1.ListOptions{LabelSelector: selector.String()}) 667 }).Should(Succeed()) 668 669 By("Wait for a new registry pod to be created") 670 notUID := func(pods *corev1.PodList) bool { 671 uids := make([]string, 0) 672 for _, pod := range pods.Items { 673 uids = append(uids, string(pod.GetUID())) 674 if pod.GetUID() == uid { 675 ctx.Ctx().Logf("waiting for %v not to contain %s", uids, uid) 676 return false 677 } 678 } 679 ctx.Ctx().Logf("waiting for %v to not be empty and not contain %s", uids, uid) 680 return len(pods.Items) > 0 681 } 682 registryPods, err = awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), unionPodsCheck(singlePod, notUID)) 683 Expect(err).ShouldNot(HaveOccurred(), "error waiting for replacement registry pod") 684 Expect(registryPods).ToNot(BeNil(), "nil replacement registry pods") 685 Expect(registryPods.Items).To(HaveLen(1), "unexpected number of replacement registry pods found") 686 }) 687 688 It("delete gRPC registry pod triggers recreation", func() { 689 690 By("Create gRPC CatalogSource using an external registry image (community-operators)") 691 By("Wait for a registry pod to be created") 692 By("Delete the registry pod") 693 By("Wait for a new registry pod to be created") 694 695 By("Create gRPC CatalogSource using an external registry image (community-operators)") 696 source := &v1alpha1.CatalogSource{ 697 TypeMeta: metav1.TypeMeta{ 698 Kind: v1alpha1.CatalogSourceKind, 699 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 700 }, 701 ObjectMeta: metav1.ObjectMeta{ 702 Name: genName("catalog-"), 703 Namespace: generatedNamespace.GetName(), 704 }, 705 Spec: v1alpha1.CatalogSourceSpec{ 706 SourceType: v1alpha1.SourceTypeGrpc, 707 Image: communityOperatorsImage, 708 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 709 SecurityContextConfig: v1alpha1.Restricted, 710 }, 711 }, 712 } 713 714 source, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 715 Expect(err).ShouldNot(HaveOccurred()) 716 717 By("Wait for a new registry pod to be created") 718 selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": source.GetName()}) 719 singlePod := podCount(1) 720 registryPods, err := awaitPods(GinkgoT(), c, source.GetNamespace(), selector.String(), singlePod) 721 Expect(err).ShouldNot(HaveOccurred(), "error awaiting registry pod") 722 Expect(registryPods).ToNot(BeNil(), "nil registry pods") 723 Expect(registryPods.Items).To(HaveLen(1), "unexpected number of registry pods found") 724 725 By("Store the UID for later comparison") 726 uid := registryPods.Items[0].GetUID() 727 728 By("Delete the registry pod") 729 Eventually(func() error { 730 backgroundDeletion := metav1.DeletePropagationBackground 731 return c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).DeleteCollection(context.Background(), metav1.DeleteOptions{PropagationPolicy: &backgroundDeletion}, metav1.ListOptions{LabelSelector: selector.String()}) 732 }).Should(Succeed()) 733 734 By("Wait for a new registry pod to be created") 735 notUID := func(pods *corev1.PodList) bool { 736 uids := make([]string, 0) 737 for _, pod := range pods.Items { 738 uids = append(uids, string(pod.GetUID())) 739 if pod.GetUID() == uid { 740 ctx.Ctx().Logf("waiting for %v not to contain %s", uids, uid) 741 return false 742 } 743 } 744 ctx.Ctx().Logf("waiting for %v to not be empty and not contain %s", uids, uid) 745 return len(pods.Items) > 0 746 } 747 registryPods, err = awaitPods(GinkgoT(), c, generatedNamespace.GetName(), selector.String(), unionPodsCheck(singlePod, notUID)) 748 Expect(err).ShouldNot(HaveOccurred(), "error waiting for replacement registry pod") 749 Expect(registryPods).ShouldNot(BeNil(), "nil replacement registry pods") 750 Expect(registryPods.Items).To(HaveLen(1), "unexpected number of replacement registry pods found") 751 }) 752 753 It("configure gRPC registry pod to extract content", func() { 754 755 By("Create gRPC CatalogSource using an external registry image (community-operators)") 756 source := &v1alpha1.CatalogSource{ 757 TypeMeta: metav1.TypeMeta{ 758 Kind: v1alpha1.CatalogSourceKind, 759 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 760 }, 761 ObjectMeta: metav1.ObjectMeta{ 762 Name: genName("catalog-"), 763 Namespace: generatedNamespace.GetName(), 764 }, 765 Spec: v1alpha1.CatalogSourceSpec{ 766 SourceType: v1alpha1.SourceTypeGrpc, 767 Image: communityOperatorsImage, 768 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 769 SecurityContextConfig: v1alpha1.Restricted, 770 ExtractContent: &v1alpha1.ExtractContentConfig{ 771 CacheDir: "/tmp/cache", 772 CatalogDir: "/configs", 773 }, 774 }, 775 }, 776 } 777 778 source, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 779 Expect(err).ShouldNot(HaveOccurred()) 780 781 By("Wait for the CatalogSource to be ready") 782 source, err = fetchCatalogSourceOnStatus(crc, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced()) 783 Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready") 784 785 By("the gRPC endpoints are not exposed from the pod, and there's no simple way to get at them -") 786 By("the index images don't contain `grpcurl`, port-forwarding is a mess, etc. let's use the") 787 By("package-server as a proxy for a functional catalog") 788 By("Waiting for packages from the catalog to show up in the Kubernetes API") 789 Eventually(func() error { 790 manifests, err := packageserverClient.OperatorsV1().PackageManifests("default").List(context.Background(), metav1.ListOptions{}) 791 if err != nil { 792 return err 793 } 794 if len(manifests.Items) == 0 { 795 return errors.New("did not find any PackageManifests") 796 } 797 return nil 798 }).Should(Succeed()) 799 }) 800 801 It("image update", func() { 802 if ok, err := inKind(c); ok && err == nil { 803 Skip("This spec fails when run using KIND cluster. See https://github.com/operator-framework/operator-lifecycle-manager/issues/2420 for more details") 804 } else if err != nil { 805 Skip("Could not determine whether running in a kind cluster. Skipping.") 806 } 807 By("Create an image based catalog source from public Quay image") 808 By("Use a unique tag as identifier") 809 By("See https://quay.io/repository/olmtest/catsrc-update-test?namespace=olmtest for registry") 810 By("Push an updated version of the image with the same identifier") 811 By("Confirm catalog source polling feature is working as expected: a newer version of the catalog source pod comes up") 812 By("etcd operator updated from 0.9.0 to 0.9.2-clusterwide") 813 By("Subscription should detect the latest version of the operator in the new catalog source and pull it") 814 815 By("create internal registry for purposes of pushing/pulling IF running e2e test locally") 816 By("registry is insecure and for purposes of this test only") 817 818 local, err := Local(c) 819 Expect(err).NotTo(HaveOccurred(), "cannot determine if test running locally or on CI: %s", err) 820 821 By("Create an image based catalog source from public Quay image using a unique tag as identifier") 822 var registryURL string 823 var registryAuthSecretName string 824 if local { 825 By("Creating a local registry to use") 826 registryURL, err = createDockerRegistry(c, generatedNamespace.GetName()) 827 Expect(err).NotTo(HaveOccurred(), "error creating container registry: %s", err) 828 defer deleteDockerRegistry(c, generatedNamespace.GetName()) 829 830 By("ensure registry pod with local URL " + registryURL + " is ready before attempting port-forwarding") 831 _ = awaitPod(GinkgoT(), c, generatedNamespace.GetName(), registryName, podReady) 832 833 By("By port-forwarding to the registry") 834 err = registryPortForward(generatedNamespace.GetName()) 835 Expect(err).NotTo(HaveOccurred(), "port-forwarding local registry: %s", err) 836 } else { 837 registryURL = fmt.Sprintf("%s/%s", openshiftregistryFQDN, generatedNamespace.GetName()) 838 By("Using the OpenShift registry at " + registryURL) 839 registryAuthSecretName, err = getRegistryAuthSecretName(c, generatedNamespace.GetName()) 840 Expect(err).NotTo(HaveOccurred(), "error getting openshift registry authentication: %s", err) 841 } 842 843 By("testImage is the name of the image used throughout the test - the image overwritten by skopeo") 844 By("the tag is generated randomly and appended to the end of the testImage") 845 testImage := fmt.Sprint("docker://", registryURL, "/catsrc-update", ":") 846 tag := genName("x") 847 By("Generating a target test image name " + testImage + " with tag " + tag) 848 849 By("copying old catalog image into test-specific tag in internal docker registry") 850 if local { 851 By("executing out to a local skopeo client") 852 _, err := skopeoLocalCopy(testImage, tag, catsrcImage, "old") 853 Expect(err).NotTo(HaveOccurred(), "error copying old registry file: %s", err) 854 } else { 855 By("creating a skopoeo Pod to do the copying") 856 skopeoArgs := skopeoCopyCmd(testImage, tag, catsrcImage, "old", registryAuthSecretName) 857 err = createSkopeoPod(c, skopeoArgs, generatedNamespace.GetName(), registryAuthSecretName) 858 Expect(err).NotTo(HaveOccurred(), "error creating skopeo pod: %s", err) 859 860 By("waiting for the skopeo pod to exit successfully") 861 awaitPod(GinkgoT(), c, generatedNamespace.GetName(), skopeo, func(pod *corev1.Pod) bool { 862 return pod.Status.Phase == corev1.PodSucceeded 863 }) 864 865 By("removing the skopeo pod") 866 err = deleteSkopeoPod(c, generatedNamespace.GetName()) 867 Expect(err).NotTo(HaveOccurred(), "error deleting skopeo pod: %s", err) 868 } 869 870 By("setting catalog source") 871 sourceName := genName("catalog-") 872 packageName := "busybox" 873 channelName := "alpha" 874 875 By("Create gRPC CatalogSource using an external registry image and poll interval") 876 var image string 877 image = testImage[9:] // strip off docker:// 878 image = fmt.Sprint(image, tag) 879 880 source := &v1alpha1.CatalogSource{ 881 TypeMeta: metav1.TypeMeta{ 882 Kind: v1alpha1.CatalogSourceKind, 883 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 884 }, 885 ObjectMeta: metav1.ObjectMeta{ 886 Name: sourceName, 887 Namespace: generatedNamespace.GetName(), 888 Labels: map[string]string{"olm.catalogSource": sourceName}, 889 }, 890 Spec: v1alpha1.CatalogSourceSpec{ 891 SourceType: v1alpha1.SourceTypeGrpc, 892 Image: image, 893 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 894 SecurityContextConfig: v1alpha1.Restricted, 895 }, 896 UpdateStrategy: &v1alpha1.UpdateStrategy{ 897 RegistryPoll: &v1alpha1.RegistryPoll{ 898 // Using RawInterval rather than Interval due to this issue: 899 // https://github.com/operator-framework/operator-lifecycle-manager/issues/2621 900 RawInterval: "1m0s", 901 }, 902 }, 903 }, 904 } 905 906 source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 907 Expect(err).ShouldNot(HaveOccurred()) 908 defer func() { 909 err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Delete(context.Background(), source.GetName(), metav1.DeleteOptions{}) 910 Expect(err).ShouldNot(HaveOccurred()) 911 }() 912 913 By("wait for new catalog source pod to be created") 914 By("Wait for a new registry pod to be created") 915 selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": source.GetName()}) 916 singlePod := podCount(1) 917 registryPods, err := awaitPods(GinkgoT(), c, source.GetNamespace(), selector.String(), singlePod) 918 Expect(err).ToNot(HaveOccurred(), "error awaiting registry pod") 919 Expect(registryPods).ShouldNot(BeNil(), "nil registry pods") 920 Expect(registryPods.Items).To(HaveLen(1), "unexpected number of registry pods found") 921 922 By("Create a Subscription for package") 923 subscriptionName := genName("sub-") 924 cleanupSubscription := createSubscriptionForCatalog(crc, source.GetNamespace(), subscriptionName, source.GetName(), packageName, channelName, "", v1alpha1.ApprovalAutomatic) 925 defer cleanupSubscription() 926 927 By("Wait for the Subscription to succeed") 928 subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker()) 929 Expect(err).ShouldNot(HaveOccurred()) 930 Expect(subscription).ShouldNot(BeNil()) 931 932 By("Wait for csv to succeed") 933 _, err = fetchCSV(crc, subscription.GetNamespace(), subscription.Status.CurrentCSV, csvSucceededChecker) 934 Expect(err).ShouldNot(HaveOccurred()) 935 936 registryCheckFunc := func(podList *corev1.PodList) bool { 937 if len(podList.Items) > 1 { 938 return false 939 } 940 return podList.Items[0].Status.ContainerStatuses[0].ImageID != "" 941 } 942 By("get old catalog source pod") 943 registryPod, err := awaitPods(GinkgoT(), c, source.GetNamespace(), selector.String(), registryCheckFunc) 944 By("Updateing image on registry to trigger a newly updated version of the catalog source pod to be deployed after some time") 945 if local { 946 By("executing out to a local skopeo client") 947 _, err := skopeoLocalCopy(testImage, tag, catsrcImage, "new") 948 Expect(err).NotTo(HaveOccurred(), "error copying new registry file: %s", err) 949 } else { 950 By("creating a skopoeo Pod to do the copying") 951 skopeoArgs := skopeoCopyCmd(testImage, tag, catsrcImage, "new", registryAuthSecretName) 952 err = createSkopeoPod(c, skopeoArgs, generatedNamespace.GetName(), registryAuthSecretName) 953 Expect(err).NotTo(HaveOccurred(), "error creating skopeo pod: %s", err) 954 955 By("waiting for the skopeo pod to exit successfully") 956 awaitPod(GinkgoT(), c, generatedNamespace.GetName(), skopeo, func(pod *corev1.Pod) bool { 957 return pod.Status.Phase == corev1.PodSucceeded 958 }) 959 960 By("removing the skopeo pod") 961 err = deleteSkopeoPod(c, generatedNamespace.GetName()) 962 Expect(err).NotTo(HaveOccurred(), "error deleting skopeo pod: %s", err) 963 } 964 965 By("update catalog source with annotation (to kick resync)") 966 Eventually(func() error { 967 source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{}) 968 if err != nil { 969 return nil 970 } 971 972 source.Annotations = make(map[string]string) 973 source.Annotations["testKey"] = "testValue" 974 _, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), source, metav1.UpdateOptions{}) 975 return err 976 }).Should(Succeed()) 977 978 By("ensure new registry pod container image is as we expect") 979 podCheckFunc := func(podList *corev1.PodList) bool { 980 ctx.Ctx().Logf("pod list length %d\n", len(podList.Items)) 981 for _, pod := range podList.Items { 982 ctx.Ctx().Logf("pod list name %v\n", pod.Name) 983 } 984 985 for _, pod := range podList.Items { 986 ctx.Ctx().Logf("old image id %s\n new image id %s\n", registryPod.Items[0].Status.ContainerStatuses[0].ImageID, 987 pod.Status.ContainerStatuses[0].ImageID) 988 if pod.Status.ContainerStatuses[0].ImageID != registryPod.Items[0].Status.ContainerStatuses[0].ImageID { 989 return true 990 } 991 } 992 By("update catalog source with annotation (to kick resync)") 993 Eventually(func() error { 994 source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{}) 995 if err != nil { 996 return err 997 } 998 999 if source.Annotations == nil { 1000 source.Annotations = make(map[string]string) 1001 } 1002 1003 source.Annotations["testKey"] = genName("newValue") 1004 _, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), source, metav1.UpdateOptions{}) 1005 return err 1006 }).Should(Succeed()) 1007 1008 return false 1009 } 1010 By("await new catalog source and ensure old one was deleted") 1011 registryPods, err = awaitPodsWithInterval(GinkgoT(), c, source.GetNamespace(), selector.String(), 30*time.Second, 10*time.Minute, podCheckFunc) 1012 Expect(err).ShouldNot(HaveOccurred(), "error awaiting registry pod") 1013 Expect(registryPods).ShouldNot(BeNil(), "nil registry pods") 1014 Expect(registryPods.Items).To(HaveLen(1), "unexpected number of registry pods found") 1015 1016 By("update catalog source with annotation (to kick resync)") 1017 Eventually(func() error { 1018 source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{}) 1019 if err != nil { 1020 return err 1021 } 1022 1023 source.Annotations["testKey"] = "newValue" 1024 _, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), source, metav1.UpdateOptions{}) 1025 return err 1026 }).Should(Succeed()) 1027 1028 subChecker := func(sub *v1alpha1.Subscription) bool { 1029 return sub.Status.InstalledCSV == "busybox.v2.0.0" 1030 } 1031 By("Wait for the Subscription to succeed") 1032 subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subChecker) 1033 Expect(err).ShouldNot(HaveOccurred()) 1034 Expect(subscription).ShouldNot(BeNil()) 1035 1036 By("Wait for csv to succeed") 1037 csv, err := fetchCSV(crc, subscription.GetNamespace(), subscription.Status.CurrentCSV, csvSucceededChecker) 1038 Expect(err).ShouldNot(HaveOccurred()) 1039 1040 By("check version of running csv to ensure the latest version (0.9.2) was installed onto the cluster") 1041 v := csv.Spec.Version 1042 busyboxVersion := semver.Version{ 1043 Major: 2, 1044 Minor: 0, 1045 Patch: 0, 1046 } 1047 1048 Expect(v).Should(Equal(version.OperatorVersion{Version: busyboxVersion}), "latest version of operator not installed: catalog source update failed") 1049 }) 1050 1051 It("Dependency has correct replaces field", func() { 1052 By("Create a CatalogSource that contains the busybox v1 and busybox-dependency v1 images") 1053 By("Create a Subscription for busybox v1, which has a dependency on busybox-dependency v1.") 1054 By("Wait for the busybox and busybox2 Subscriptions to succeed") 1055 By("Wait for the CSVs to succeed") 1056 By("Update the catalog to point to an image that contains the busybox v2 and busybox-dependency v2 images.") 1057 By("Wait for the new Subscriptions to succeed and check if they include the new CSVs") 1058 By("Wait for the CSVs to succeed and confirm that the have the correct Spec.Replaces fields.") 1059 1060 sourceName := genName("catalog-") 1061 packageName := "busybox" 1062 channelName := "alpha" 1063 1064 catSrcImage := "quay.io/olmtest/busybox-dependencies-index" 1065 1066 By("creating gRPC CatalogSource") 1067 source := &v1alpha1.CatalogSource{ 1068 TypeMeta: metav1.TypeMeta{ 1069 Kind: v1alpha1.CatalogSourceKind, 1070 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 1071 }, 1072 ObjectMeta: metav1.ObjectMeta{ 1073 Name: sourceName, 1074 Namespace: generatedNamespace.GetName(), 1075 }, 1076 Spec: v1alpha1.CatalogSourceSpec{ 1077 SourceType: v1alpha1.SourceTypeGrpc, 1078 Image: catSrcImage + ":1.0.0-with-ListBundles-method", 1079 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 1080 SecurityContextConfig: v1alpha1.Restricted, 1081 }, 1082 }, 1083 } 1084 source, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 1085 Expect(err).ShouldNot(HaveOccurred()) 1086 defer func() { 1087 err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Delete(context.Background(), source.GetName(), metav1.DeleteOptions{}) 1088 Expect(err).ShouldNot(HaveOccurred()) 1089 }() 1090 1091 By("waiting for the CatalogSource to be ready") 1092 _, err = fetchCatalogSourceOnStatus(crc, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced()) 1093 Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready") 1094 1095 By("creating a Subscription for busybox") 1096 subscriptionName := genName("sub-") 1097 cleanupSubscription := createSubscriptionForCatalog(crc, source.GetNamespace(), subscriptionName, source.GetName(), packageName, channelName, "", v1alpha1.ApprovalAutomatic) 1098 defer cleanupSubscription() 1099 1100 By("waiting for the Subscription to succeed") 1101 subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionStateAtLatestChecker()) 1102 Expect(err).ShouldNot(HaveOccurred()) 1103 Expect(subscription).ShouldNot(BeNil()) 1104 Expect(subscription.Status.InstalledCSV).To(Equal("busybox.v1.0.0")) 1105 1106 By("confirming that a subscription was created for busybox-dependency") 1107 subscriptionList, err := crc.OperatorsV1alpha1().Subscriptions(source.GetNamespace()).List(context.Background(), metav1.ListOptions{}) 1108 Expect(err).ShouldNot(HaveOccurred()) 1109 dependencySubscriptionName := "" 1110 for _, sub := range subscriptionList.Items { 1111 if strings.HasPrefix(sub.GetName(), "busybox-dependency") { 1112 dependencySubscriptionName = sub.GetName() 1113 } 1114 } 1115 Expect(dependencySubscriptionName).ToNot(BeEmpty()) 1116 1117 By("waiting for the Subscription to succeed") 1118 subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), dependencySubscriptionName, subscriptionStateAtLatestChecker()) 1119 Expect(err).ShouldNot(HaveOccurred()) 1120 Expect(subscription).ShouldNot(BeNil()) 1121 Expect(subscription.Status.InstalledCSV).To(Equal("busybox-dependency.v1.0.0")) 1122 1123 By("updating the catalog image") 1124 Eventually(func() error { 1125 existingSource, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), sourceName, metav1.GetOptions{}) 1126 if err != nil { 1127 return err 1128 } 1129 existingSource.Spec.Image = catSrcImage + ":2.0.0-with-ListBundles-method" 1130 1131 source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), existingSource, metav1.UpdateOptions{}) 1132 return err 1133 }).Should(Succeed()) 1134 1135 By("waiting for the CatalogSource to be ready") 1136 _, err = fetchCatalogSourceOnStatus(crc, source.GetName(), source.GetNamespace(), catalogSourceRegistryPodSynced()) 1137 Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready") 1138 1139 By("waiting for the busybox v2 Subscription to succeed") 1140 subChecker := func(sub *v1alpha1.Subscription) bool { 1141 return sub.Status.InstalledCSV == "busybox.v2.0.0" 1142 } 1143 subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subChecker) 1144 Expect(err).ShouldNot(HaveOccurred()) 1145 Expect(subscription).ShouldNot(BeNil()) 1146 1147 By("waiting for busybox v2 csv to succeed and check the replaces field") 1148 csv, err := fetchCSV(crc, subscription.GetNamespace(), subscription.Status.CurrentCSV, csvSucceededChecker) 1149 Expect(err).ShouldNot(HaveOccurred()) 1150 Expect(csv.Spec.Replaces).To(Equal("busybox.v1.0.0")) 1151 1152 By("waiting for the busybox-dependency v2 Subscription to succeed") 1153 subChecker = func(sub *v1alpha1.Subscription) bool { 1154 return sub.Status.InstalledCSV == "busybox-dependency.v2.0.0" 1155 } 1156 subscription, err = fetchSubscription(crc, generatedNamespace.GetName(), dependencySubscriptionName, subChecker) 1157 Expect(err).ShouldNot(HaveOccurred()) 1158 Expect(subscription).ShouldNot(BeNil()) 1159 1160 By("waiting for busybox-dependency v2 csv to succeed and check the replaces field") 1161 csv, err = fetchCSV(crc, subscription.GetNamespace(), subscription.Status.CurrentCSV, csvSucceededChecker) 1162 Expect(err).ShouldNot(HaveOccurred()) 1163 Expect(csv.Spec.Replaces).To(Equal("busybox-dependency.v1.0.0")) 1164 }) 1165 When("A catalogSource is created with correct polling interval", func() { 1166 var source *v1alpha1.CatalogSource 1167 singlePod := podCount(1) 1168 sourceName := genName("catalog-") 1169 1170 BeforeEach(func() { 1171 source = &v1alpha1.CatalogSource{ 1172 TypeMeta: metav1.TypeMeta{ 1173 Kind: v1alpha1.CatalogSourceKind, 1174 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 1175 }, 1176 ObjectMeta: metav1.ObjectMeta{ 1177 Name: sourceName, 1178 Namespace: generatedNamespace.GetName(), 1179 Labels: map[string]string{"olm.catalogSource": sourceName}, 1180 }, 1181 Spec: v1alpha1.CatalogSourceSpec{ 1182 SourceType: v1alpha1.SourceTypeGrpc, 1183 Image: "quay.io/olmtest/catsrc-update-test:new", 1184 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 1185 SecurityContextConfig: v1alpha1.Restricted, 1186 }, 1187 UpdateStrategy: &v1alpha1.UpdateStrategy{ 1188 RegistryPoll: &v1alpha1.RegistryPoll{ 1189 RawInterval: "45s", 1190 }, 1191 }, 1192 }, 1193 } 1194 1195 source, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 1196 Expect(err).ToNot(HaveOccurred()) 1197 1198 By("wait for new catalog source pod to be created and report ready") 1199 selector := labels.SelectorFromSet(map[string]string{"olm.catalogSource": source.GetName()}) 1200 1201 catalogPods, err := awaitPods(GinkgoT(), c, source.GetNamespace(), selector.String(), singlePod) 1202 Expect(err).ToNot(HaveOccurred()) 1203 Expect(catalogPods).ToNot(BeNil()) 1204 1205 Eventually(func() (bool, error) { 1206 podList, err := c.KubernetesInterface().CoreV1().Pods(source.GetNamespace()).List(context.Background(), metav1.ListOptions{LabelSelector: selector.String()}) 1207 if err != nil { 1208 return false, err 1209 } 1210 1211 for _, p := range podList.Items { 1212 if podReady(&p) { 1213 return true, nil 1214 } 1215 return false, nil 1216 } 1217 1218 return false, nil 1219 }).Should(BeTrue()) 1220 }) 1221 1222 It("registry polls on the correct interval", func() { 1223 By("Wait roughly the polling interval for update pod to show up") 1224 updateSelector := labels.SelectorFromSet(map[string]string{"catalogsource.operators.coreos.com/update": source.GetName()}) 1225 updatePods, err := awaitPodsWithInterval(GinkgoT(), c, source.GetNamespace(), updateSelector.String(), 5*time.Second, 2*time.Minute, singlePod) 1226 Expect(err).ToNot(HaveOccurred()) 1227 Expect(updatePods).ToNot(BeNil()) 1228 Expect(updatePods.Items).To(HaveLen(1)) 1229 1230 By("No update to image: update pod should be deleted quickly") 1231 noPod := podCount(0) 1232 updatePods, err = awaitPodsWithInterval(GinkgoT(), c, source.GetNamespace(), updateSelector.String(), 1*time.Second, 30*time.Second, noPod) 1233 Expect(err).ToNot(HaveOccurred()) 1234 Expect(updatePods.Items).To(HaveLen(0)) 1235 }) 1236 1237 }) 1238 1239 When("A catalogSource is created with incorrect polling interval", func() { 1240 var ( 1241 source *v1alpha1.CatalogSource 1242 sourceName string 1243 ) 1244 1245 const ( 1246 incorrectInterval = "45mError.code" 1247 correctInterval = "45m" 1248 ) 1249 1250 BeforeEach(func() { 1251 sourceName = genName("catalog-") 1252 source = &v1alpha1.CatalogSource{ 1253 TypeMeta: metav1.TypeMeta{ 1254 Kind: v1alpha1.CatalogSourceKind, 1255 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 1256 }, 1257 ObjectMeta: metav1.ObjectMeta{ 1258 Name: sourceName, 1259 Namespace: generatedNamespace.GetName(), 1260 Labels: map[string]string{"olm.catalogSource": sourceName}, 1261 }, 1262 Spec: v1alpha1.CatalogSourceSpec{ 1263 SourceType: v1alpha1.SourceTypeGrpc, 1264 Image: "quay.io/olmtest/catsrc-update-test:new", 1265 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 1266 SecurityContextConfig: v1alpha1.Restricted, 1267 }, 1268 UpdateStrategy: &v1alpha1.UpdateStrategy{ 1269 RegistryPoll: &v1alpha1.RegistryPoll{ 1270 RawInterval: incorrectInterval, 1271 }, 1272 }, 1273 }, 1274 } 1275 1276 _, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 1277 Expect(err).ToNot(HaveOccurred()) 1278 1279 }) 1280 AfterEach(func() { 1281 err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Delete(context.Background(), source.GetName(), metav1.DeleteOptions{}) 1282 Expect(err).ToNot(HaveOccurred()) 1283 }) 1284 It("the catalogsource status communicates that a default interval time is being used instead", func() { 1285 Eventually(func() bool { 1286 catsrc, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{}) 1287 Expect(err).ToNot(HaveOccurred()) 1288 if catsrc.Status.Reason == v1alpha1.CatalogSourceIntervalInvalidError { 1289 if catsrc.Status.Message == "error parsing spec.updateStrategy.registryPoll.interval. Using the default value of 15m0s instead. Error: time: unknown unit \"mError\" in duration \"45mError.code\"" { 1290 return true 1291 } 1292 } 1293 return false 1294 }).Should(BeTrue()) 1295 }) 1296 When("the catalogsource is updated with a valid polling interval", func() { 1297 1298 BeforeEach(func() { 1299 Eventually(func() error { 1300 catsrc, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{}) 1301 if err != nil { 1302 return err 1303 } 1304 catsrc.Spec.UpdateStrategy.RegistryPoll.RawInterval = correctInterval 1305 _, err = crc.OperatorsV1alpha1().CatalogSources(catsrc.GetNamespace()).Update(context.Background(), catsrc, metav1.UpdateOptions{}) 1306 return err 1307 }).Should(Succeed()) 1308 }) 1309 1310 It("the catalogsource spec shows the updated polling interval, and the error message in the status is cleared", func() { 1311 Eventually(func() error { 1312 catsrc, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{}) 1313 if err != nil { 1314 return err 1315 } 1316 expectedTime, err := time.ParseDuration(correctInterval) 1317 if err != nil { 1318 return err 1319 } 1320 if catsrc.Status.Reason != "" || (catsrc.Spec.UpdateStrategy.Interval != &metav1.Duration{Duration: expectedTime}) { 1321 return err 1322 } 1323 return nil 1324 }).Should(Succeed()) 1325 }) 1326 }) 1327 }) 1328 1329 It("adding catalog template adjusts image used", func() { 1330 By("This test attempts to create a catalog source, and update it with a template annotation") 1331 By("and ensure that the image gets changed according to what's in the template as well as") 1332 By("check the status conditions are updated accordingly") 1333 sourceName := genName("catalog-") 1334 source := &v1alpha1.CatalogSource{ 1335 TypeMeta: metav1.TypeMeta{ 1336 Kind: v1alpha1.CatalogSourceKind, 1337 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 1338 }, 1339 ObjectMeta: metav1.ObjectMeta{ 1340 Name: sourceName, 1341 Namespace: generatedNamespace.GetName(), 1342 Labels: map[string]string{"olm.catalogSource": sourceName}, 1343 }, 1344 Spec: v1alpha1.CatalogSourceSpec{ 1345 SourceType: v1alpha1.SourceTypeGrpc, 1346 Image: "quay.io/olmtest/catsrc-update-test:old", 1347 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 1348 SecurityContextConfig: v1alpha1.Restricted, 1349 }, 1350 }, 1351 } 1352 1353 By("creating a catalog source") 1354 1355 var err error 1356 Eventually(func() error { 1357 source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 1358 return err 1359 }).Should(Succeed()) 1360 1361 By("updating the catalog source with template annotation") 1362 1363 Eventually(func() error { 1364 source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), source.GetName(), metav1.GetOptions{}) 1365 if err != nil { 1366 return err 1367 } 1368 By("create an annotation using the kube templates") 1369 source.SetAnnotations(map[string]string{ 1370 catalogsource.CatalogImageTemplateAnnotation: fmt.Sprintf("quay.io/olmtest/catsrc-update-test:%s.%s.%s", catalogsource.TemplKubeMajorV, catalogsource.TemplKubeMinorV, catalogsource.TemplKubePatchV), 1371 }) 1372 1373 By("Update the catalog image") 1374 _, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Update(context.Background(), source, metav1.UpdateOptions{}) 1375 return err 1376 }).Should(Succeed()) 1377 1378 By("wait for status condition to show up") 1379 Eventually(func() (bool, error) { 1380 source, err = crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Get(context.Background(), sourceName, metav1.GetOptions{}) 1381 if err != nil { 1382 return false, err 1383 } 1384 By("if the conditions array has the entry we know things got updated") 1385 condition := meta.FindStatusCondition(source.Status.Conditions, catalogtemplate.StatusTypeTemplatesHaveResolved) 1386 if condition != nil { 1387 return true, nil 1388 } 1389 1390 return false, nil 1391 }).Should(BeTrue()) 1392 1393 By("source should be the latest we got from the eventually block") 1394 Expect(source.Status.Conditions).ToNot(BeNil()) 1395 1396 templatesResolvedCondition := meta.FindStatusCondition(source.Status.Conditions, catalogtemplate.StatusTypeTemplatesHaveResolved) 1397 if Expect(templatesResolvedCondition).ToNot(BeNil()) { 1398 Expect(templatesResolvedCondition.Reason).To(BeIdenticalTo(catalogtemplate.ReasonAllTemplatesResolved)) 1399 Expect(templatesResolvedCondition.Status).To(BeIdenticalTo(metav1.ConditionTrue)) 1400 } 1401 resolvedImageCondition := meta.FindStatusCondition(source.Status.Conditions, catalogtemplate.StatusTypeResolvedImage) 1402 if Expect(resolvedImageCondition).ToNot(BeNil()) { 1403 Expect(resolvedImageCondition.Reason).To(BeIdenticalTo(catalogtemplate.ReasonAllTemplatesResolved)) 1404 Expect(resolvedImageCondition.Status).To(BeIdenticalTo(metav1.ConditionTrue)) 1405 1406 By("if we can, try to determine the server version so we can check the resulting image") 1407 if serverVersion, err := crc.Discovery().ServerVersion(); err != nil { 1408 if serverGitVersion, err := semver.Parse(serverVersion.GitVersion); err != nil { 1409 expectedImage := fmt.Sprintf("quay.io/olmtest/catsrc-update-test:%s.%s.%s", serverVersion.Major, serverVersion.Minor, strconv.FormatUint(serverGitVersion.Patch, 10)) 1410 Expect(resolvedImageCondition.Message).To(BeIdenticalTo(expectedImage)) 1411 } 1412 } 1413 } 1414 }) 1415 1416 When("A CatalogSource is created with an operator that has a CSV with missing metadata.ApiVersion", func() { 1417 var ( 1418 magicCatalog *MagicCatalog 1419 catalogSourceName string 1420 subscription *v1alpha1.Subscription 1421 c client.Client 1422 ) 1423 1424 BeforeEach(func() { 1425 c = ctx.Ctx().Client() 1426 1427 provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, badCSVDir, "bad-csv.yaml")) 1428 Expect(err).To(BeNil()) 1429 1430 catalogSourceName = genName("cat-bad-csv") 1431 magicCatalog = NewMagicCatalog(c, generatedNamespace.GetName(), catalogSourceName, provider) 1432 Expect(magicCatalog.DeployCatalog(context.Background())).To(BeNil()) 1433 1434 }) 1435 1436 AfterEach(func() { 1437 TeardownNamespace(generatedNamespace.GetName()) 1438 }) 1439 1440 When("A Subscription is created catalogSource built with the malformed CSV", func() { 1441 1442 BeforeEach(func() { 1443 subscription = &v1alpha1.Subscription{ 1444 ObjectMeta: metav1.ObjectMeta{ 1445 Name: fmt.Sprintf("%s-sub", catalogSourceName), 1446 Namespace: generatedNamespace.GetName(), 1447 }, 1448 Spec: &v1alpha1.SubscriptionSpec{ 1449 CatalogSource: catalogSourceName, 1450 CatalogSourceNamespace: generatedNamespace.GetName(), 1451 Channel: "stable", 1452 Package: "test-package", 1453 }, 1454 } 1455 Expect(c.Create(context.Background(), subscription)).To(BeNil()) 1456 }) 1457 1458 It("fails with a BundleUnpackFailed error condition, and a message that highlights the missing field in the CSV", func() { 1459 Eventually(func(g Gomega) string { 1460 fetchedSubscription, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subscription.GetName(), metav1.GetOptions{}) 1461 g.Expect(err).NotTo(HaveOccurred()) 1462 1463 By("expect the message that API missing") 1464 failingCondition := fetchedSubscription.Status.GetCondition(v1alpha1.SubscriptionBundleUnpackFailed) 1465 return failingCondition.Message 1466 }).Should(ContainSubstring("missing APIVersion")) 1467 }) 1468 }) 1469 }) 1470 When("The namespace is labled as Pod Security Admission policy enforce:restricted", func() { 1471 BeforeEach(func() { 1472 var err error 1473 testNS := &corev1.Namespace{} 1474 Eventually(func() error { 1475 testNS, err = c.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), generatedNamespace.GetName(), metav1.GetOptions{}) 1476 if err != nil { 1477 return err 1478 } 1479 return nil 1480 }).Should(BeNil()) 1481 1482 testNS.ObjectMeta.Labels = map[string]string{ 1483 "pod-security.kubernetes.io/enforce": "restricted", 1484 "pod-security.kubernetes.io/enforce-version": "latest", 1485 } 1486 1487 Eventually(func() error { 1488 _, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), testNS, metav1.UpdateOptions{}) 1489 if err != nil { 1490 return err 1491 } 1492 return nil 1493 }).Should(BeNil()) 1494 }) 1495 When("A CatalogSource built with opm v1.21.0 (<v1.23.2)is created with spec.GrpcPodConfig.SecurityContextConfig set to restricted", func() { 1496 var sourceName string 1497 BeforeEach(func() { 1498 sourceName = genName("catalog-") 1499 source := &v1alpha1.CatalogSource{ 1500 TypeMeta: metav1.TypeMeta{ 1501 Kind: v1alpha1.CatalogSourceKind, 1502 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 1503 }, 1504 ObjectMeta: metav1.ObjectMeta{ 1505 Name: sourceName, 1506 Namespace: generatedNamespace.GetName(), 1507 Labels: map[string]string{"olm.catalogSource": sourceName}, 1508 }, 1509 Spec: v1alpha1.CatalogSourceSpec{ 1510 SourceType: v1alpha1.SourceTypeGrpc, 1511 Image: "quay.io/olmtest/old-opm-catsrc:v1.21.0", 1512 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 1513 SecurityContextConfig: v1alpha1.Restricted, 1514 }, 1515 }, 1516 } 1517 1518 Eventually(func() error { 1519 _, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 1520 return err 1521 }).Should(Succeed()) 1522 }) 1523 It("The registry pod fails to become come up because of lack of permission", func() { 1524 Eventually(func() (bool, error) { 1525 podList, err := c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).List(context.TODO(), metav1.ListOptions{}) 1526 if err != nil { 1527 return false, err 1528 } 1529 for _, pod := range podList.Items { 1530 if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName { 1531 if pod.Status.ContainerStatuses != nil && pod.Status.ContainerStatuses[0].State.Terminated != nil { 1532 return true, nil 1533 } 1534 } 1535 } 1536 return false, nil 1537 }).Should(BeTrue()) 1538 }) 1539 }) 1540 }) 1541 When("The namespace is labled as Pod Security Admission policy enforce:baseline", func() { 1542 BeforeEach(func() { 1543 var err error 1544 testNS := &corev1.Namespace{} 1545 Eventually(func() error { 1546 testNS, err = c.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), generatedNamespace.GetName(), metav1.GetOptions{}) 1547 if err != nil { 1548 return err 1549 } 1550 return nil 1551 }).Should(BeNil()) 1552 1553 testNS.ObjectMeta.Labels = map[string]string{ 1554 "pod-security.kubernetes.io/enforce": "baseline", 1555 "pod-security.kubernetes.io/enforce-version": "latest", 1556 } 1557 1558 Eventually(func() error { 1559 _, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), testNS, metav1.UpdateOptions{}) 1560 if err != nil { 1561 return err 1562 } 1563 return nil 1564 }).Should(BeNil()) 1565 }) 1566 When("A CatalogSource built with opm v1.21.0 (<v1.23.2)is created with spec.GrpcPodConfig.SecurityContextConfig set to legacy", func() { 1567 var sourceName string 1568 BeforeEach(func() { 1569 sourceName = genName("catalog-") 1570 source := &v1alpha1.CatalogSource{ 1571 TypeMeta: metav1.TypeMeta{ 1572 Kind: v1alpha1.CatalogSourceKind, 1573 APIVersion: v1alpha1.CatalogSourceCRDAPIVersion, 1574 }, 1575 ObjectMeta: metav1.ObjectMeta{ 1576 Name: sourceName, 1577 Namespace: generatedNamespace.GetName(), 1578 Labels: map[string]string{"olm.catalogSource": sourceName}, 1579 }, 1580 Spec: v1alpha1.CatalogSourceSpec{ 1581 SourceType: v1alpha1.SourceTypeGrpc, 1582 Image: "quay.io/olmtest/old-opm-catsrc:v1.21.0", 1583 GrpcPodConfig: &v1alpha1.GrpcPodConfig{ 1584 SecurityContextConfig: v1alpha1.Legacy, 1585 }, 1586 }, 1587 } 1588 1589 Eventually(func() error { 1590 _, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{}) 1591 return err 1592 }).Should(Succeed()) 1593 }) 1594 It("The registry pod comes up successfully", func() { 1595 Eventually(func() (bool, error) { 1596 podList, err := c.KubernetesInterface().CoreV1().Pods(generatedNamespace.GetName()).List(context.TODO(), metav1.ListOptions{}) 1597 if err != nil { 1598 return false, err 1599 } 1600 for _, pod := range podList.Items { 1601 if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName { 1602 if pod.Status.ContainerStatuses != nil { 1603 if *pod.Status.ContainerStatuses[0].Started == true { 1604 return true, nil 1605 } 1606 } 1607 } 1608 } 1609 return false, nil 1610 }).Should(BeTrue()) 1611 }) 1612 }) 1613 }) 1614 }) 1615 1616 func getOperatorDeployment(c operatorclient.ClientInterface, namespace string, operatorLabels labels.Set) (*appsv1.Deployment, error) { 1617 deployments, err := c.ListDeploymentsWithLabels(namespace, operatorLabels) 1618 if err != nil || deployments == nil || len(deployments.Items) != 1 { 1619 return nil, fmt.Errorf("Error getting single operator deployment for label: %v", operatorLabels) 1620 } 1621 return &deployments.Items[0], nil 1622 } 1623 1624 func rescaleDeployment(c operatorclient.ClientInterface, deployment *appsv1.Deployment) error { 1625 // scale down 1626 var replicas int32 = 0 1627 deployment.Spec.Replicas = &replicas 1628 deployment, updated, err := c.UpdateDeployment(deployment) 1629 1630 if err != nil || updated == false || deployment == nil { 1631 return fmt.Errorf("Failed to scale down deployment") 1632 } 1633 1634 waitForScaleup := func() (bool, error) { 1635 fetchedDeployment, err := c.GetDeployment(deployment.GetNamespace(), deployment.GetName()) 1636 if err != nil { 1637 return true, err 1638 } 1639 if fetchedDeployment.Status.Replicas == replicas { 1640 return true, nil 1641 } 1642 1643 return false, nil 1644 } 1645 1646 // wait for deployment to scale down 1647 Eventually(waitForScaleup, 5*time.Minute, 1*time.Second).Should(BeTrue()) 1648 1649 // scale up 1650 replicas = 1 1651 deployment.Spec.Replicas = &replicas 1652 deployment, updated, err = c.UpdateDeployment(deployment) 1653 if err != nil || updated == false || deployment == nil { 1654 return fmt.Errorf("Failed to scale up deployment") 1655 } 1656 1657 // wait for deployment to scale up 1658 Eventually(waitForScaleup, 5*time.Minute, 1*time.Second).Should(BeTrue()) 1659 1660 return err 1661 } 1662 1663 func replicateCatalogPod(c operatorclient.ClientInterface, catalog *v1alpha1.CatalogSource) *corev1.Pod { 1664 initialPods, err := c.KubernetesInterface().CoreV1().Pods(catalog.GetNamespace()).List(context.Background(), metav1.ListOptions{LabelSelector: "olm.catalogSource=" + catalog.GetName()}) 1665 Expect(err).ToNot(HaveOccurred()) 1666 Expect(initialPods.Items).To(HaveLen(1)) 1667 1668 pod := initialPods.Items[0] 1669 copied := &corev1.Pod{ 1670 ObjectMeta: metav1.ObjectMeta{ 1671 Namespace: catalog.GetNamespace(), 1672 Name: catalog.GetName() + "-copy", 1673 }, 1674 Spec: pod.Spec, 1675 } 1676 1677 copied, err = c.KubernetesInterface().CoreV1().Pods(catalog.GetNamespace()).Create(context.Background(), copied, metav1.CreateOptions{}) 1678 Expect(err).ToNot(HaveOccurred()) 1679 1680 return copied 1681 }