Skip to content

Commit 21ea6a8

Browse files
UPSTREAM: <carry>: Migrate single/own namespace tests
This commit migrates the OLMv1 single and own namespace watch mode tests from openshift/origin/test/extended/olm/olmv1-singleownnamespace.go to this repository. This is part of the effort to move component-specific tests into their respective downstream locations. Assisted-by: Gemini
1 parent 778bd57 commit 21ea6a8

File tree

4 files changed

+475
-54
lines changed

4 files changed

+475
-54
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,34 @@
11
[
2+
{
3+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected] OLMv1 operator installation support for singleNamespace watch mode with quay-operator should install a cluster extension successfully",
4+
"labels": {},
5+
"resources": {
6+
"isolation": {}
7+
},
8+
"source": "openshift:payload:olmv1",
9+
"lifecycle": "blocking",
10+
"environmentSelector": {}
11+
},
12+
{
13+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMOwnSingleNamespace][Skipped:Disconnected] OLMv1 operator installation support for ownNamespace watch mode with quay-operator should install a cluster extension successfully",
14+
"labels": {},
15+
"resources": {
16+
"isolation": {}
17+
},
18+
"source": "openshift:payload:olmv1",
19+
"lifecycle": "blocking",
20+
"environmentSelector": {}
21+
},
22+
{
23+
"name": "[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",
24+
"labels": {},
25+
"resources": {
26+
"isolation": {}
27+
},
28+
"source": "openshift:payload:olmv1",
29+
"lifecycle": "blocking",
30+
"environmentSelector": {}
31+
},
232
{
333
"name": "[sig-olmv1] OLMv1 should pass a trivial sanity check",
434
"labels": {},

openshift/tests-extension/pkg/helpers/cluster_extension.go

Lines changed: 144 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,87 +2,195 @@ package helpers
22

33
import (
44
"context"
5+
"fmt"
6+
"time"
57

8+
//nolint:staticcheck // ST1001: dot-imports for readability
9+
. "github.com/onsi/ginkgo/v2"
610
//nolint:staticcheck // ST1001: dot-imports for readability
711
. "github.com/onsi/gomega"
812

913
corev1 "k8s.io/api/core/v1"
1014
rbacv1 "k8s.io/api/rbac/v1"
15+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
16+
"k8s.io/apimachinery/pkg/api/errors"
17+
"k8s.io/apimachinery/pkg/api/meta"
1118
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1219
"k8s.io/apimachinery/pkg/util/rand"
20+
"sigs.k8s.io/controller-runtime/pkg/client"
1321

14-
ocv1 "github.com/operator-framework/operator-controller/api/v1"
22+
olmv1 "github.com/operator-framework/operator-controller/api/v1"
1523

1624
"github/operator-framework-operator-controller/openshift/tests-extension/pkg/env"
1725
)
1826

19-
const openshiftOperatorsNs = "openshift-operators"
20-
2127
// CreateClusterExtension creates a ServiceAccount, ClusterRoleBinding, and ClusterExtension using typed APIs.
2228
// It returns the unique suffix and a cleanup function.
23-
func CreateClusterExtension(packageName, version string) (string, func()) {
29+
func CreateClusterExtension(packageName, version, namespace string) (string, func()) {
2430
ctx := context.TODO()
2531
k8sClient := env.Get().K8sClient
26-
unique := rand.String(8)
32+
unique := rand.String(4)
2733

2834
saName := "install-test-sa-" + unique
2935
crbName := "install-test-crb-" + unique
3036
ceName := "install-test-ce-" + unique
3137

3238
// 1. Create ServiceAccount
33-
sa := &corev1.ServiceAccount{
34-
ObjectMeta: metav1.ObjectMeta{
35-
Name: saName,
36-
Namespace: openshiftOperatorsNs,
37-
},
38-
}
39-
Expect(k8sClient.Create(ctx, sa)).To(Succeed(), "failed to create ServiceAccount")
39+
sa := NewServiceAccount(saName, namespace)
40+
Expect(k8sClient.Create(ctx, sa)).To(Succeed(),
41+
"failed to create ServiceAccount")
42+
By("ensuring ServiceAccount is available before proceeding")
43+
ExpectServiceAccountExists(ctx, saName, namespace)
4044

4145
// 2. Create ClusterRoleBinding
42-
crb := &rbacv1.ClusterRoleBinding{
46+
crb := NewClusterRoleBinding(crbName, "cluster-admin", saName, namespace)
47+
Expect(k8sClient.Create(ctx, crb)).To(Succeed(), "failed to create ClusterRoleBinding")
48+
By("ensuring ClusterRoleBinding is available before proceeding")
49+
ExpectClusterRoleBindingExists(ctx, crbName)
50+
51+
// 3. Create ClusterExtension
52+
ce := NewClusterExtensionObject(packageName, version, ceName, saName, namespace)
53+
Expect(k8sClient.Create(ctx, ce)).To(Succeed(), "failed to create ClusterExtension")
54+
55+
// Cleanup closure
56+
return ceName, func() {
57+
_ = k8sClient.Delete(ctx, ce)
58+
_ = k8sClient.Delete(ctx, crb)
59+
_ = k8sClient.Delete(ctx, sa)
60+
}
61+
}
62+
63+
// NewServiceAccount creates a new ServiceAccount object in the openshift-operators namespace.
64+
func NewServiceAccount(name, namespace string) *corev1.ServiceAccount {
65+
return &corev1.ServiceAccount{
4366
ObjectMeta: metav1.ObjectMeta{
44-
Name: crbName,
67+
Name: name,
68+
Namespace: namespace,
4569
},
70+
}
71+
}
72+
73+
// NewClusterRoleBinding creates a new ClusterRoleBinding object that binds a ClusterRole to a ServiceAccount.
74+
func NewClusterRoleBinding(name, roleName, saName, namespace string) *rbacv1.ClusterRoleBinding {
75+
return &rbacv1.ClusterRoleBinding{
76+
ObjectMeta: metav1.ObjectMeta{Name: name},
4677
RoleRef: rbacv1.RoleRef{
4778
APIGroup: "rbac.authorization.k8s.io",
4879
Kind: "ClusterRole",
49-
Name: "cluster-admin",
80+
Name: roleName,
5081
},
5182
Subjects: []rbacv1.Subject{{
5283
Kind: "ServiceAccount",
5384
Name: saName,
54-
Namespace: openshiftOperatorsNs,
85+
Namespace: namespace,
5586
}},
5687
}
57-
Expect(k8sClient.Create(ctx, crb)).To(Succeed(), "failed to create ClusterRoleBinding")
88+
}
5889

59-
// 3. Create ClusterExtension
60-
ce := &ocv1.ClusterExtension{
61-
ObjectMeta: metav1.ObjectMeta{
62-
Name: ceName,
63-
},
64-
Spec: ocv1.ClusterExtensionSpec{
65-
Namespace: openshiftOperatorsNs,
66-
ServiceAccount: ocv1.ServiceAccountReference{
90+
// NewClusterExtensionObject creates a new ClusterExtension object with the specified package, version, name, and ServiceAccount.
91+
func NewClusterExtensionObject(pkg, version, ceName, saName, namespace string) *olmv1.ClusterExtension {
92+
return &olmv1.ClusterExtension{
93+
ObjectMeta: metav1.ObjectMeta{Name: ceName},
94+
Spec: olmv1.ClusterExtensionSpec{
95+
Namespace: namespace,
96+
ServiceAccount: olmv1.ServiceAccountReference{
6797
Name: saName,
6898
},
69-
Source: ocv1.SourceConfig{
70-
SourceType: ocv1.SourceTypeCatalog,
71-
Catalog: &ocv1.CatalogFilter{
72-
PackageName: packageName,
99+
Source: olmv1.SourceConfig{
100+
SourceType: olmv1.SourceTypeCatalog,
101+
Catalog: &olmv1.CatalogFilter{
102+
PackageName: pkg,
73103
Version: version,
74104
Selector: &metav1.LabelSelector{},
75-
UpgradeConstraintPolicy: ocv1.UpgradeConstraintPolicyCatalogProvided,
105+
UpgradeConstraintPolicy: olmv1.UpgradeConstraintPolicyCatalogProvided,
76106
},
77107
},
78108
},
79109
}
80-
Expect(k8sClient.Create(ctx, ce)).To(Succeed(), "failed to create ClusterExtension")
110+
}
81111

82-
// Cleanup closure
83-
return ceName, func() {
84-
_ = k8sClient.Delete(ctx, ce)
85-
_ = k8sClient.Delete(ctx, crb)
86-
_ = k8sClient.Delete(ctx, sa)
112+
// ExpectClusterExtensionToBeInstalled checks that the ClusterExtension has both Progressing=True and Installed=True.
113+
func ExpectClusterExtensionToBeInstalled(ctx context.Context, name string) {
114+
k8sClient := env.Get().K8sClient
115+
Eventually(func(g Gomega) {
116+
var ext olmv1.ClusterExtension
117+
err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, &ext)
118+
g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get ClusterExtension %q", name))
119+
120+
conditions := ext.Status.Conditions
121+
g.Expect(conditions).NotTo(BeEmpty(), fmt.Sprintf("ClusterExtension %q has empty status.conditions", name))
122+
123+
progressing := meta.FindStatusCondition(conditions, string(olmv1.TypeProgressing))
124+
g.Expect(progressing).ToNot(BeNil(), "Progressing condition not found")
125+
g.Expect(progressing.Status).To(Equal(metav1.ConditionTrue), "Progressing should be True")
126+
127+
installed := meta.FindStatusCondition(conditions, string(olmv1.TypeInstalled))
128+
g.Expect(installed).ToNot(BeNil(), "Installed condition not found")
129+
g.Expect(installed.Status).To(Equal(metav1.ConditionTrue), "Installed should be True")
130+
}).WithTimeout(5 * time.Minute).WithPolling(1 * time.Second).Should(Succeed())
131+
}
132+
133+
// EnsureCleanupClusterExtension attempts to delete any ClusterExtension and a specified CRD
134+
// that might be left over from previous test runs. This helps prevent conflicts in serial tests.
135+
func EnsureCleanupClusterExtension(ctx context.Context, packageName, crdName string) {
136+
k8sClient := env.Get().K8sClient
137+
138+
// 1. Clean up any ClusterExtensions related to this test/package
139+
ceList := &olmv1.ClusterExtensionList{}
140+
// List all ClusterExtensions, then filter in code by packageName
141+
if err := k8sClient.List(ctx, ceList); err == nil {
142+
for _, ce := range ceList.Items {
143+
if ce.Spec.Source.Catalog.PackageName == packageName {
144+
By(fmt.Sprintf("deleting ClusterExtension %s (package: %s)", ce.Name, packageName))
145+
propagationPolicy := metav1.DeletePropagationForeground
146+
deleteOpts := &client.DeleteOptions{PropagationPolicy: &propagationPolicy}
147+
if err := k8sClient.Delete(ctx, &ce, deleteOpts); err != nil && !errors.IsNotFound(err) {
148+
fmt.Fprintf(GinkgoWriter, "Warning: Failed to delete lingering ClusterExtension %s: %v\n", ce.Name, err)
149+
}
150+
Eventually(func() bool {
151+
err := k8sClient.Get(ctx, client.ObjectKey{Name: ce.Name}, &olmv1.ClusterExtension{})
152+
return errors.IsNotFound(err)
153+
}).WithTimeout(1*time.Minute).WithPolling(2*time.Second).Should(BeTrue(), "Lingering ClusterExtension %s failed to delete", ce.Name)
154+
}
155+
}
156+
} else if !errors.IsNotFound(err) {
157+
fmt.Fprintf(GinkgoWriter, "Warning: Failed to list ClusterExtensions during cleanup: %v\n", err)
158+
}
159+
160+
// 2. Clean up specific operator-created CRD if it exists
161+
if crdName != "" {
162+
crd := &apiextensionsv1.CustomResourceDefinition{}
163+
if err := k8sClient.Get(ctx, client.ObjectKey{Name: crdName}, crd); err == nil {
164+
By(fmt.Sprintf("deleting CRD %s", crdName))
165+
if err := k8sClient.Delete(ctx, crd); err != nil && !errors.IsNotFound(err) {
166+
fmt.Fprintf(GinkgoWriter, "Warning: Failed to delete lingering CRD %s: %v\n", crdName, err)
167+
}
168+
Eventually(func() bool {
169+
err := k8sClient.Get(ctx, client.ObjectKey{Name: crdName}, &apiextensionsv1.CustomResourceDefinition{})
170+
return errors.IsNotFound(err)
171+
}).WithTimeout(1*time.Minute).WithPolling(2*time.Second).Should(BeTrue(), "Lingering CRD %s failed to delete", crdName)
172+
} else if !errors.IsNotFound(err) {
173+
fmt.Fprintf(GinkgoWriter, "Warning: Failed to get CRD %s during cleanup: %v\n", crdName, err)
174+
}
87175
}
88176
}
177+
178+
// ExpectServiceAccountExists waits for a ServiceAccount to be available and visible to the client.
179+
func ExpectServiceAccountExists(ctx context.Context, name, namespace string) {
180+
k8sClient := env.Get().K8sClient
181+
sa := &corev1.ServiceAccount{}
182+
Eventually(func(g Gomega) {
183+
err := k8sClient.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, sa)
184+
g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get ServiceAccount %q/%q: %v", namespace, name, err))
185+
}).WithTimeout(10*time.Second).WithPolling(1*time.Second).Should(Succeed(), "ServiceAccount %q/%q did not become visible within timeout", namespace, name)
186+
}
187+
188+
// ExpectClusterRoleBindingExists waits for a ClusterRoleBinding to be available and visible to the client.
189+
func ExpectClusterRoleBindingExists(ctx context.Context, name string) {
190+
k8sClient := env.Get().K8sClient
191+
crb := &rbacv1.ClusterRoleBinding{}
192+
Eventually(func(g Gomega) {
193+
err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, crb)
194+
g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get ClusterRoleBinding %q: %v", name, err))
195+
}).WithTimeout(10*time.Second).WithPolling(1*time.Second).Should(Succeed(), "ClusterRoleBinding %q did not become visible within timeout", name)
196+
}

0 commit comments

Comments
 (0)