Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
140 changes: 55 additions & 85 deletions .tekton/integration-tests/lightspeed-console-pre-commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,68 +37,33 @@ spec:
value: $(context.pipelineRun.name)
- name: ownerUid
value: $(context.pipelineRun.uid)
- name: provision-cluster
- name: provision-ephemeral-cluster
taskRef:
resolver: git
params:
- name: url
value: https://github.com/openshift/konflux-tasks
- name: revision
value: main
- name: pathInRepo
value: tasks/provision-ephemeral-cluster/0.1/provision-ephemeral-cluster.yaml
params:
- name: ownerName
value: $(context.pipelineRun.name) # Internal usage only, use always this value here
- name: ownerUid
value: $(context.pipelineRun.uid) # Internal usage only, use always this value here
- name: workflow
value: hypershift-hostedcluster-workflow
- name: clusterProfile
value: aws-konflux-prod
- name: env
value: '{"COMPUTE_NODE_TYPE": "m5.2xlarge", "HYPERSHIFT_NODE_COUNT": "3"}'
- name: releases
value: |
{"latest":{"release":{"channel":"stable","version":"4.19","architecture":"multi"}}}
runAfter:
- eaas-provision-space
- lint
taskSpec:
results:
- name: clusterName
value: "$(steps.create-cluster.results.clusterName)"
steps:
- name: pick-version
ref:
resolver: git
params:
- name: url
value: https://github.com/konflux-ci/build-definitions.git
- name: revision
value: main
- name: pathInRepo
value: stepactions/eaas-get-latest-openshift-version-by-prefix/0.1/eaas-get-latest-openshift-version-by-prefix.yaml
params:
- name: prefix
value: "4.19."
- name: create-cluster
ref:
resolver: git
params:
- name: url
value: https://github.com/konflux-ci/build-definitions.git
- name: revision
value: main
- name: pathInRepo
value: stepactions/eaas-create-ephemeral-cluster-hypershift-aws/0.1/eaas-create-ephemeral-cluster-hypershift-aws.yaml
params:
- name: eaasSpaceSecretRef
value: $(tasks.eaas-provision-space.results.secretRef)
- name: version
value: "$(steps.pick-version.results.version)"
- name: instanceType
value: "m5.xlarge"
- name: imageContentSources
value: |
- source: registry.redhat.io/openshift-lightspeed/lightspeed-rhel9-operator
mirrors:
- quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/ols/lightspeed-operator
- source: registry.redhat.io/openshift-lightspeed/lightspeed-operator-bundle
mirrors:
- quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/ols/bundle
- source: registry.redhat.io/openshift-lightspeed/lightspeed-service-api-rhel9
mirrors:
- quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/ols/lightspeed-service
- source: registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-rhel9
mirrors:
- quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/ols/lightspeed-console
- source: registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-pf5-rhel9
mirrors:
- quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/lightspeed-console-pf5
- source: registry.redhat.io/openshift-lightspeed/lightspeed-to-dataverse-exporter-rhel9
mirrors:
- quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/lightspeed-to-dataverse-exporter
- source: registry.redhat.io/openshift-lightspeed/openshift-mcp-server-rhel9
mirrors:
- quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/openshift-mcp-server
- name: extract-snapshot-metadata
description: Extract console image and commit from SNAPSHOT input
runAfter:
Expand Down Expand Up @@ -174,12 +139,14 @@ spec:
description: Task to run tests from service repository
runAfter:
- extract-snapshot-metadata
- provision-cluster
- provision-ephemeral-cluster
params:
- name: console-image
value: $(tasks.extract-snapshot-metadata.results.console-image)
- name: commit
value: $(tasks.extract-snapshot-metadata.results.commit)
- name: clusterCredentialsSecretRef
value: $(tasks.provision-ephemeral-cluster.results.secretRef)
taskSpec:
volumes:
- name: openai-token
Expand All @@ -193,24 +160,10 @@ spec:
params:
- name: console-image
- name: commit
- name: clusterCredentialsSecretRef
type: string
description: "The secret that holds the cluster credentials"
steps:
- name: get-kubeconfig
ref:
resolver: git
params:
- name: url
value: https://github.com/konflux-ci/build-definitions.git
- name: revision
value: main
- name: pathInRepo
value: stepactions/eaas-get-ephemeral-cluster-credentials/0.1/eaas-get-ephemeral-cluster-credentials.yaml
params:
- name: eaasSpaceSecretRef
value: $(tasks.eaas-provision-space.results.secretRef)
- name: clusterName
value: "$(tasks.provision-cluster.results.clusterName)"
- name: credentials
value: credentials
- name: run-e2e-tests
onError: continue
volumeMounts:
Expand All @@ -220,7 +173,12 @@ spec:
mountPath: /credentials
env:
- name: KUBECONFIG_PATH
value: "/credentials/$(steps.get-kubeconfig.results.kubeconfig)"
value: "/credentials/kubeconfig"
- name: KUBECONFIG_VALUE
valueFrom:
secretKeyRef:
name: $(params.clusterCredentialsSecretRef)
key: kubeconfig
- name: LOGIN_IDP
value: "kube:admin"
- name: LLM_TOKEN_PATH
Expand All @@ -229,17 +187,13 @@ spec:
value: "$(params.console-image)"
- name: COMMIT_SHA
value: "$(params.commit)"
- name: PASSWORD_PATH
value: "/credentials/$(steps.get-kubeconfig.results.passwordPath)"
- name: BASE_URL
value: "$(steps.get-kubeconfig.results.consoleURL)"
resources:
limits:
memory: 8Gi
image: mcr.microsoft.com/playwright:v1.62.1-noble
script: |
echo "$KUBECONFIG_VALUE" > /credentials/kubeconfig
echo "COMMIT_SHA: ${COMMIT_SHA}"
echo "BASE_URL: ${BASE_URL}"
echo "CONSOLE_IMAGE: ${CONSOLE_IMAGE}"
echo "KUBECONFIG_PATH: ${KUBECONFIG_PATH}"
echo "---------------------------------------------"
Expand All @@ -248,6 +202,8 @@ spec:
&& chmod +x kubectl oc \
&& mv oc kubectl /usr/local/bin/
echo "---------------------------------------------"
export BASE_URL=https://$(oc --kubeconfig "$KUBECONFIG_PATH" get route console -n openshift-console -o go-template --template="{{.spec.host}}")
echo "BASE_URL: ${BASE_URL}"
export OPERATOR_SDK_VERSION=1.36.1
export ARCH=$(case $(uname -m) in x86_64) echo -n amd64 ;; aarch64) echo -n arm64 ;; *) echo -n $(uname -m) ;; esac)
export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v${OPERATOR_SDK_VERSION}
Expand All @@ -274,7 +230,6 @@ spec:
echo "---------------------------------------------"
NODE_OPTIONS=--max-old-space-size=4096 npm ci --omit=optional --no-fund
echo "---------------------------------------------"
export LOGIN_PASSWORD=$(cat ${PASSWORD_PATH})
set +e
npx playwright test
err_status=$?
Expand All @@ -300,7 +255,7 @@ spec:
- name: credentials
value: "credentials"
- name: kubeconfig
value: "$(steps.get-kubeconfig.results.kubeconfig)"
value: "kubeconfig"
- name: artifact-dir
value: "/workspace/artifacts/konflux-artifacts"
# validate that the cluster resources are available in another tekton step
Expand Down Expand Up @@ -377,3 +332,18 @@ spec:
value: "quay.io/openshift-lightspeed/ols-console-artifacts"
- name: artifact-credentials-secret
value: ols-konflux-artifacts-bot
- name: deprovision-ephemeral-cluster
taskRef:
resolver: git
params:
- name: url
value: https://github.com/openshift/konflux-tasks
- name: revision
value: main
- name: pathInRepo
value: tasks/deprovision-ephemeral-cluster/0.1/deprovision-ephemeral-cluster.yaml
params:
- name: testPlatformClusterClaimName
value: $(tasks.provision-ephemeral-cluster.results.testPlatformClusterClaimName)
- name: testPlatformClusterClaimNamespace
value: $(tasks.provision-ephemeral-cluster.results.testPlatformClusterClaimNamespace)
100 changes: 82 additions & 18 deletions tests/support/global-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const globalSetup = async (config: FullConfig) => {
oc(['adm', 'policy', 'add-cluster-role-to-user', 'cluster-admin', username]);
oc(['adm', 'policy', 'add-cluster-role-to-user', 'lightspeed-operator-query-access', username]);

let oauthOrigin: string | undefined;
try {
const oauthResult = oc([
'get',
Expand All @@ -29,7 +30,7 @@ const globalSetup = async (config: FullConfig) => {
'go-template',
'--template={{index .redirectURIs 0}}',
]);
const oauthOrigin = new URL(oauthResult.trim().replace(/"/g, '')).origin;
oauthOrigin = new URL(oauthResult.trim().replace(/"/g, '')).origin;
console.log(`OAuth origin: ${oauthOrigin}`);
} catch {
console.log('oauthclient not available on this cluster, skipping OAuth origin lookup');
Expand All @@ -52,10 +53,15 @@ spec:
name: openai-api-keys
url: https://api.openai.com/v1
models:
- name: gpt-4o-mini
- name: gpt-5.4-mini
ols:
defaultModel: gpt-4o-mini
defaultModel: gpt-5.4-mini
defaultProvider: openai
# The released operator bundle configures the MCP server with the removed
# metrics toolset, while its current MCP image only accepts
# observability/metrics. Disable introspection for console UI tests until
# the bundle and MCP image are published as a compatible pair.
introspectionEnabled: false
logLevel: INFO`;

// Check if operator is already installed
Expand Down Expand Up @@ -245,29 +251,87 @@ spec:
]);
}

// Log in via browser and save storageState
// Log in via browser and save storageState. The ephemeral-cluster task only
// guarantees an admin kubeconfig; unlike the legacy EaaS task, it does not
// guarantee a kubeadmin password. Its kubeconfig can be client-certificate
// based, in which case `oc whoami --show-token` has no token to return.
// Create a short-lived, cluster-admin service-account token for OAuth in
// that case instead of relying on an unsupported secret key.
let kubeToken: string;
try {
kubeToken = oc(['whoami', '--show-token']).trim();
} catch {
const serviceAccount = 'playwright-e2e';
try {
oc(['create', 'serviceaccount', serviceAccount, '-n', OLS_NAMESPACE]);
} catch {
// The account may have been created by an earlier setup attempt.
}
oc([
'adm',
'policy',
'add-cluster-role-to-user',
'cluster-admin',
`system:serviceaccount:${OLS_NAMESPACE}:${serviceAccount}`,
]);
kubeToken = oc([
'create',
'token',
serviceAccount,
'-n',
OLS_NAMESPACE,
'--duration=1h',
]).trim();
}
const consoleOrigin = new URL(baseURL).origin;
const browser = await chromium.launch();
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();

// Do not apply the admin token to every browser request: the console can
// load third-party resources. It is only needed by the console and OAuth
// origins while the authorization code and console session are established.
if (kubeToken) {
await context.route('**/*', async (route) => {
const request = route.request();
const origin = new URL(request.url()).origin;
if (origin !== consoleOrigin && origin !== oauthOrigin) {
await route.continue();
return;
}
await route.continue({
headers: { ...request.headers(), authorization: `Bearer ${kubeToken}` },
});
});
}

const page = await context.newPage();
await page.goto(baseURL);

// Perform login
const idp = process.env.LOGIN_IDP || 'kube:admin';
const password = process.env.LOGIN_PASSWORD!;
const waitForConsole = () => page.waitForURL('**/');

// Select IDP if the login page shows identity provider selection
const idpLink = page.locator(`a:has-text("${idp}")`);
if (await idpLink.isVisible({ timeout: 10_000 }).catch(() => false)) {
await idpLink.click();
}
// Retain password login for callers that explicitly provide it, but the
// ephemeral-cluster pipeline authenticates with the kubeconfig token above.
const usernameInput = page.locator('#inputUsername');
if (await usernameInput.isVisible({ timeout: 10_000 }).catch(() => false)) {
const password = process.env.LOGIN_PASSWORD;
if (!password) {
throw new Error('OAuth bearer-token login did not succeed and LOGIN_PASSWORD is not set');
}

await page.locator('#inputUsername').fill(username);
await page.locator('#inputPassword').fill(password);
await page.locator('button[type=submit]').click();
const idp = process.env.LOGIN_IDP || 'kube:admin';
const idpLink = page.locator(`a:has-text("${idp}")`);
if (await idpLink.isVisible({ timeout: 10_000 }).catch(() => false)) {
await idpLink.click();
}

// Wait for console to load
await page.waitForURL('**/');
await usernameInput.fill(username);
await page.locator('#inputPassword').fill(password);
const consoleNavigation = waitForConsole();
await page.locator('button[type=submit]').click();
await consoleNavigation;
} else {
await waitForConsole();
}

// Dismiss guided tour and set localStorage to prevent it reappearing
const tourSettings = {
Expand Down