Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5ed8cf4
add PDB to make sure at least 1 pod is always available during upgrad…
jianzhangbjz Nov 27, 2025
4355cde
Update ClusterExtensionRevision API docs (#2350)
perdasilva Nov 27, 2025
91b03c7
🌱 new tag symmetry and required validations (#2358)
grokspawn Dec 1, 2025
4e5542f
:seedling: Bump pymdown-extensions from 10.17.1 to 10.17.2 (#2364)
dependabot[bot] Dec 1, 2025
b23e124
institute 2wk dependency cooldown policy (#2363)
grokspawn Dec 1, 2025
34394ce
Fix testCatalogName conflict in parallel e2e tests (#2367)
jianzhangbjz Dec 2, 2025
0072857
Merge branch 'main' into synchronize
Dec 2, 2025
4a547db
UPSTREAM: <carry>: Add OpenShift specific files
dtfranz Oct 26, 2023
b6f4da7
UPSTREAM: <carry>: Add new tests for single/own namespaces install modes
camilamacedo86 Oct 6, 2025
93b3929
UPSTREAM: <carry>: Upgrade OCP image from 4.20 to 4.21
camilamacedo86 Oct 13, 2025
b3b6d06
UPSTREAM: <carry>: [Default Catalog Tests] - Change logic to get ocp …
camilamacedo86 Oct 13, 2025
58c2f61
UPSTREAM: <carry>: Update OCP catalogs to v4.21
tmshort Oct 13, 2025
3fa03ea
UPSTREAM: <carry>: support singleown cases in disconnected
kuiwang02 Oct 16, 2025
9d58caa
UPSTREAM: <carry>: fix cases 81696 and 74618 for product code changes
kuiwang02 Oct 17, 2025
84201cb
UPSTREAM: <carry>: Define Default timeouts and apply their usage accr…
camilamacedo86 Oct 22, 2025
ec5a401
UPSTREAM: <carry>: Update to new feature-gate options in helm
tmshort Oct 22, 2025
df6d7d4
UPSTREAM: <carry>: Fix flake for single/own ns tests by ensuring uniq…
camilamacedo86 Oct 22, 2025
27c04e5
UPSTREAM: <carry>: [OTE]: Enhance single/own ns based on review comme…
camilamacedo86 Oct 24, 2025
9393d7f
UPSTREAM: <carry>: Update OwnSingle template to use spec.config.inlin…
kuiwang02 Nov 3, 2025
1a4b54e
UPSTREAM: <carry>: [OTE]: Add webhook cleanup validation on extension…
camilamacedo86 Nov 4, 2025
be96547
UPSTREAM: <carry>: Add [OTP] to migrated cases
kuiwang02 Nov 7, 2025
f3f9743
UPSTREAM: <carry>: [OTE]: Upgrade dependencies used
camilamacedo86 Nov 5, 2025
0ae57c7
UPSTREAM: <carry>: fix(OTE): fix OpenShift Kubernetes replace version…
camilamacedo86 Nov 10, 2025
31c8501
UPSTREAM: <carry>: [Default Catalog Tests] Upgrade go 1.24.6 and depe…
camilamacedo86 Nov 11, 2025
f7ae9c3
UPSTREAM: <carry>: add disconnected environment support with custom p…
kuiwang02 Nov 12, 2025
63848dc
UPSTREAM: <carry>: migrate jiazha test cases to OTE
jianzhangbjz Nov 14, 2025
bac284b
UPSTREAM: <carry>: migrate clustercatalog case to ote
Xia-Zhao-rh Oct 17, 2025
7ad780d
UPSTREAM: <carry>: migrate olmv1 QE stress cases
kuiwang02 Nov 20, 2025
e037806
UPSTREAM: <carry>: Use busybox/httpd to simulate probes
tmshort Nov 25, 2025
ed96aa9
UPSTREAM: <carry>: migrate olmv1 QE cases
Xia-Zhao-rh Nov 25, 2025
71859c9
UPSTREAM: <carry>: add agent for olmv1 qe cases
kuiwang02 Oct 21, 2025
e4972ac
UPSTREAM: <drop>: go mod vendor
Dec 2, 2025
0176527
UPSTREAM: <drop>: remove upstream GitHub configuration
Dec 2, 2025
f45bd14
UPSTREAM: <drop>: configure the commit-checker
Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
UPSTREAM: <carry>: Add new tests for single/own namespaces install modes
  • Loading branch information
camilamacedo86 authored and ci-robot committed Dec 2, 2025
commit b6f4da74bcd170ea72c99ae3394c380382ee5c85
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,16 @@
"lifecycle": "blocking",
"environmentSelector": {}
},
{
"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 watch modes",
"labels": {},
"resources": {
"isolation": {}
},
"source": "openshift:payload:olmv1",
"lifecycle": "blocking",
"environmentSelector": {}
},
{
"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",
"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",
Expand All @@ -454,6 +464,16 @@
"lifecycle": "blocking",
"environmentSelector": {}
},
{
"name": "[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected][Serial] OLMv1 operator installation should reject invalid watch namespace configuration and update the status conditions accordingly should fail to install the ClusterExtension when watch namespace is invalid",
"labels": {},
"resources": {
"isolation": {}
},
"source": "openshift:payload:olmv1",
"lifecycle": "blocking",
"environmentSelector": {}
},
{
"name": "[sig-olmv1] OLMv1 should pass a trivial sanity check",
"labels": {},
Expand Down
2 changes: 2 additions & 0 deletions openshift/tests-extension/pkg/env/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
configv1 "github.com/openshift/api/config/v1"
imagev1 "github.com/openshift/api/image/v1"
operatorv1 "github.com/openshift/api/operator/v1"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
Expand Down Expand Up @@ -83,6 +84,7 @@ func initTestEnv() *TestEnv {
// Create the runtime scheme and register all necessary types
scheme := runtime.NewScheme()
utilruntime.Must(corev1.AddToScheme(scheme))
utilruntime.Must(appsv1.AddToScheme(scheme))
utilruntime.Must(rbacv1.AddToScheme(scheme))
utilruntime.Must(batchv1.AddToScheme(scheme))
utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
Expand Down
275 changes: 275 additions & 0 deletions openshift/tests-extension/test/olmv1-singleownnamespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
//nolint:staticcheck // ST1001: dot-imports for readability
. "github.com/onsi/gomega"

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

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() {
var (
k8sClient client.Client
activeNamespaces map[string]struct{}
)

BeforeEach(func() {
By("checking if OpenShift is available for tests")
if !env.Get().IsOpenShift {
Skip("Requires OpenShift for the tests")
}
helpers.RequireOLMv1CapabilityOnOpenshift()
k8sClient = env.Get().K8sClient
activeNamespaces = map[string]struct{}{}
})

AfterEach(func(ctx SpecContext) {
if CurrentSpecReport().Failed() {
for ns := range activeNamespaces {
helpers.DescribeAllClusterExtensions(ctx, ns)
}
}
})

It("should install cluster extensions successfully in both watch modes",
func(ctx SpecContext) {
scenarios := []struct {
id string
label string
watchN func(string) string
}{
{
id: "singlens",
label: "singleNamespace watch mode",
watchN: func(installNamespace string) string {
return fmt.Sprintf("%s-watch", installNamespace)
},
},
{
id: "ownns",
label: "ownNamespace watch mode",
watchN: func(installNamespace string) string {
return installNamespace
},
},
}

for _, scenario := range scenarios {
sc := scenario
suffix := rand.String(4)
installNamespace := fmt.Sprintf("olmv1-quay-bothns-%s-%s", sc.id, suffix)
watchNamespace := sc.watchN(installNamespace)

activeNamespaces[installNamespace] = struct{}{}
if watchNamespace != installNamespace {
activeNamespaces[watchNamespace] = struct{}{}
}

By(fmt.Sprintf("ensuring no ClusterExtension and CRD for quay-operator before %s scenario", sc.label))
helpers.EnsureCleanupClusterExtension(context.Background(), "quay-operator", "quayregistries.quay.redhat.com")

By(fmt.Sprintf("creating namespace %s for %s tests", installNamespace, sc.label))
installNS := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: installNamespace,
},
}
Expect(k8sClient.Create(ctx, installNS)).To(Succeed(), "failed to create install namespace %q", installNamespace)
installNamespaceCopy := installNamespace
DeferCleanup(func() {
By(fmt.Sprintf("cleanup: deleting install namespace %s", installNamespaceCopy))
_ = k8sClient.Delete(context.Background(), &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: installNamespaceCopy},
}, client.PropagationPolicy(metav1.DeletePropagationForeground))
})

var watchNSObj *corev1.Namespace
if watchNamespace != installNamespace {
By(fmt.Sprintf("creating namespace %s for watch namespace in %s scenario", watchNamespace, sc.label))
watchNSObj = &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: watchNamespace},
}
Expect(k8sClient.Create(ctx, watchNSObj)).To(Succeed(), "failed to create watch namespace %q", watchNamespace)
watchNamespaceCopy := watchNamespace
DeferCleanup(func() {
By(fmt.Sprintf("cleanup: deleting watch namespace %s", watchNamespaceCopy))
_ = k8sClient.Delete(context.Background(), &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: watchNamespaceCopy},
}, client.PropagationPolicy(metav1.DeletePropagationForeground))
})
}

saName := fmt.Sprintf("install-quay-bothns-%s-sa-%s", sc.id, suffix)
By(fmt.Sprintf("creating ServiceAccount %s for %s scenario", saName, sc.label))
sa := helpers.NewServiceAccount(saName, installNamespace)
Expect(k8sClient.Create(ctx, sa)).To(Succeed(), "failed to create ServiceAccount %q", saName)
helpers.ExpectServiceAccountExists(ctx, saName, installNamespace)
DeferCleanup(func() {
By(fmt.Sprintf("cleanup: deleting ServiceAccount %s in namespace %s", sa.Name, sa.Namespace))
_ = k8sClient.Delete(context.Background(), sa, client.PropagationPolicy(metav1.DeletePropagationForeground))
})

crbName := fmt.Sprintf("install-quay-bothns-%s-crb-%s", sc.id, suffix)
By(fmt.Sprintf("creating ClusterRoleBinding %s for %s scenario", crbName, sc.label))
crb := helpers.NewClusterRoleBinding(crbName, "cluster-admin", saName, installNamespace)
Expect(k8sClient.Create(ctx, crb)).To(Succeed(), "failed to create ClusterRoleBinding %q", crbName)
helpers.ExpectClusterRoleBindingExists(ctx, crbName)
DeferCleanup(func() {
By(fmt.Sprintf("cleanup: deleting ClusterRoleBinding %s", crb.Name))
_ = k8sClient.Delete(context.Background(), crb, client.PropagationPolicy(metav1.DeletePropagationForeground))
})

ceName := fmt.Sprintf("install-quay-bothns-%s-ce-%s", sc.id, suffix)
By(fmt.Sprintf("creating ClusterExtension %s for %s scenario", ceName, sc.label))
ce := helpers.NewClusterExtensionObject("quay-operator", "3.14.2", ceName, saName, installNamespace)
ce.Spec.Config = &olmv1.ClusterExtensionConfig{
ConfigType: olmv1.ClusterExtensionConfigTypeInline,
Inline: &apiextensionsv1.JSON{
Raw: []byte(fmt.Sprintf(`{"watchNamespace": "%s"}`, watchNamespace)),
},
}
Expect(k8sClient.Create(ctx, ce)).To(Succeed(), "failed to create ClusterExtension %q", ceName)
DeferCleanup(func() {
By(fmt.Sprintf("cleanup: deleting ClusterExtension %s", ce.Name))
_ = k8sClient.Delete(context.Background(), ce, client.PropagationPolicy(metav1.DeletePropagationForeground))
})

By(fmt.Sprintf("waiting for the ClusterExtension %s to be installed for %s scenario", ceName, sc.label))
helpers.ExpectClusterExtensionToBeInstalled(ctx, ceName)

By(fmt.Sprintf("verifying the operator deployment watch scope annotation for %s scenario", sc.label))
Eventually(func(g Gomega) {
deployments := &appsv1.DeploymentList{}
err := k8sClient.List(ctx, deployments, client.InNamespace(installNamespace))
g.Expect(err).ToNot(HaveOccurred(), "failed to list deployments in namespace %s", installNamespace)
g.Expect(deployments.Items).ToNot(BeEmpty(), "expected at least one deployment in namespace %s", installNamespace)

found := false
for i := range deployments.Items {
annotations := deployments.Items[i].Spec.Template.Annotations
if annotations == nil {
continue
}
if val, ok := annotations["olm.targetNamespaces"]; ok {
g.Expect(val).To(Equal(watchNamespace), "unexpected watch scope annotation value")
found = true
break
}
}
g.Expect(found).To(BeTrue(), "failed to find deployment with olm.targetNamespaces annotation")
}).WithTimeout(5 * time.Minute).WithPolling(3 * time.Second).Should(Succeed())

By(fmt.Sprintf("cleaning up resources created for %s scenario to allow next scenario", sc.label))
deletePolicy := metav1.DeletePropagationForeground
Expect(k8sClient.Delete(ctx, ce, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete ClusterExtension %q", ceName)
helpers.EnsureCleanupClusterExtension(context.Background(), "quay-operator", "quayregistries.quay.redhat.com")

Expect(k8sClient.Delete(ctx, crb, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete ClusterRoleBinding %q", crbName)
Expect(k8sClient.Delete(ctx, sa, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete ServiceAccount %q", saName)

if watchNSObj != nil {
Expect(k8sClient.Delete(ctx, watchNSObj, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete watch namespace %q", watchNamespace)
}
Expect(k8sClient.Delete(ctx, installNS, client.PropagationPolicy(deletePolicy))).To(Succeed(), "failed to delete install namespace %q", installNamespace)
}
})
})

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() {
var (
k8sClient client.Client
Expand Down Expand Up @@ -311,3 +480,109 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:D
}).WithTimeout(5 * time.Minute).WithPolling(1 * time.Second).Should(Succeed())
})
})

var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected][Serial] OLMv1 operator installation should reject invalid watch namespace configuration and update the status conditions accordingly", Ordered, Serial, func() {
var (
k8sClient client.Client
namespace string
testPrefix = "invalidwatch"
)

