diff --git a/web/cypress/support/commands/coo-install-commands.ts b/web/cypress/support/commands/coo-install-commands.ts index 578c9f81e..39873b2a2 100644 --- a/web/cypress/support/commands/coo-install-commands.ts +++ b/web/cypress/support/commands/coo-install-commands.ts @@ -48,7 +48,7 @@ export const cooInstallUtils = { )}`, ); cy.exec( - `operator-sdk run bundle --timeout=10m --namespace ${ + `operator-sdk run bundle --timeout=10m --install-mode=AllNamespaces --namespace ${ MCP.namespace } --security-context-config restricted ${Cypress.env( 'KONFLUX_COO_BUNDLE_IMAGE', @@ -65,11 +65,13 @@ export const cooInstallUtils = { 'KUBECONFIG_PATH', )} apply -f ./cypress/fixtures/coo/coo-imagecontentsourcepolicy.yaml`, ); + cy.log(`Creating namespace ${MCP.namespace}`); cy.exec( `oc create namespace ${MCP.namespace} --kubeconfig ${Cypress.env( 'KUBECONFIG_PATH', )} --dry-run=client -o yaml | oc apply --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} -f -`, ); + cy.log(`Labeling namespace ${MCP.namespace} with openshift.io/cluster-monitoring=true`); cy.exec( `oc label namespace ${ MCP.namespace @@ -78,7 +80,7 @@ export const cooInstallUtils = { )}`, ); cy.exec( - `operator-sdk run bundle --timeout=10m --namespace ${ + `operator-sdk run bundle --timeout=10m --install-mode=AllNamespaces --namespace ${ MCP.namespace } --security-context-config restricted ${Cypress.env( 'CUSTOM_COO_BUNDLE_IMAGE', @@ -162,6 +164,107 @@ export const cooInstallUtils = { } }, + enableOpenShiftMode(MCP: { namespace: string }): void { + if (!Cypress.env('KONFLUX_COO_BUNDLE_IMAGE') && !Cypress.env('CUSTOM_COO_BUNDLE_IMAGE')) { + return; + } + + const kubeconfig = Cypress.env('KUBECONFIG_PATH'); + const ns = MCP.namespace; + cy.log('Enabling OpenShift mode on bundle-installed COO'); + + // Patch the CSV so OLM's source of truth includes the flag. + // Find the correct CSV and deployment index for observability-operator. + cy.exec( + `oc get csv -n ${ns} -o jsonpath=` + + `'{range .items[?(@.status.phase=="Succeeded")]}` + + `{.metadata.name}{"\\n"}{end}' --kubeconfig ${kubeconfig}`, + ).then((result) => { + const csvNames = result.stdout.trim().split('\n').filter(Boolean); + const csvName = csvNames.find((name) => name.includes('observability-operator')); + if (!csvName) { + throw new Error( + `No observability-operator CSV found in namespace ${ns}. ` + + `Available CSVs: [${csvNames.join(', ')}]`, + ); + } + cy.log(`Found CSV: ${csvName}`); + + cy.exec( + `oc get csv ${csvName} -n ${ns} -o jsonpath=` + + `'{range .spec.install.spec.deployments[*]}` + + `{.name}{"\\n"}{end}' --kubeconfig ${kubeconfig}`, + ).then((deploymentsResult) => { + const deploymentNames = deploymentsResult.stdout.trim().split('\n').filter(Boolean); + const opIdx = deploymentNames.indexOf('observability-operator'); + if (opIdx === -1) { + throw new Error( + `observability-operator not found in CSV deployments: [${deploymentNames.join(', ')}]`, + ); + } + cy.log(`Patching CSV ${csvName} deployment[${opIdx}] to add --openshift.enabled=true`); + cy.exec( + `oc patch csv ${csvName} -n ${ns} --type=json ` + + `-p '[{"op":"add","path":"/spec/install/spec/deployments/` + + `${opIdx}/spec/template/spec/containers/0/args/-",` + + `"value":"--openshift.enabled=true"}]' ` + + `--kubeconfig ${kubeconfig}`, + ); + }); + }); + + // Step 2: Patch the deployment directly to apply the change immediately. + cy.log('Patching deployment to add --openshift.enabled=true'); + cy.exec( + `oc patch deployment observability-operator -n ${ns} --type=json ` + + `-p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-",` + + `"value":"--openshift.enabled=true"}]' ` + + `--kubeconfig ${kubeconfig}`, + ); + + // Step 3: Wait for the rollout to complete. + cy.exec( + `oc rollout status deployment/observability-operator -n ${ns} ` + + `--timeout=120s --kubeconfig ${kubeconfig}`, + { timeout: 130000 }, + ); + + // Final verification: confirm the running pod actually has the flag. + cy.exec( + `oc get deployment observability-operator -n ${ns} ` + + `-o jsonpath="{.spec.template.spec.containers[0].args}" --kubeconfig ${kubeconfig}`, + ).then((result) => { + const args = result.stdout; + cy.log(`Deployment args after rollout: ${args}`); + if (!args.includes('openshift.enabled=true')) { + cy.exec(`oc get csv -n ${ns} -o yaml --kubeconfig ${kubeconfig}`, { + failOnNonZeroExit: false, + }).then((csvResult) => { + cy.log(`CSV YAML:\n${csvResult.stdout.substring(0, 3000)}`); + }); + cy.exec( + `oc get deployment observability-operator -n ${ns} -o yaml --kubeconfig ${kubeconfig}`, + ).then((yamlResult) => { + cy.log(`Deployment YAML:\n${yamlResult.stdout}`); + }); + cy.then(() => { + throw new Error( + '--openshift.enabled=true NOT found in deployment args after rollout. ' + + `Actual args: ${args}`, + ); + }); + } + }); + + cy.exec( + `oc logs -l app.kubernetes.io/name=observability-operator -n ${ns} ` + + `--tail=5 --kubeconfig ${kubeconfig}`, + { failOnNonZeroExit: false }, + ).then((result) => { + cy.log(`Operator logs after restart:\n${result.stdout}`); + }); + }, + cleanupCOONamespace(MCP: { namespace: string }): void { if (Cypress.env('SKIP_COO_INSTALL')) { return; @@ -169,6 +272,24 @@ export const cooInstallUtils = { cy.log('Remove Cluster Observability Operator namespace'); + // For bundle installs, run operator-sdk cleanup first to remove + // CatalogSource, registry pod, and other bundle-specific resources. + // The bundle package name is "observability-operator" (not the MCP.packageName + // which is "cluster-observability-operator" used for catalog installs). + if (Cypress.env('KONFLUX_COO_BUNDLE_IMAGE') || Cypress.env('CUSTOM_COO_BUNDLE_IMAGE')) { + cy.exec( + `operator-sdk cleanup observability-operator -n ${MCP.namespace} ` + + `--kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + { failOnNonZeroExit: false, timeout: 60000 }, + ).then((result) => { + if (result.code === 0) { + cy.log('operator-sdk cleanup completed successfully'); + } else { + cy.log(`operator-sdk cleanup failed (may not exist): ${result.stderr}`); + } + }); + } + cy.exec(`oc get namespace ${MCP.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, { timeout: readyTimeoutMilliseconds, failOnNonZeroExit: false, @@ -287,7 +408,7 @@ export const cooInstallUtils = { checkStatus(); cy.then(() => { - cooInstallUtils.waitForPodsDeleted(MCP.namespace); + cooInstallUtils.waitForPodsDeleted(MCP.namespace, 300000); }); } else { cy.log('Namespace does not exist, skipping deletion'); diff --git a/web/cypress/support/commands/operator-commands.ts b/web/cypress/support/commands/operator-commands.ts index 12ae5e72c..b19086d15 100644 --- a/web/cypress/support/commands/operator-commands.ts +++ b/web/cypress/support/commands/operator-commands.ts @@ -67,6 +67,11 @@ function collectDebugInfo(MP: { namespace: string }, MCP?: { namespace: string } cy.log('DEBUG not set. Skipping operator debug information collection.'); return; } + // TODO: Remove once DataViewFilters incompatibility with COO 1.5.0 is resolved + if (Cypress.env('COO_1_5_0')) { + cy.log('COO_1_5_0 set: skipping collectDebugInfo (DataViewFilters incompatibility)'); + return; + } cy.aboutModal(); cy.podImage('monitoring-plugin', MP.namespace); if (MCP && MCP.namespace) { @@ -234,6 +239,7 @@ Cypress.Commands.add( } cooInstallUtils.installCOO(MCP); cooInstallUtils.waitForCOOReady(MCP); + cooInstallUtils.enableOpenShiftMode(MCP); imagePatchUtils.setupMonitoringConsolePlugin(MCP); if (opts.healthAnalyzer) { imagePatchUtils.setupClusterHealthAnalyzer(MCP);