Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 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
4fe402e
Merge branch 'main' into synchronize
Nov 28, 2025
3a919cc
UPSTREAM: <carry>: Add OpenShift specific files
dtfranz Oct 26, 2023
aecc583
UPSTREAM: <carry>: Add new tests for single/own namespaces install modes
camilamacedo86 Oct 6, 2025
8b2e266
UPSTREAM: <carry>: Upgrade OCP image from 4.20 to 4.21
camilamacedo86 Oct 13, 2025
8d1977f
UPSTREAM: <carry>: [Default Catalog Tests] - Change logic to get ocp …
camilamacedo86 Oct 13, 2025
d826906
UPSTREAM: <carry>: Update OCP catalogs to v4.21
tmshort Oct 13, 2025
668070f
UPSTREAM: <carry>: support singleown cases in disconnected
kuiwang02 Oct 16, 2025
e08376e
UPSTREAM: <carry>: fix cases 81696 and 74618 for product code changes
kuiwang02 Oct 17, 2025
65749c8
UPSTREAM: <carry>: Define Default timeouts and apply their usage accr…
camilamacedo86 Oct 22, 2025
34cd245
UPSTREAM: <carry>: Update to new feature-gate options in helm
tmshort Oct 22, 2025
48f7325
UPSTREAM: <carry>: Fix flake for single/own ns tests by ensuring uniq…
camilamacedo86 Oct 22, 2025
712450f
UPSTREAM: <carry>: [OTE]: Enhance single/own ns based on review comme…
camilamacedo86 Oct 24, 2025
404f4f2
UPSTREAM: <carry>: Update OwnSingle template to use spec.config.inlin…
kuiwang02 Nov 3, 2025
3e29ac4
UPSTREAM: <carry>: [OTE]: Add webhook cleanup validation on extension…
camilamacedo86 Nov 4, 2025
217b20d
UPSTREAM: <carry>: Add [OTP] to migrated cases
kuiwang02 Nov 7, 2025
bde7f25
UPSTREAM: <carry>: [OTE]: Upgrade dependencies used
camilamacedo86 Nov 5, 2025
538daa2
UPSTREAM: <carry>: fix(OTE): fix OpenShift Kubernetes replace version…
camilamacedo86 Nov 10, 2025
68c01ff
UPSTREAM: <carry>: [Default Catalog Tests] Upgrade go 1.24.6 and depe…
camilamacedo86 Nov 11, 2025
1c294fa
UPSTREAM: <carry>: add disconnected environment support with custom p…
kuiwang02 Nov 12, 2025
e037935
UPSTREAM: <carry>: migrate jiazha test cases to OTE
jianzhangbjz Nov 14, 2025
a5f8b93
UPSTREAM: <carry>: migrate clustercatalog case to ote
Xia-Zhao-rh Oct 17, 2025
2e48488
UPSTREAM: <carry>: migrate olmv1 QE stress cases
kuiwang02 Nov 20, 2025
1304080
UPSTREAM: <carry>: Use busybox/httpd to simulate probes
tmshort Nov 25, 2025
6d3b37b
UPSTREAM: <carry>: migrate olmv1 QE cases
Xia-Zhao-rh Nov 25, 2025
f44ea13
UPSTREAM: <drop>: go mod vendor
Nov 28, 2025
ecac2a6
UPSTREAM: <drop>: remove upstream GitHub configuration
Nov 28, 2025
37da6eb
UPSTREAM: <drop>: configure the commit-checker
Nov 28, 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>: migrate olmv1 QE stress cases
  • Loading branch information
