Skip to content

Commit 424779b

Browse files
UPSTREAM: <carry>: Add new tests for single/own namespaces install modes
1 parent f69276f commit 424779b

File tree

2 files changed

+296
-0
lines changed

2 files changed

+296
-0
lines changed

openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,16 @@
441441
"lifecycle": "blocking",
442442
"environmentSelector": {}
443443
},
444+
{
445+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected][Serial] OLMv1 operator installation support for ownNamespace and single namespace watch mode with quay-operator should install cluster extensions successfully in both singleNamespace and ownNamespace watch modes",
446+
"labels": {},
447+
"resources": {
448+
"isolation": {}
449+
},
450+
"source": "openshift:payload:olmv1",
451+
"lifecycle": "blocking",
452+
"environmentSelector": {}
453+
},
444454
{
445455
"name": "[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected][Serial] OLMv1 operator installation support for ownNamespace watch mode with an operator that does not support ownNamespace installation mode should fail to install a cluster extension successfully",
446456
"originalName": "[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected] OLMv1 operator installation support for ownNamespace watch mode with an operator that does not support ownNamespace installation mode should fail to install a cluster extension successfully",
@@ -454,6 +464,16 @@
454464
"lifecycle": "blocking",
455465
"environmentSelector": {}
456466
},
467+
{
468+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected][Serial] OLMv1 operator installation should reject invalid watch namespace configuration should fail to install the ClusterExtension when watch namespace is invalid and update the status conditions accordingly",
469+
"labels": {},
470+
"resources": {
471+
"isolation": {}
472+
},
473+
"source": "openshift:payload:olmv1",
474+
"lifecycle": "blocking",
475+
"environmentSelector": {}
476+
},
457477
{
458478
"name": "[sig-olmv1] OLMv1 should pass a trivial sanity check",
459479
"labels": {},

openshift/tests-extension/test/olmv1-singleownnamespace.go

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
//nolint:staticcheck // ST1001: dot-imports for readability
1111
. "github.com/onsi/gomega"
1212

13+
appsv1 "k8s.io/api/apps/v1"
1314
corev1 "k8s.io/api/core/v1"
1415
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1516
"k8s.io/apimachinery/pkg/api/meta"
@@ -208,6 +209,174 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:D
208209
})
209210
})
210211