var unique, saName, crbName, ceName string
BeforeEach(func() {
By("checking if OpenShift is available for tests")
if !env.Get().IsOpenShift {
Skip("Requires OpenShift for the tests")
}
helpers.RequireOLMv1CapabilityOnOpenshift()
k8sClient = env.Get().K8sClient
unique = rand.String(4)
namespace = fmt.Sprintf("olmv1-%s-ns-%s", testPrefix, unique)
saName = fmt.Sprintf("install-%s-sa-%s", testPrefix, unique)
crbName = fmt.Sprintf("install-%s-crb-%s", testPrefix, unique)
ceName = fmt.Sprintf("install-%s-ce-%s", testPrefix, unique)

By("ensuring no lingering ClusterExtensions or CRDs for quay-operator")
helpers.EnsureCleanupClusterExtension(context.Background(), "quay-operator", "quayregistries.quay.redhat.com")

By(fmt.Sprintf("creating namespace %s for invalid watch namespace tests", namespace))
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
},
}
Expect(k8sClient.Create(context.Background(), ns)).To(Succeed(), "failed to create test namespace %q", namespace)
DeferCleanup(func() {
By(fmt.Sprintf("cleaning up namespace %s", namespace))
_ = k8sClient.Delete(context.Background(), ns, client.PropagationPolicy(metav1.DeletePropagationForeground))
})
})