kuiwang02 authored and ci-robot committed Nov 28, 2025
commit 2e48488d81258edca4246667263c466a7475b0d3
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,22 @@
"exclude": "topology==\"External\""
}
},
{
"name": "[sig-olmv1][Jira:OLM] OLM v1 for stress PolarionID:81509-[OTP][Skipped:Disconnected][OlmStress]olmv1 create mass operator to see if they all are installed successfully [Slow][Timeout:330m]",
"labels": {
"Extended": {},
"NonHyperShiftHOST": {},
"StressTest": {}
},
"resources": {
"isolation": {}
},
"source": "openshift:payload:olmv1",
"lifecycle": "blocking",
"environmentSelector": {
"exclude": "topology==\"External\""
}
},
{
"name": "[sig-olmv1][OCPFeatureGate:NewOLM][Skipped:Disconnected] OLMv1 Catalogs should be installed",
"labels": {},
Expand Down
15 changes: 14 additions & 1 deletion openshift/tests-extension/test/qe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,20 @@ All migrated test case code needs the following changes to run in the new test f

**Environment Validation for Disconnected-Supporting Migrated Test Cases:**

If your test case supports disconnected environments, you MUST call `ValidateAccessEnvironment` at the beginning of the test:
**When to use `ValidateAccessEnvironment`:**

1. **Test cases that create ClusterCatalog or ClusterExtension**:
- If your test supports disconnected environments (both connected+disconnected, or disconnected-only)
- AND your test creates ClusterCatalog or ClusterExtension resources
- **MUST** call `ValidateAccessEnvironment(oc)` at the beginning of the test
- This applies to both newly created test cases and migrated test cases

2. **Test cases that do NOT create both ClusterCatalog or ClusterExtension**:
- Optional to use `ValidateAccessEnvironment(oc)`
- Using it won't cause errors, but it's not required
- The validation is primarily for ensuring catalog images can be mirrored

**Usage example:**

```go
g.It("test case supporting disconnected", func() {
Expand Down
146 changes: 146 additions & 0 deletions openshift/tests-extension/test/qe/specs/olmv1_stress.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package specs

import (
"fmt"
"path/filepath"
"time"

g "github.com/onsi/ginkgo/v2"
o "github.com/onsi/gomega"
e2e "k8s.io/kubernetes/test/e2e/framework"

exutil "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/test/qe/util"
olmv1util "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/test/qe/util/olmv1util"
)

var _ = g.Describe("[sig-olmv1][Jira:OLM] OLM v1 for stress", func() {

defer g.GinkgoRecover()
var (
oc = exutil.NewCLIWithoutNamespace("default")
)

g.BeforeEach(func() {
exutil.SkipMicroshift(oc)
exutil.SkipNoOLMv1Core(oc)
})

// author: [email protected]
g.It("PolarionID:81509-[OTP][Skipped:Disconnected][OlmStress]olmv1 create mass operator to see if they all are installed successfully [Slow][Timeout:330m]", g.Label("StressTest"), g.Label("NonHyperShiftHOST"), func() {
var (
caseID = "81509"
prefixCatalog = "catalog-" + caseID
prefixSa = "sa-" + caseID
prefixCe = "ce-" + caseID
prefixNs = "ns-" + caseID
prefixPackage = "stress-olmv1-c"
prefixImage = "quay.io/olmqe/stress-index:vokv"
nsOc = "openshift-operator-controller"
nsCatalog = "openshift-catalogd"
catalogLabel = "control-plane=catalogd-controller-manager"
ocLabel = "control-plane=operator-controller-controller-manager"
baseDir = exutil.FixturePath("testdata", "olm")
clustercatalogTemplate = filepath.Join(baseDir, "clustercatalog.yaml")
clusterextensionTemplate = filepath.Join(baseDir, "clusterextension.yaml")
saClusterRoleBindingTemplate = filepath.Join(baseDir, "sa-admin.yaml")
)

if !olmv1util.IsPodReady(oc, nsCatalog, catalogLabel) {
_, _ = olmv1util.Get(oc, "pod", "-n", nsCatalog, "-l", catalogLabel, "-o", "yaml")
exutil.AssertWaitPollNoErr(fmt.Errorf("the pod with %s is not correct", catalogLabel), "the pod with app=catalog-operator is not correct")
}
if !olmv1util.IsPodReady(oc, nsOc, ocLabel) {
_, _ = olmv1util.Get(oc, "pod", "-n", nsOc, "-l", ocLabel, "-o", "yaml")
exutil.AssertWaitPollNoErr(fmt.Errorf("the pod with %s is not correct", ocLabel), "the pod with app=olm-operator is not correct")
}

startTime := time.Now().UTC()
e2e.Logf("Start time: %s", startTime.Format(time.RFC3339))

// for i := 0; i < 500; i++ {
for i := 900; i < 969; i++ {
// it is not enough with 330m for one case if we run 100 times
e2e.Logf("=================it is round %v=================", i)
ns := fmt.Sprintf("%s-%d", prefixNs, i)
clustercatalog := olmv1util.ClusterCatalogDescription{
Name: fmt.Sprintf("%s-%d", prefixCatalog, i),
Imageref: fmt.Sprintf("%s%d", prefixImage, i),
Template: clustercatalogTemplate,
}
saCrb := olmv1util.SaCLusterRolebindingDescription{
Name: fmt.Sprintf("%s-%d", prefixSa, i),
Namespace: ns,
Template: saClusterRoleBindingTemplate,
}
ce := olmv1util.ClusterExtensionDescription{
Name: fmt.Sprintf("%s-%d", prefixCe, i),
PackageName: fmt.Sprintf("%s%d", prefixPackage, i),
Channel: "alpha",
Version: ">=0.0.1",
InstallNamespace: ns,
SaName: fmt.Sprintf("%s-%d", prefixSa, i),
Template: clusterextensionTemplate,
}
g.By(fmt.Sprintf("Create namespace for %d", i))
// defer oc.WithoutNamespace().AsAdmin().Run("delete").Args("ns", ns, "--ignore-not-found").Execute()
// it take time delete ns which is not necessary. currently 5.5h is not enough to delete them.
// so I prefer to keep ns to save case duration
err := oc.WithoutNamespace().AsAdmin().Run("create").Args("ns", ns).Execute()
o.Expect(err).NotTo(o.HaveOccurred())

o.Expect(olmv1util.Appearance(oc, exutil.Appear, "ns", ns)).To(o.BeTrue())

g.By(fmt.Sprintf("Create clustercatalog for %d", i))
e2e.Logf("=========Create clustercatalog %v=========", clustercatalog.Name)
defer clustercatalog.Delete(oc)
err = clustercatalog.CreateWithoutCheck(oc)
o.Expect(err).NotTo(o.HaveOccurred())
clustercatalog.WaitCatalogStatus(oc, "true", "Serving", 0)

g.By(fmt.Sprintf("Create SA for clusterextension for %d", i))
defer saCrb.Delete(oc)
saCrb.Create(oc)

g.By(fmt.Sprintf("check ce to be installed for %d", i))
e2e.Logf("=========Create clusterextension %v=========", ce.Name)
defer ce.Delete(oc)
err = ce.CreateWithoutCheck(oc)
o.Expect(err).NotTo(o.HaveOccurred())
ce.CheckClusterExtensionCondition(oc, "Progressing", "reason", "Succeeded", 10, 600, 0)
ce.WaitClusterExtensionCondition(oc, "Installed", "True", 0)
}

endTime := time.Now().UTC()
e2e.Logf("End time: %v", endTime.Format(time.RFC3339))

duration := endTime.Sub(startTime)
minutes := int(duration.Minutes())
if minutes < 1 {
minutes = 1
}

podName, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("pods", "-l", catalogLabel, "-o=jsonpath={.items[0].metadata.name}", "-n", nsCatalog).Output()
if err == nil {
if !olmv1util.WriteErrToArtifactDir(oc, nsCatalog, podName, "error", "Unhandled|Reconciler error|level=info", caseID, minutes) {
e2e.Logf("no error log into artifact for pod %s in %s", podName, nsCatalog)
}
}
podName, err = oc.AsAdmin().WithoutNamespace().Run("get").Args("pods", "-l", ocLabel, "-o=jsonpath={.items[0].metadata.name}", "-n", nsOc).Output()
if err == nil {
if !olmv1util.WriteErrToArtifactDir(oc, nsOc, podName, "error", "Unhandled|Reconciler error|level=info", caseID, minutes) {
e2e.Logf("no error log into artifact for pod %s in %s", podName, nsOc)
}
}

if !olmv1util.IsPodReady(oc, nsCatalog, catalogLabel) {
_, _ = olmv1util.Get(oc, "pod", "-n", nsCatalog, "-l", catalogLabel, "-o", "yaml")
exutil.AssertWaitPollNoErr(fmt.Errorf("the pod with %s is not correct", catalogLabel), "the pod with app=catalog-operator is not correct")
}
if !olmv1util.IsPodReady(oc, nsOc, ocLabel) {
_, _ = olmv1util.Get(oc, "pod", "-n", nsOc, "-l", ocLabel, "-o", "yaml")
exutil.AssertWaitPollNoErr(fmt.Errorf("the pod with %s is not correct", ocLabel), "the pod with app=olm-operator is not correct")
}

})

})
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ func SkipNonAmd64SingleArch(oc *exutil.CLI) Architecture {
func getNodeArchitectures(oc *exutil.CLI) []string {
output, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("nodes", "-o=jsonpath={.items[*].status.nodeInfo.architecture}").Output()
if err != nil {
e2e.Failf("unable to get cluster node architectures: %v", err)
g.Skip(fmt.Sprintf("unable to get cluster node architectures: %v", err))
}
if output == "" {
e2e.Failf("no nodes found or architecture information missing")
g.Skip("no nodes found or architecture information missing")
}
return strings.Fields(output) // Use Fields instead of Split to handle multiple spaces
}
Expand All @@ -97,7 +97,7 @@ func getNodeArchitectures(oc *exutil.CLI) []string {
func GetAvailableArchitecturesSet(oc *exutil.CLI) []Architecture {
architectureStrings := getNodeArchitectures(oc)
if len(architectureStrings) == 0 {
e2e.Failf("no node architectures found")
g.Skip("no node architectures found")
}

// Use map for deduplication with Architecture as key
Expand Down Expand Up @@ -199,7 +199,7 @@ func (a Architecture) String() string {
func ClusterArchitecture(oc *exutil.CLI) Architecture {
architectureStrings := getNodeArchitectures(oc)
if len(architectureStrings) == 0 {
e2e.Failf("no node architectures found")
g.Skip("no node architectures found")
}

// Filter out empty strings and convert to Architecture
Expand All @@ -211,7 +211,7 @@ func ClusterArchitecture(oc *exutil.CLI) Architecture {
}

if len(architectures) == 0 {
e2e.Failf("no valid node architectures found")
g.Skip("no valid node architectures found")
}

// Check if all architectures are the same
Expand Down Expand Up @@ -267,7 +267,7 @@ func GetControlPlaneArch(oc *exutil.CLI) Architecture {

architectureStr = strings.TrimSpace(architectureStr)
if architectureStr == "" {
e2e.Failf("Control plane node %s has no architecture information", masterNode)
g.Skip(fmt.Sprintf("Control plane node %s has no architecture information", masterNode))
}

return FromString(architectureStr)
Expand Down
2 changes: 1 addition & 1 deletion openshift/tests-extension/test/qe/util/olmv1util/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ func HasExternalNetworkAccess(oc *exutil.CLI) bool {
// Note: In disconnected environments, curl will fail and bash will return non-zero exit code,
// causing DebugNodeWithChroot to return an error. We ignore this error and rely on output checking.
cmd := `timeout 10 curl -k https://quay.io > /dev/null 2>&1; [ $? -eq 0 ] && echo "connected"`
output, _ := exutil.DebugNodeWithChroot(oc, masterNode, "bash", "-c", cmd)
output, _ := exutil.DebugNodeWithOptionsAndChroot(oc, masterNode, []string{"--to-namespace=default"}, "bash", "-c", cmd)

// Check if the output contains "connected"
// - Connected environment: curl succeeds -> echo "connected" -> output contains "connected"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
- endpoint: {{.PROMETHEUS_URL}}
token: {{.PROMETHEUS_TOKEN}}
step: 10s
skipTLSVerify: true
metrics:
- metrics-profiles/metrics-aggregated.yml
indexer:
type: local
metricsDirectory: collected-metrics-{{.UUID}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Containers & pod metrics

- query: (sum(irate(container_cpu_usage_seconds_total{container="manager",namespace="openshift-catalogd"}[2m]) * 100) by (container, pod)) > 0
metricName: containerCPU-Catlogd

- query: (sum(irate(container_cpu_usage_seconds_total{container="manager",namespace="openshift-operator-controller"}[2m]) * 100) by (container, pod)) > 0
metricName: containerCPU-OpCon
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
global:
gc: {{.GC}}
gcMetrics: {{.GC_METRICS}}
measurements:
- name: podLatency


jobs:
- name: {{.OPERATION}}
jobType: create
jobIterations: {{.JOB_ITERATIONS}}
namespace: {{.OPERATION}}
namespacedIterations: {{.NAMESPACED_ITERATIONS}}
iterationsPerNamespace: {{.ITERATIONS_PER_NAMESPACE}}
cleanup: true
podWait: true
waitWhenFinished: true
maxWaitTimeout: {{.MAX_WAIT_TIMEOUT}}
jobIterationDelay: {{.JOB_ITERATION_DELAY}}
jobPause: {{.JOB_PAUSE}}
qps: {{.QPS}}
burst: {{.BURST}}
executionMode: parallel
verifyObjects: true
errorOnVerify: true
skipIndexing: false
preLoadImages: true
preLoadPeriod: 15s
churn: false
defaultMissingKeysWithZero: false
namespaceLabels:
security.openshift.io/scc.podSecurityLabelSync: false
pod-security.kubernetes.io/enforce: privileged
pod-security.kubernetes.io/audit: privileged
pod-security.kubernetes.io/warn: privileged
objects:

- objectTemplate: templates/catalogd.yml
replicas: 1
inputVars:
prefixImageName: "quay.io/olmqe/stress-index:vokv"
waitOptions:
customStatusPaths:
- key: ".conditions[] | select(.type==\"Serving\") | .status"
value: "True"

- objectTemplate: templates/clusterrole.yml
replicas: 1

- objectTemplate: templates/sa.yml
replicas: 1

- objectTemplate: templates/clusterrolebinding.yml
replicas: 1
inputVars:
prefixNamespace: {{.OPERATION}}

- objectTemplate: templates/ce.yml
replicas: 1
inputVars:
prefixNamespace: {{.OPERATION}}
prefixPkgName: {{.PREFIX_PKG_NAME_V1}}
waitOptions:
customStatusPaths:
- key: ".conditions[] | select(.type==\"Installed\") | .status"
value: "True"
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: olm.operatorframework.io/v1
kind: ClusterCatalog
metadata:
name: "clustercatalog-{{.Iteration}}"
spec:
source:
type: Image
image:
ref: "{{.prefixImageName}}{{.Iteration}}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: olm.operatorframework.io/v1
kind: ClusterExtension
metadata:
name: "ce-{{.Iteration}}"
spec:
namespace: "{{.prefixNamespace}}-{{.Iteration}}"
serviceAccount:
name: "ins-sa-{{.Iteration}}"
source:
sourceType: Catalog
catalog:
packageName: "{{.prefixPkgName}}{{.Iteration}}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: "ins-admin-clusterrole-{{.Iteration}}"
rules:
- apiGroups:
- "*"
resources:
- "*"
verbs:
- "*"
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: "ins-admin-clusterrole-binding-{{.Iteration}}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: "ins-admin-clusterrole-{{.Iteration}}"
subjects:
- kind: ServiceAccount
name: "ins-sa-{{.Iteration}}"
namespace: "{{.prefixNamespace}}-{{.Iteration}}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: "ins-sa-{{.Iteration}}"
6 changes: 6 additions & 0 deletions openshift/tests-extension/test/qe/util/stress/util/ma.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env python3

from ma.cli.__main__ import main

if __name__ == "__main__":
main()
Loading