|
| 1 | +package test |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "strings" |
| 6 | + "time" |
| 7 | + |
| 8 | + //nolint:staticcheck // ST1001: dot-imports for readability |
| 9 | + . "github.com/onsi/ginkgo/v2" |
| 10 | + //nolint:staticcheck // ST1001: dot-imports for readability |
| 11 | + . "github.com/onsi/gomega" |
| 12 | + |
| 13 | + batchv1 "k8s.io/api/batch/v1" |
| 14 | + corev1 "k8s.io/api/core/v1" |
| 15 | + "k8s.io/apimachinery/pkg/api/meta" |
| 16 | + "k8s.io/apimachinery/pkg/api/resource" |
| 17 | + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 18 | + "sigs.k8s.io/controller-runtime/pkg/client" |
| 19 | + |
| 20 | + olmv1 "github.com/operator-framework/operator-controller/api/v1" |
| 21 | + |
| 22 | + "github/operator-framework-operator-controller/openshift/tests-extension/pkg/env" |
| 23 | + "github/operator-framework-operator-controller/openshift/tests-extension/pkg/helpers" |
| 24 | +) |
| 25 | + |
| 26 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 Catalogs", func() { |
| 27 | + BeforeEach(func() { |
| 28 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 29 | + }) |
| 30 | + |
| 31 | + It("should be installed", func(ctx SpecContext) { |
| 32 | + if !env.Get().IsOpenShift { |
| 33 | + Skip("Skipping test because it requires OpenShift Catalogs") |
| 34 | + } |
| 35 | + |
| 36 | + k8sClient := env.Get().K8sClient |
| 37 | + |
| 38 | + catalogs := []string{ |
| 39 | + "openshift-certified-operators", |
| 40 | + "openshift-community-operators", |
| 41 | + "openshift-redhat-marketplace", |
| 42 | + "openshift-redhat-operators", |
| 43 | + } |
| 44 | + |
| 45 | + for _, name := range catalogs { |
| 46 | + By(fmt.Sprintf("checking that %q exists", name)) |
| 47 | + catalog := &olmv1.ClusterCatalog{} |
| 48 | + err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, catalog) |
| 49 | + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("failed to get catalog %q", name)) |
| 50 | + |
| 51 | + conditions := catalog.Status.Conditions |
| 52 | + Expect(conditions).NotTo(BeEmpty(), fmt.Sprintf("catalog %q has empty status.conditions", name)) |
| 53 | + |
| 54 | + By(fmt.Sprintf("checking that %q is serving", name)) |
| 55 | + |
| 56 | + Expect(meta.IsStatusConditionPresentAndEqual(conditions, "Serving", metav1.ConditionTrue)). |
| 57 | + To(BeTrue(), fmt.Sprintf("expected catalog %q to have condition {type: Serving, status: True},"+ |
| 58 | + " but it did not", name)) |
| 59 | + } |
| 60 | + }) |
| 61 | +}) |
| 62 | + |
| 63 | +func verifyCatalogEndpoint(ctx SpecContext, catalog, endpoint, query string) { |
| 64 | + if !env.Get().IsOpenShift { |
| 65 | + Skip("This test requires OpenShift") |
| 66 | + } |
| 67 | + |
| 68 | + k8sClient := env.Get().K8sClient |
| 69 | + |
| 70 | + By(fmt.Sprintf("Retrieving base URL from ClusterCatalog %q", catalog)) |
| 71 | + cc := &olmv1.ClusterCatalog{} |
| 72 | + err := k8sClient.Get(ctx, client.ObjectKey{Name: catalog}, cc) |
| 73 | + Expect(err).NotTo(HaveOccurred(), "failed to get ClusterCatalog") |
| 74 | + |
| 75 | + Expect(cc.Status.URLs.Base).NotTo(BeEmpty(), fmt.Sprintf("catalog %q has empty base URL", catalog)) |
| 76 | + serviceURL := fmt.Sprintf("%s/api/v1/%s%s", cc.Status.URLs.Base, endpoint, query) |
| 77 | + |
| 78 | + By(fmt.Sprintf("Creating curl Job to hit: %s", serviceURL)) |
| 79 | + |
| 80 | + jobName := fmt.Sprintf("verify-%s-%s", |
| 81 | + strings.ReplaceAll(endpoint, "?", ""), |
| 82 | + strings.ReplaceAll(catalog, "-", "")) |
| 83 | + |
| 84 | + job := buildCurlJob(jobName, "default", serviceURL) |
| 85 | + err = k8sClient.Create(ctx, job) |
| 86 | + Expect(err).NotTo(HaveOccurred(), "failed to create Job") |
| 87 | + |
| 88 | + DeferCleanup(func(ctx SpecContext) { |
| 89 | + _ = k8sClient.Delete(ctx, job) |
| 90 | + }) |
| 91 | + |
| 92 | + By("Waiting for Job to succeed") |
| 93 | + Eventually(func(g Gomega) { |
| 94 | + recheck := &batchv1.Job{} |
| 95 | + g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(job), recheck)).NotTo(HaveOccurred()) |
| 96 | + |
| 97 | + for _, c := range recheck.Status.Conditions { |
| 98 | + if c.Type == batchv1.JobComplete && c.Status == corev1.ConditionTrue { |
| 99 | + return |
| 100 | + } |
| 101 | + if c.Type == batchv1.JobFailed && c.Status == corev1.ConditionTrue { |
| 102 | + Fail(fmt.Sprintf("Job failed: %s", c.Message)) |
| 103 | + } |
| 104 | + } |
| 105 | + // If neither condition is met, do nothing. This allows Gomega to keep polling. |
| 106 | + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) |
| 107 | +} |
| 108 | + |
| 109 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 openshift-community-operators Catalog", func() { |
| 110 | + BeforeEach(func() { |
| 111 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 112 | + }) |
| 113 | + It("should serve FBC via the /v1/api/all endpoint", func(ctx SpecContext) { |
| 114 | + verifyCatalogEndpoint(ctx, "openshift-community-operators", "all", "") |
| 115 | + }) |
| 116 | +}) |
| 117 | + |
| 118 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 openshift-certified-operators Catalog", func() { |
| 119 | + BeforeEach(func() { |
| 120 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 121 | + }) |
| 122 | + It("should serve FBC via the /v1/api/all endpoint", func(ctx SpecContext) { |
| 123 | + verifyCatalogEndpoint(ctx, "openshift-certified-operators", "all", "") |
| 124 | + }) |
| 125 | +}) |
| 126 | + |
| 127 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 openshift-redhat-marketplace Catalog", func() { |
| 128 | + BeforeEach(func() { |
| 129 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 130 | + }) |
| 131 | + It("should serve FBC via the /v1/api/all endpoint", func(ctx SpecContext) { |
| 132 | + verifyCatalogEndpoint(ctx, "openshift-redhat-marketplace", "all", "") |
| 133 | + }) |
| 134 | +}) |
| 135 | + |
| 136 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 openshift-redhat-operators Catalog", func() { |
| 137 | + BeforeEach(func() { |
| 138 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 139 | + }) |
| 140 | + It("should serve FBC via the /v1/api/all endpoint", func(ctx SpecContext) { |
| 141 | + verifyCatalogEndpoint(ctx, "openshift-redhat-operators", "all", "") |
| 142 | + }) |
| 143 | +}) |
| 144 | + |
| 145 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMCatalogdAPIV1Metas][Skipped:Disconnected] OLMv1 openshift-community-operators Catalog", func() { |
| 146 | + BeforeEach(func() { |
| 147 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 148 | + }) |
| 149 | + It("should serve FBC via the /v1/api/metas endpoint", func(ctx SpecContext) { |
| 150 | + verifyCatalogEndpoint(ctx, "openshift-community-operators", "metas", "?schema=olm.package") |
| 151 | + }) |
| 152 | +}) |
| 153 | + |
| 154 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMCatalogdAPIV1Metas][Skipped:Disconnected] OLMv1 openshift-certified-operators Catalog", func() { |
| 155 | + BeforeEach(func() { |
| 156 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 157 | + }) |
| 158 | + It("should serve FBC via the /v1/api/metas endpoint", func(ctx SpecContext) { |
| 159 | + verifyCatalogEndpoint(ctx, "openshift-certified-operators", "metas", "?schema=olm.package") |
| 160 | + }) |
| 161 | +}) |
| 162 | + |
| 163 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMCatalogdAPIV1Metas][Skipped:Disconnected] OLMv1 openshift-redhat-marketplace Catalog", func() { |
| 164 | + BeforeEach(func() { |
| 165 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 166 | + }) |
| 167 | + It("should serve FBC via the /v1/api/metas endpoint", func(ctx SpecContext) { |
| 168 | + verifyCatalogEndpoint(ctx, "openshift-redhat-marketplace", "metas", "?schema=olm.package") |
| 169 | + }) |
| 170 | +}) |
| 171 | + |
| 172 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMCatalogdAPIV1Metas][Skipped:Disconnected] OLMv1 openshift-redhat-operators Catalog", func() { |
| 173 | + BeforeEach(func() { |
| 174 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 175 | + }) |
| 176 | + It("should serve FBC via the /v1/api/metas endpoint", func(ctx SpecContext) { |
| 177 | + verifyCatalogEndpoint(ctx, "openshift-redhat-operators", "metas", "?schema=olm.package") |
| 178 | + }) |
| 179 | +}) |
| 180 | + |
| 181 | +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 New Catalog Install", func() { |
| 182 | + BeforeEach(func() { |
| 183 | + helpers.RequireOLMv1CapabilityOnOpenshift() |
| 184 | + }) |
| 185 | + It("should fail to install if it has an invalid reference", func(ctx SpecContext) { |
| 186 | + catName := "bad-catalog" |
| 187 | + imageRef := "example.com/does-not-exist:latest" |
| 188 | + |
| 189 | + By("creating the malformed catalog with an invalid image ref") |
| 190 | + cleanup, err := helpers.CreateClusterCatalog(ctx, catName, imageRef) |
| 191 | + Expect(err).NotTo(HaveOccurred(), "failed to create ClusterCatalog") |
| 192 | + DeferCleanup(cleanup) |
| 193 | + |
| 194 | + k8sClient := env.Get().K8sClient |
| 195 | + |
| 196 | + By("waiting for the catalog to report failure via Progressing=True and reason=Retrying") |
| 197 | + Eventually(func(g Gomega) { |
| 198 | + catalog := &olmv1.ClusterCatalog{} |
| 199 | + err := k8sClient.Get(ctx, client.ObjectKey{Name: catName}, catalog) |
| 200 | + g.Expect(err).NotTo(HaveOccurred(), "failed to get catalog") |
| 201 | + conditions := catalog.Status.Conditions |
| 202 | + c := meta.FindStatusCondition(conditions, "Progressing") |
| 203 | + g.Expect(c).NotTo(BeNil(), "expected 'Progressing' condition to be present") |
| 204 | + g.Expect(c.Status).To(Equal(metav1.ConditionTrue), "expected Progressing=True") |
| 205 | + g.Expect(c.Reason).To(Equal("Retrying"), "expected reason to be 'Retrying'") |
| 206 | + g.Expect(c.Message).To(ContainSubstring("error creating image source"), "expected image source error") |
| 207 | + }).WithTimeout(5 * time.Minute).WithPolling(1 * time.Second).Should(Succeed()) |
| 208 | + }) |
| 209 | +}) |
| 210 | + |
| 211 | +func buildCurlJob(name, namespace, url string) *batchv1.Job { |
| 212 | + backoff := int32(1) |
| 213 | + return &batchv1.Job{ |
| 214 | + ObjectMeta: metav1.ObjectMeta{ |
| 215 | + Name: name, |
| 216 | + Namespace: namespace, |
| 217 | + }, |
| 218 | + Spec: batchv1.JobSpec{ |
| 219 | + BackoffLimit: &backoff, |
| 220 | + Template: corev1.PodTemplateSpec{ |
| 221 | + Spec: corev1.PodSpec{ |
| 222 | + RestartPolicy: corev1.RestartPolicyNever, |
| 223 | + Containers: []corev1.Container{{ |
| 224 | + Name: "curl", |
| 225 | + Image: "curlimages/curl:latest", |
| 226 | + Command: []string{"/bin/bash", "-c", fmt.Sprintf("curl -vk %q", url)}, |
| 227 | + Resources: corev1.ResourceRequirements{ |
| 228 | + Requests: corev1.ResourceList{ |
| 229 | + corev1.ResourceCPU: resource.MustParse("10m"), |
| 230 | + corev1.ResourceMemory: resource.MustParse("50Mi"), |
| 231 | + }, |
| 232 | + }, |
| 233 | + }}, |
| 234 | + }, |
| 235 | + }, |
| 236 | + }, |
| 237 | + } |
| 238 | +} |
0 commit comments