212+
var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected][Serial] OLMv1 operator installation support for ownNamespace and single namespace watch mode with quay-operator", Ordered, Serial, func() {
213+
var (
214+
k8sClient client.Client
215+
activeNamespaces map[string]struct{}
216+
)
217+
218+
BeforeEach(func() {
219+
By("checking if OpenShift is available for tests")
220+
if !env.Get().IsOpenShift {
221+
Skip("Requires OpenShift for the tests")
222+
}
223+
helpers.RequireOLMv1CapabilityOnOpenshift()
224+
k8sClient = env.Get().K8sClient
225+
activeNamespaces = map[string]struct{}{}
226+
})
227+
228+
AfterEach(func(ctx SpecContext) {
229+
if CurrentSpecReport().Failed() {
230+
for ns := range activeNamespaces {
231+
helpers.DescribeAllClusterExtensions(ctx, ns)
232+
}
233+
}
234+
})
235+
236+
It("should install cluster extensions successfully in both singleNamespace and ownNamespace watch modes",
237+
func(ctx SpecContext) {
238+
scenarios := []struct {
239+
id string
240+
label string
241+
watchN func(string) string
242+
}{
243+
{
244+
id: "singlens",
245+
label: "singleNamespace watch mode",
246+
watchN: func(installNamespace string) string {
247+
return fmt.Sprintf("%s-watch", installNamespace)
248+
},
249+
},
250+
{
251+
id: "ownns",
252+
label: "ownNamespace watch mode",
253+
watchN: func(installNamespace string) string {
254+
return installNamespace
255+
},
256+
},
257+
}
258+
259+
for _, scenario := range scenarios {
260+
sc := scenario
261+
suffix := rand.String(4)
262+
installNamespace := fmt.Sprintf("olmv1-quay-bothns-%s-%s", sc.id, suffix)
263+
watchNamespace := sc.watchN(installNamespace)
264+
265+
activeNamespaces[installNamespace] = struct{}{}
266+
if watchNamespace != installNamespace {
267+
activeNamespaces[watchNamespace] = struct{}{}
268+
}
269+
270+
By(fmt.Sprintf("ensuring no ClusterExtension and CRD for quay-operator before %s scenario", sc.label))
271+
helpers.EnsureCleanupClusterExtension(context.Background(), "quay-operator", "quayregistries.quay.redhat.com")
272+
273+
By(fmt.Sprintf("creating namespace %s for %s tests", installNamespace, sc.label))
274+
installNS := &corev1.Namespace{
275+
ObjectMeta: metav1.ObjectMeta{
276+
Name: installNamespace,
277+
},
278+
}
279+
Expect(k8sClient.Create(ctx, installNS)).To(Succeed(), "failed to create install namespace %q", installNamespace)
280+
installNamespaceCopy := installNamespace
281+
DeferCleanup(func() {
282+
By(fmt.Sprintf("cleanup: deleting install namespace %s", installNamespaceCopy))
283+
_ = k8sClient.Delete(context.Background(), &corev1.Namespace{
284+
ObjectMeta: metav1.ObjectMeta{Name: installNamespaceCopy},
285+
}, client.PropagationPolicy(metav1.DeletePropagationForeground))
286+
})
287+
288+
var watchNSObj *corev1.Namespace
289+
if watchNamespace != installNamespace {
290+
By(fmt.Sprintf("creating namespace %s for watch namespace in %s scenario", watchNamespace, sc.label))
291+
watchNSObj = &corev1.Namespace{
292+
ObjectMeta: metav1.ObjectMeta{Name: watchNamespace},
293+
}
294+
Expect(k8sClient.Create(ctx, watchNSObj)).To(Succeed(), "failed to create watch namespace %q", watchNamespace)
295+
watchNamespaceCopy := watchNamespace
296+
DeferCleanup(func() {
297+
By(fmt.Sprintf("cleanup: deleting watch namespace %s", watchNamespaceCopy))
298+
_ = k8sClient.Delete(context.Background(), &corev1.Namespace{
299+
ObjectMeta: metav1.ObjectMeta{Name: watchNamespaceCopy},
300+
}, client.PropagationPolicy(metav1.DeletePropagationForeground))
301+
})
302+
}
303+
304+
saName := fmt.Sprintf("install-quay-bothns-%s-sa-%s", sc.id, suffix)
305+
By(fmt.Sprintf("creating ServiceAccount %s for %s scenario", saName, sc.label))
306+
sa := helpers.NewServiceAccount(saName, installNamespace)
307+
Expect(k8sClient.Create(ctx, sa)).To(Succeed(), "failed to create ServiceAccount %q", saName)
308+
helpers.ExpectServiceAccountExists(ctx, saName, installNamespace)
309+
DeferCleanup(func() {
310+
By(fmt.Sprintf("cleanup: deleting ServiceAccount %s in namespace %s", sa.Name, sa.Namespace))
311+
_ = k8sClient.Delete(context.Background(), sa, client.PropagationPolicy(metav1.DeletePropagationForeground))
312+
})
313+
314+
crbName := fmt.Sprintf("install-quay-bothns-%s-crb-%s", sc.id, suffix)
315+
By(fmt.Sprintf("creating ClusterRoleBinding %s for %s scenario", crbName, sc.label))
316+
crb := helpers.NewClusterRoleBinding(crbName, "cluster-admin", saName, installNamespace)
317+
Expect(k8sClient.Create(ctx, crb)).To(Succeed(), "failed to create ClusterRoleBinding %q", crbName)
318+
helpers.ExpectClusterRoleBindingExists(ctx, crbName)
319+
DeferCleanup(func() {
320+
By(fmt.Sprintf("cleanup: deleting ClusterRoleBinding %s", crb.Name))
321+
_ = k8sClient.Delete(context.Background(), crb, client.PropagationPolicy(metav1.DeletePropagationForeground))
322+
})
323+
324+
ceName := fmt.Sprintf("install-quay-bothns-%s-ce-%s", sc.id, suffix)
325+
By(fmt.Sprintf("creating ClusterExtension %s for %s scenario", ceName, sc.label))
326+
ce := helpers.NewClusterExtensionObject("quay-operator", "3.14.2", ceName, saName, installNamespace)
327+
ce.Spec.Config = &olmv1.ClusterExtensionConfig{
328+
ConfigType: olmv1.ClusterExtensionConfigTypeInline,
329+
Inline: &apiextensionsv1.JSON{
330+
Raw: []byte(fmt.Sprintf(`{"watchNamespace": "%s"}`, watchNamespace)),
331+
},
332+
}
333+
Expect(k8sClient.Create(ctx, ce)).To(Succeed(), "failed to create ClusterExtension %q", ceName)
334+
DeferCleanup(func() {
335+
By(fmt.Sprintf("cleanup: deleting ClusterExtension %s", ce.Name))
336+
_ = k8sClient.Delete(context.Background(), ce, client.PropagationPolicy(metav1.DeletePropagationForeground))
337+
})
338+
339+
By(fmt.Sprintf("waiting for the ClusterExtension %s to be installed for %s scenario", ceName, sc.label))
340+
helpers.ExpectClusterExtensionToBeInstalled(ctx, ceName)
341+
342+
By(fmt.Sprintf("verifying the operator deployment watch scope annotation for %s scenario", sc.label))
343+
Eventually(func(g Gomega) {
344+
deployments := &appsv1.DeploymentList{}
345+
err := k8sClient.List(ctx, deployments, client.InNamespace(installNamespace))
346+
g.Expect(err).ToNot(HaveOccurred(), "failed to list deployments in namespace %s", installNamespace)
347+
g.Expect(deployments.Items).ToNot(BeEmpty(), "expected at least one deployment in namespace %s", installNamespace)
348+
349+
found := false
350+
for i := range deployments.Items {
351+
annotations := deployments.Items[i].Spec.Template.Annotations
352+
if annotations == nil {
353+
continue
354+
}
355+
if val, ok := annotations["olm.targetNamespaces"]; ok {
356+
g.Expect(val).To(Equal(watchNamespace), "unexpected watch scope annotation value")
357+
found = true
358+
break
359+
}
360+
}
361+
g.Expect(found).To(BeTrue(), "failed to find deployment with olm.targetNamespaces annotation")
362+
}).WithTimeout(5 * time.Minute).WithPolling(2 * time.Second).Should(Succeed())
363+
364+
By(fmt.Sprintf("cleaning up resources created for %s scenario to allow next scenario", sc.label))
365+
deletePolicy := metav1.DeletePropagationForeground
366+
Expect(k8sClient.Delete(ctx, ce, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete ClusterExtension %q", ceName)
367+
helpers.EnsureCleanupClusterExtension(context.Background(), "quay-operator", "quayregistries.quay.redhat.com")
368+
369+
Expect(k8sClient.Delete(ctx, crb, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete ClusterRoleBinding %q", crbName)
370+
Expect(k8sClient.Delete(ctx, sa, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete ServiceAccount %q", saName)
371+
372+
if watchNSObj != nil {
373+
Expect(k8sClient.Delete(ctx, watchNSObj, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete watch namespace %q", watchNamespace)
374+
}
375+
Expect(k8sClient.Delete(ctx, installNS, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete install namespace %q", installNamespace)
376+
}
377+
})
378+
})
379+
211380
var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected][Serial] OLMv1 operator installation support for ownNamespace watch mode with an operator that does not support ownNamespace installation mode", Ordered, Serial, func() {
212381
var (
213382
k8sClient client.Client
@@ -311,3 +480,110 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:D
311480
}).WithTimeout(5 * time.Minute).WithPolling(1 * time.Second).Should(Succeed())
312481
})
313482
})
483+
484+
var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected][Serial] OLMv1 operator installation should reject invalid watch namespace configuration", Ordered, Serial, func() {
485+
var (
486+
k8sClient client.Client
487+
namespace string
488+
testPrefix = "invalidwatch"
489+
)
490+
491+
var unique, saName, crbName, ceName string
492+
BeforeEach(func() {
493+
By("checking if OpenShift is available for tests")
494+
if !env.Get().IsOpenShift {
495+
Skip("Requires OpenShift for the tests")
496+
}
497+
helpers.RequireOLMv1CapabilityOnOpenshift()
498+
k8sClient = env.Get().K8sClient
499+
unique = rand.String(4)
500+
namespace = fmt.Sprintf("olmv1-%s-ns-%s", testPrefix, unique)
501+
saName = fmt.Sprintf("install-%s-sa-%s", testPrefix, unique)
502+
crbName = fmt.Sprintf("install-%s-crb-%s", testPrefix, unique)
503+
ceName = fmt.Sprintf("install-%s-ce-%s", testPrefix, unique)
504+
505+
By("ensuring no lingering ClusterExtensions or CRDs for quay-operator")
506+
helpers.EnsureCleanupClusterExtension(context.Background(), "quay-operator", "quayregistries.quay.redhat.com")
507+
508+
By(fmt.Sprintf("creating namespace %s for invalid watch namespace tests", namespace))
509+
ns := &corev1.Namespace{
510+
ObjectMeta: metav1.ObjectMeta{
511+
Name: namespace,
512+
},
513+
}
514+
Expect(k8sClient.Create(context.Background(), ns)).To(Succeed(), "failed to create test namespace %q", namespace)
515+
DeferCleanup(func() {
516+
By(fmt.Sprintf("cleaning up namespace %s", namespace))
517+
_ = k8sClient.Delete(context.Background(), ns, client.PropagationPolicy(metav1.DeletePropagationForeground))
518+
})
519+
})
520+
521+
AfterEach(func(ctx SpecContext) {
522+
if CurrentSpecReport().Failed() {
523+
By("dumping for debugging")
524+
helpers.DescribeAllClusterExtensions(ctx, namespace)
525+
}
526+
})
527+
528+
// The controller validates the inline watchNamespace using the same DNS-1123 rules that gate namespace names.
529+
// Setting a trailing '-' produces an invalid identifier that cannot exist in the cluster, so the install should
530+
// fail fast and surface the problematic value in status conditions.
531+
It("should fail to install the ClusterExtension when watch namespace is invalid and update the status conditions accordingly",
532+
func(ctx SpecContext) {
533+
By("creating ServiceAccount")
534+
sa := helpers.NewServiceAccount(saName, namespace)
535+
Expect(k8sClient.Create(ctx, sa)).To(Succeed(), "failed to create ServiceAccount %q", saName)
536+
By("ensuring ServiceAccount is available before proceeding")
537+
helpers.ExpectServiceAccountExists(ctx, saName, namespace)
538+
DeferCleanup(func() {
539+
By(fmt.Sprintf("cleanup: deleting ServiceAccount %s in namespace %s", sa.Name, sa.Namespace))
540+
_ = k8sClient.Delete(context.Background(), sa, client.PropagationPolicy(metav1.DeletePropagationForeground))
541+
})
542+
543+
By("creating ClusterRoleBinding")
544+
crb := helpers.NewClusterRoleBinding(crbName, "cluster-admin", saName, namespace)
545+
Expect(k8sClient.Create(ctx, crb)).To(Succeed(), "failed to create ClusterRoleBinding %q", crbName)
546+
By("ensuring ClusterRoleBinding is available before proceeding")
547+
helpers.ExpectClusterRoleBindingExists(ctx, crbName)
548+
DeferCleanup(func() {
549+
By(fmt.Sprintf("cleanup: deleting ClusterRoleBinding %s", crb.Name))
550+
_ = k8sClient.Delete(context.Background(), crb, client.PropagationPolicy(metav1.DeletePropagationForeground))
551+
})
552+
553+
invalidWatchNamespace := fmt.Sprintf("%s-", namespace)
554+
555+
By("creating ClusterExtension with an invalid watch namespace configured")
556+
ce := helpers.NewClusterExtensionObject("quay-operator", "3.14.2", ceName, saName, namespace)
557+
ce.Spec.Config = &olmv1.ClusterExtensionConfig{
558+
ConfigType: olmv1.ClusterExtensionConfigTypeInline,
559+
Inline: &apiextensionsv1.JSON{
560+
Raw: []byte(fmt.Sprintf(`{"watchNamespace": "%s"}`, invalidWatchNamespace)),
561+
},
562+
}
563+
Expect(k8sClient.Create(ctx, ce)).To(Succeed(), "failed to create ClusterExtension %q", ceName)
564+
DeferCleanup(func() {
565+
By(fmt.Sprintf("cleanup: deleting ClusterExtension %s", ce.Name))
566+
_ = k8sClient.Delete(context.Background(), ce, client.PropagationPolicy(metav1.DeletePropagationForeground))
567+
568+
By("ensuring ClusterExtension is deleted")
569+
helpers.EnsureCleanupClusterExtension(context.Background(), ceName, namespace)
570+
})
571+
572+
By("waiting for the ClusterExtension installation to fail due to invalid watch namespace")
573+
Eventually(func(g Gomega) {
574+
var ext olmv1.ClusterExtension
575+
err := k8sClient.Get(ctx, client.ObjectKey{Name: ceName}, &ext)
576+
g.Expect(err).ToNot(HaveOccurred(), "failed to get ClusterExtension %q", ceName)
577+
578+
conditions := ext.Status.Conditions
579+
g.Expect(conditions).ToNot(BeEmpty(), "ClusterExtension %q has empty status.conditions", ceName)
580+
581+
installed := meta.FindStatusCondition(conditions, olmv1.TypeInstalled)
582+
g.Expect(installed).ToNot(BeNil(), "Installed condition not found")
583+
g.Expect(installed.Status).To(Equal(metav1.ConditionFalse), "Installed should be False")
584+
g.Expect(installed.Reason).To(Equal(olmv1.ReasonFailed))
585+
g.Expect(installed.Message).To(ContainSubstring("invalid watch namespace"))
586+
g.Expect(installed.Message).To(ContainSubstring(invalidWatchNamespace))
587+
}).WithTimeout(5 * time.Minute).WithPolling(1 * time.Second).Should(Succeed())
588+
})
589+
})

0 commit comments

Comments
 (0)