AfterEach(func(ctx SpecContext) {
if CurrentSpecReport().Failed() {
By("dumping for debugging")
helpers.DescribeAllClusterExtensions(ctx, namespace)
}
})

// The controller validates the inline watchNamespace using the same DNS-1123 rules that gate namespace names.
// Setting a trailing '-' produces an invalid identifier that cannot exist in the cluster, so the install should
// fail fast and surface a failure through the Installed condition.
It("should fail to install the ClusterExtension when watch namespace is invalid",
func(ctx SpecContext) {
By("creating ServiceAccount")
sa := helpers.NewServiceAccount(saName, namespace)
Expect(k8sClient.Create(ctx, sa)).To(Succeed(), "failed to create ServiceAccount %q", saName)
By("ensuring ServiceAccount is available before proceeding")
helpers.ExpectServiceAccountExists(ctx, saName, namespace)
DeferCleanup(func() {
By(fmt.Sprintf("cleanup: deleting ServiceAccount %s in namespace %s", sa.Name, sa.Namespace))
_ = k8sClient.Delete(context.Background(), sa, client.PropagationPolicy(metav1.DeletePropagationForeground))
})

By("creating ClusterRoleBinding")
crb := helpers.NewClusterRoleBinding(crbName, "cluster-admin", saName, namespace)
Expect(k8sClient.Create(ctx, crb)).To(Succeed(), "failed to create ClusterRoleBinding %q", crbName)
By("ensuring ClusterRoleBinding is available before proceeding")
helpers.ExpectClusterRoleBindingExists(ctx, crbName)
DeferCleanup(func() {
By(fmt.Sprintf("cleanup: deleting ClusterRoleBinding %s", crb.Name))
_ = k8sClient.Delete(context.Background(), crb, client.PropagationPolicy(metav1.DeletePropagationForeground))
})

invalidWatchNamespace := fmt.Sprintf("%s-", namespace)

By("creating ClusterExtension with an invalid watch namespace configured")
ce := helpers.NewClusterExtensionObject("quay-operator", "3.14.2", ceName, saName, namespace)
ce.Spec.Config = &olmv1.ClusterExtensionConfig{
ConfigType: olmv1.ClusterExtensionConfigTypeInline,
Inline: &apiextensionsv1.JSON{
Raw: []byte(fmt.Sprintf(`{"watchNamespace": "%s"}`, invalidWatchNamespace)),
},
}
Expect(k8sClient.Create(ctx, ce)).To(Succeed(), "failed to create ClusterExtension %q", ceName)
DeferCleanup(func() {
By(fmt.Sprintf("cleanup: deleting ClusterExtension %s", ce.Name))
_ = k8sClient.Delete(context.Background(), ce, client.PropagationPolicy(metav1.DeletePropagationForeground))

By("ensuring ClusterExtension is deleted")
helpers.EnsureCleanupClusterExtension(context.Background(), ceName, namespace)
})

By("waiting for the ClusterExtension installation to fail due to invalid watch namespace")
Eventually(func(g Gomega) {
var ext olmv1.ClusterExtension
err := k8sClient.Get(ctx, client.ObjectKey{Name: ceName}, &ext)
g.Expect(err).ToNot(HaveOccurred(), "failed to get ClusterExtension %q", ceName)

conditions := ext.Status.Conditions
g.Expect(conditions).ToNot(BeEmpty(), "ClusterExtension %q has empty status.conditions", ceName)

installed := meta.FindStatusCondition(conditions, olmv1.TypeInstalled)
g.Expect(installed).ToNot(BeNil(), "Installed condition not found")
g.Expect(installed.Status).To(Equal(metav1.ConditionFalse), "Installed should be False")
g.Expect(installed.Reason).To(Equal(olmv1.ReasonFailed))
g.Expect(installed.Message).ToNot(BeEmpty())
}).WithTimeout(5 * time.Minute).WithPolling(3 * time.Second).Should(Succeed())
})
})