diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 00000000..9ca42e47 --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,163 @@ +name: "[E2E Tests] Minikube" + +on: + workflow_dispatch: + inputs: + theia-cloud-helm-branch: + description: "Theia Cloud Helm Branch to use" + type: string + default: "main" + schedule: + - cron: "0 13 * * 0" + +permissions: + contents: read + +concurrency: + group: ci-e2e-theia-cloud-minikube-${{ github.ref }} + cancel-in-progress: true + +jobs: + runtests: + name: "Run Tests on Minikube (K8s: ${{ matrix.kubernetes }}, Paths: ${{ matrix.paths }}, Ephemeral: ${{ matrix.ephemeral }}, Keycloak: ${{ matrix.keycloak }})" + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + kubernetes: [v1.32.0, v1.31.4, v1.30.8, v1.29.12] + paths: [true, false] + ephemeral: [true, false] + keycloak: [true, false] + steps: + - name: Set Helm Branch for Scheduled Runs + if: ${{ github.event_name == 'schedule' }} + run: echo "INPUT_THEIA_CLOUD_HELM_BRANCH=main" >> $GITHUB_ENV + + - name: Set Helm Branch for Manual Runs + if: ${{ github.event_name == 'workflow_dispatch' }} + run: echo "INPUT_THEIA_CLOUD_HELM_BRANCH=${{ github.event.inputs.theia-cloud-helm-branch }}" >> $GITHUB_ENV + + - name: Checkout Theia Cloud + uses: actions/checkout@v4 + with: + path: "./theia-cloud" + + - name: Checkout Theia Cloud Helm + uses: actions/checkout@v4 + with: + repository: "eclipsesource/theia-cloud-helm" + ref: "${{ env.INPUT_THEIA_CLOUD_HELM_BRANCH }}" + path: "./theia-cloud-helm" + + - name: Setup Minikube + uses: manusa/actions-setup-minikube@92af4db914ab207f837251cd53eb7060e6477614 + with: + minikube version: v1.33.1 + kubernetes version: ${{ matrix.kubernetes }} + github token: ${{ secrets.GITHUB_TOKEN }} + start args: "--force" + + - name: Enable Minikube Addons + run: | + minikube addons enable dashboard + minikube addons enable default-storageclass + minikube addons enable ingress + minikube addons enable metrics-server + + - name: List Minikube Addons + run: minikube addons list + + - name: Patch Ingress to allow snippet annotations and restart + run: | + kubectl -n ingress-nginx patch cm ingress-nginx-controller --patch '{"data":{"allow-snippet-annotations":"true"}}' + kubectl -n ingress-nginx delete pod -l app.kubernetes.io/name=ingress-nginx + + # we use the none driver, so minikube should see the same images on the host + - name: Build Theia Cloud Images + run: | + cd theia-cloud + docker build --no-cache -t theiacloud/theia-cloud-service:minikube-ci-e2e -f dockerfiles/service/Dockerfile . + docker build --no-cache -t theiacloud/theia-cloud-operator:minikube-ci-e2e -f dockerfiles/operator/Dockerfile . + docker build --no-cache -t theiacloud/theia-cloud-landing-page:minikube-ci-e2e -f dockerfiles/landing-page/Dockerfile . + docker build --no-cache -t theiacloud/theia-cloud-wondershaper:minikube-ci-e2e -f dockerfiles/wondershaper/Dockerfile . + docker build --no-cache -t theiacloud/theia-cloud-conversion-webhook:minikube-ci-e2e -f dockerfiles/conversion-webhook/Dockerfile . + docker build --no-cache -t theiacloud/theia-cloud-demo:latest -f demo/dockerfiles/demo-theia-docker/Dockerfile demo/dockerfiles/demo-theia-docker/. + docker tag theiacloud/theia-cloud-demo:latest theiacloud/theia-cloud-demo:minikube-ci-e2e + docker build --no-cache -t theiacloud/theia-cloud-activity-demo:minikube-ci-e2e -f demo/dockerfiles/demo-theia-monitor-vscode/Dockerfile demo/dockerfiles/demo-theia-monitor-vscode/. + docker build --no-cache -t theiacloud/theia-cloud-activity-demo-theia:minikube-ci-e2e -f demo/dockerfiles/demo-theia-monitor-theia/Dockerfile . + + - name: Get NGINX Ingress Controller Host + id: ingress_info + run: | + INGRESS_HOST=$(kubectl get svc -n ingress-nginx ingress-nginx-controller -o jsonpath='{.spec.clusterIP}') + echo "INGRESS_HOST=$INGRESS_HOST" >> $GITHUB_ENV + + - name: Run Terraform + run: | + cd theia-cloud/terraform/ci-configurations + terraform init + terraform apply \ + -var="ingress_ip=${{ env.INGRESS_HOST }}" \ + -var="use_paths=${{ matrix.paths }}" \ + -var="use_ephemeral_storage=${{ matrix.ephemeral }}" \ + -var="enable_keycloak=${{ matrix.keycloak }}" \ + -auto-approve + + - name: List All Services in All Namespaces + run: kubectl get services --all-namespaces + + - name: List All AppDefinitions in All Namespaces + run: kubectl get appdefinitions --all-namespaces + + - name: List all Pods Images + run: kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq + + - name: Wait for Deployments to be Ready + run: | + kubectl wait --namespace ingress-nginx --for=condition=available deployment/ingress-nginx-controller --timeout=300s + kubectl wait --namespace theiacloud --for=condition=available deployment/conversion-webhook --timeout=300s + kubectl wait --namespace theiacloud --for=condition=available deployment/landing-page-deployment --timeout=300s + kubectl wait --namespace theiacloud --for=condition=available deployment/operator-deployment --timeout=300s + kubectl wait --namespace theiacloud --for=condition=available deployment/service-deployment --timeout=300s + + # URLs + # service: servicex + # landing: trynow + # instance: instances + - name: Access NGINX Ingress URL + if: ${{ matrix.paths == false }} + run: | + curl -LkI "https://trynow.${{ env.INGRESS_HOST }}.nip.io/" + + - name: Access NGINX Ingress URL + if: ${{ matrix.paths == true }} + run: | + curl -LkI "https://${{ env.INGRESS_HOST }}.nip.io/trynow/" + + - name: Get CA cert + run: | + kubectl get secret theia-cloud-ca-key-pair -n cert-manager -o jsonpath='{.data.tls\.crt}' | base64 --decode > ca.crt + ls -al + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies and run tests + run: | + cd theia-cloud/node + npm ci + npm run build -w e2e-tests + export MATRIX_PATHS=${{ matrix.paths }} + export MATRIX_EPHEMERAL=${{ matrix.ephemeral }} + export MATRIX_KEYCLOAK=${{ matrix.keycloak }} + npm run ui-tests -w e2e-tests + + - name: Upload Playwright test failure screenshots + if: failure() + uses: actions/upload-artifact@v3 + with: + name: test-failure-screenshots + path: theia-cloud/node/e2e-tests/test-results/**/*.png + retention-days: 7 diff --git a/.gitignore b/.gitignore index 79ffbe1a..6090565b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .openapi-generator .openapi-generator-ignore openapitools.json -.DS_Store \ No newline at end of file +.DS_Store +node/e2e-tests/test-results/.last-run.json \ No newline at end of file diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pv/MinikubePersistentVolumeCreator.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pv/MinikubePersistentVolumeCreator.java index b8bc5271..2fac66b3 100644 --- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pv/MinikubePersistentVolumeCreator.java +++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/pv/MinikubePersistentVolumeCreator.java @@ -64,8 +64,7 @@ public Optional createAndApplyPersistentVolume(String correlat e); return Optional.empty(); } - return client.persistentVolumesClient().loadAndCreate(correlationId, persistentVolumeYaml, - volume -> volume.addOwnerReference(workspace)); + return client.persistentVolumesClient().loadAndCreate(correlationId, persistentVolumeYaml); } @Override diff --git a/node/configs/license-check-exclusions.json b/node/configs/license-check-exclusions.json index 7a4db807..7cfd39ba 100644 --- a/node/configs/license-check-exclusions.json +++ b/node/configs/license-check-exclusions.json @@ -1,4 +1,5 @@ { "npm/npmjs/-/landing-page/0.1.0": "Theia Cloud Internal Package", - "npm/npmjs/-/testing-page/0.1.0": "Theia Cloud Internal Package" + "npm/npmjs/-/testing-page/0.1.0": "Theia Cloud Internal Package", + "npm/npmjs/-/e2e-tests/0.1.0": "Theia Cloud Internal Package" } diff --git a/node/e2e-tests/configs/playwright.config.ts b/node/e2e-tests/configs/playwright.config.ts new file mode 100644 index 00000000..6b81971f --- /dev/null +++ b/node/e2e-tests/configs/playwright.config.ts @@ -0,0 +1,24 @@ +import { PlaywrightTestConfig } from '@playwright/test'; + +const config: PlaywrightTestConfig = { + testDir: '../lib/tests', + testMatch: ['**/*.js'], + workers: 1, + fullyParallel: false, + // Timeout for each test in milliseconds. + timeout: 60 * 1000, + use: { + baseURL: + process.env.MATRIX_PATHS !== 'true' + ? `https://trynow.${process.env.INGRESS_HOST}.nip.io` + : `https://${process.env.INGRESS_HOST}.nip.io/trynow`, + browserName: 'chromium', + permissions: ['clipboard-read'], + screenshot: 'only-on-failure', + ignoreHTTPSErrors: true + }, + preserveOutput: 'failures-only', + reporter: [['list']] +}; + +export default config; diff --git a/node/e2e-tests/package.json b/node/e2e-tests/package.json new file mode 100644 index 00000000..8db6f076 --- /dev/null +++ b/node/e2e-tests/package.json @@ -0,0 +1,19 @@ +{ + "name": "e2e-tests", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "tsc && npx playwright install chromium", + "lint": "eslint -c ../.eslintrc.js --ext .ts ./src", + "ui-tests": "npm run build && playwright test --config=./configs/playwright.config.ts" + }, + "dependencies": { + "@kubernetes/client-node": "^0.22.3", + "@playwright/test": "^1.41.2", + "@theia/playwright": "^1.34.0" + }, + "devDependencies": { + "@types/node": "^20.10.0", + "cross-env": "^7.0.3" + } +} diff --git a/node/e2e-tests/src/constats.ts b/node/e2e-tests/src/constats.ts new file mode 100644 index 00000000..f07c8612 --- /dev/null +++ b/node/e2e-tests/src/constats.ts @@ -0,0 +1,6 @@ +export const namespace = 'theiacloud'; +export const resourceGroup = 'theia.cloud'; +export const sessionVersion = 'v1beta8'; +export const sessionPlural = 'sessions'; +export const workspaceVersion = 'v1beta5'; +export const workspacePlural = 'workspaces'; diff --git a/node/e2e-tests/src/k8s.ts b/node/e2e-tests/src/k8s.ts new file mode 100644 index 00000000..20120eee --- /dev/null +++ b/node/e2e-tests/src/k8s.ts @@ -0,0 +1,45 @@ +import { CustomObjectsApi, KubeConfig } from '@kubernetes/client-node'; + +import { namespace, resourceGroup, sessionPlural, sessionVersion, workspacePlural, workspaceVersion } from './constats'; + +const kc = new KubeConfig(); +kc.loadFromDefault(); +export const k8sApi = kc.makeApiClient(CustomObjectsApi); + +export async function deleteAllSessions(): Promise { + const sessions: any = await k8sApi.listNamespacedCustomObject( + resourceGroup, + sessionVersion, + namespace, + sessionPlural + ); + + for (const resource of sessions.body.items) { + await k8sApi.deleteNamespacedCustomObject( + resourceGroup, + sessionVersion, + namespace, + sessionPlural, + resource.metadata.name + ); + } +} + +export async function deleteAllWorkspaces(): Promise { + const sessions: any = await k8sApi.listNamespacedCustomObject( + resourceGroup, + workspaceVersion, + namespace, + workspacePlural + ); + + for (const resource of sessions.body.items) { + await k8sApi.deleteNamespacedCustomObject( + resourceGroup, + workspaceVersion, + namespace, + workspacePlural, + resource.metadata.name + ); + } +} diff --git a/node/e2e-tests/src/tests/login.test.ts b/node/e2e-tests/src/tests/login.test.ts new file mode 100644 index 00000000..bb4feae8 --- /dev/null +++ b/node/e2e-tests/src/tests/login.test.ts @@ -0,0 +1,73 @@ +import { expect, test } from '@playwright/test'; + +import { deleteAllSessions, deleteAllWorkspaces } from '../k8s'; + +test.describe('Login', () => { + test.beforeEach(async () => { + deleteAllSessions(); + deleteAllWorkspaces(); + }); + + test('should work', async ({ page, baseURL }) => { + test.skip(process.env.MATRIX_KEYCLOAK !== 'true', 'Skipping test because keycloak not enabled'); + + expect(baseURL).toBeDefined(); + + /* Click on Login button */ + await page.goto(baseURL!); + const loginButton = await page.locator('.App__try-now-button'); + await expect(loginButton).toHaveText('Login'); + await loginButton.click(); + const signInHeading = await page.locator('#kc-page-title'); + await expect(signInHeading).toHaveText('Sign in to your account'); + + /* Enter user data */ + await page.fill('#username', 'foo'); + await page.fill('#password', 'foo'); + await page.click('#kc-login'); + await page.waitForLoadState('networkidle'); + const launchButton = await page.locator('.App__try-now-button'); + await expect(launchButton).toContainText('Launch Theia with Theia Extension Monitor'); + }); + + test('not required when already logged in', async ({ page, baseURL }) => { + test.skip(process.env.MATRIX_KEYCLOAK !== 'true', 'Skipping test because keycloak not enabled'); + + expect(baseURL).toBeDefined(); + + /* Click on Login button */ + await page.goto(baseURL!); + const loginButton = await page.locator('.App__try-now-button'); + await expect(loginButton).toHaveText('Login'); + await loginButton.click(); + const signInHeading = await page.locator('#kc-page-title'); + await expect(signInHeading).toHaveText('Sign in to your account'); + + /* Enter user data */ + await page.fill('#username', 'foo'); + await page.fill('#password', 'foo'); + await page.click('#kc-login'); + await page.waitForLoadState('networkidle'); + + /* reload page */ + await page.reload(); + await page.waitForLoadState('networkidle'); + + /* check button and email */ + const launchButton = await page.locator('.App__try-now-button'); + await expect(launchButton).toContainText('Launch Theia with Theia Extension Monitor'); + const email = await page.locator('#root .App .header p:nth-child(1)'); + await expect(email).toHaveText('foo@theia-cloud.io'); + }); + + test('not required when not using Keycloak', async ({ page, baseURL }) => { + test.skip(process.env.MATRIX_KEYCLOAK === 'true', 'Skipping test because keycloak is enabled'); + + expect(baseURL).toBeDefined(); + + /* Check no Login Button */ + await page.goto(baseURL!); + const launchButton = await page.locator('.App__try-now-button'); + await expect(launchButton).toContainText('Launch Theia with Theia Extension Monitor'); + }); +}); diff --git a/node/e2e-tests/src/tests/logout.test.ts b/node/e2e-tests/src/tests/logout.test.ts new file mode 100644 index 00000000..66c241d5 --- /dev/null +++ b/node/e2e-tests/src/tests/logout.test.ts @@ -0,0 +1,76 @@ +import { expect, test } from '@playwright/test'; + +import { deleteAllSessions, deleteAllWorkspaces } from '../k8s'; + +test.describe('Logout', () => { + test.beforeEach(async () => { + deleteAllSessions(); + deleteAllWorkspaces(); + }); + + test('should work', async ({ page, baseURL }) => { + test.skip(process.env.MATRIX_KEYCLOAK !== 'true', 'Skipping test because keycloak not enabled'); + + expect(baseURL).toBeDefined(); + + /* Click on Login button */ + await page.goto(baseURL!); + const loginButton = await page.locator('.App__try-now-button'); + await expect(loginButton).toHaveText('Login'); + await loginButton.click(); + const signInHeading = await page.locator('#kc-page-title'); + await expect(signInHeading).toHaveText('Sign in to your account'); + + /* Enter user data */ + await page.fill('#username', 'foo'); + await page.fill('#password', 'foo'); + await page.click('#kc-login'); + await page.waitForLoadState('networkidle'); + const launchButton = await page.locator('.App__try-now-button'); + await expect(launchButton).toContainText('Launch Theia with Theia Extension Monitor'); + + /* Log out */ + const logoutImageTitle = await page.locator('#root .App .header p:nth-child(2) svg > title'); + await expect(logoutImageTitle).toHaveText('logout'); + await page.click('#root .App .header p:nth-child(2) a'); + await page.waitForLoadState('networkidle'); + const loginButtonAgain = await page.locator('.App__try-now-button'); + await expect(loginButtonAgain).toHaveText('Login'); + }); + + test('not available when not logged in,', async ({ page, baseURL }) => { + test.skip(process.env.MATRIX_KEYCLOAK !== 'true', 'Skipping test because keycloak not enabled'); + + expect(baseURL).toBeDefined(); + + /* Check that logout ui is not there but login ui*/ + // Assert that the logout UI elements are not present + await page.goto(baseURL!); + const logoutImageTitle = page.locator('#root .App .header p:nth-child(2) svg > title'); + await expect(logoutImageTitle).toHaveCount(0); + + const logoutLink = page.locator('#root .App .header p:nth-child(2) a'); + await expect(logoutLink).toHaveCount(0); + + const loginButton = await page.locator('.App__try-now-button'); + await expect(loginButton).toHaveText('Login'); + }); + + test('not available when not using Keycloak', async ({ page, baseURL }) => { + test.skip(process.env.MATRIX_KEYCLOAK === 'true', 'Skipping test because keycloak is enabled'); + + expect(baseURL).toBeDefined(); + + /* Check that logout ui is not there but login ui*/ + // Assert that the logout UI elements are not present + await page.goto(baseURL!); + const logoutImageTitle = page.locator('#root .App .header p:nth-child(2) svg > title'); + await expect(logoutImageTitle).toHaveCount(0); + + const logoutLink = page.locator('#root .App .header p:nth-child(2) a'); + await expect(logoutLink).toHaveCount(0); + + const loginButton = await page.locator('.App__try-now-button'); + await expect(loginButton).toContainText('Launch Theia with Theia Extension Monitor'); + }); +}); diff --git a/node/e2e-tests/src/tests/start.test.ts b/node/e2e-tests/src/tests/start.test.ts new file mode 100644 index 00000000..cc65c009 --- /dev/null +++ b/node/e2e-tests/src/tests/start.test.ts @@ -0,0 +1,60 @@ +import { expect, test } from '@playwright/test'; + +import { namespace, resourceGroup, sessionPlural, sessionVersion } from '../constats'; +import { deleteAllSessions, deleteAllWorkspaces, k8sApi } from '../k8s'; + +test.describe('Start Session', () => { + test.beforeEach(async () => { + deleteAllSessions(); + deleteAllWorkspaces(); + }); + + test('should work', async ({ page, baseURL }) => { + expect(baseURL).toBeDefined(); + await page.goto(baseURL!); + + if (process.env.MATRIX_KEYCLOAK === 'true') { + /* Click on Login button */ + const loginButton = await page.locator('.App__try-now-button'); + await expect(loginButton).toHaveText('Login'); + await loginButton.click(); + const signInHeading = await page.locator('#kc-page-title'); + await expect(signInHeading).toHaveText('Sign in to your account'); + + /* Enter user data */ + await page.fill('#username', 'foo'); + await page.fill('#password', 'foo'); + await page.click('#kc-login'); + await page.waitForLoadState('networkidle'); + } + + await page.click('.App__try-now-button'); + + /* Check loading animation */ + const loadingAnimation = page.locator('.Loading__description'); + await expect(loadingAnimation).toBeVisible(); + + /* wait for loading to be done */ + await expect(loadingAnimation).toBeHidden({ timeout: 180000 }); + + /* check redirect url */ + const browserUrl = page.url(); + expect(browserUrl).toContain(baseURL!.replace('trynow', 'instances')); + + /* check created session */ + const resources: any = await k8sApi.listNamespacedCustomObject( + resourceGroup, + sessionVersion, + namespace, + sessionPlural + ); + expect(resources.body.items).toHaveLength(1); + + const sessionUrl = resources.body.items[0].status.url; + expect(sessionUrl).toBeDefined(); + const normalizedBrowserUrl = new URL(browserUrl); + const normalizedSessionUrl = new URL(sessionUrl.startsWith('http') ? sessionUrl : `https://${sessionUrl}`); + expect(normalizedBrowserUrl.hostname).toBe(normalizedSessionUrl.hostname); + expect(normalizedBrowserUrl.pathname).toBe(normalizedSessionUrl.pathname); + }); +}); diff --git a/node/e2e-tests/tsconfig.json b/node/e2e-tests/tsconfig.json new file mode 100644 index 00000000..cec76c33 --- /dev/null +++ b/node/e2e-tests/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib" + } +} diff --git a/node/package-lock.json b/node/package-lock.json index c1167fba..92543ccb 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "workspaces": [ "common", + "e2e-tests", "landing-page", "monitor-theia", "testing-page" @@ -43,6 +44,18 @@ "uuid": "^8.3.2" } }, + "e2e-tests": { + "version": "0.1.0", + "dependencies": { + "@kubernetes/client-node": "^0.22.3", + "@playwright/test": "^1.41.2", + "@theia/playwright": "^1.34.0" + }, + "devDependencies": { + "@types/node": "^20.10.0", + "cross-env": "^7.0.3" + } + }, "landing-page": { "version": "0.1.0", "license": "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0", @@ -3283,6 +3296,17 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3761,6 +3785,48 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@kubernetes/client-node": { + "version": "0.22.3", + "resolved": "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.22.3.tgz", + "integrity": "sha512-dG8uah3+HDJLpJEESshLRZlAZ4PgDeV9mZXT0u1g7oy4KMRzdZ7n5g0JEIlL6QhK51/2ztcIqURAnjfjJt6Z+g==", + "dependencies": { + "byline": "^5.0.0", + "isomorphic-ws": "^5.0.0", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^10.2.0", + "request": "^2.88.0", + "rfc4648": "^1.3.0", + "stream-buffers": "^3.0.2", + "tar": "^7.0.0", + "tslib": "^2.4.1", + "ws": "^8.18.0" + }, + "optionalDependencies": { + "openid-client": "^6.1.3" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -4373,6 +4439,20 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", + "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", + "dependencies": { + "playwright": "1.47.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", @@ -5544,6 +5624,48 @@ "tslib": "^2.6.2" } }, + "node_modules/@theia/playwright": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@theia/playwright/-/playwright-1.56.0.tgz", + "integrity": "sha512-qQbeAggpMXHgei3peQym8BRP5guzrdB6igrQiSOVWcEXtVmR+kKYLT23TY7cC+hrbFHeSqgKN9m/FjzSSoOmEQ==", + "dependencies": { + "@playwright/test": "^1.47.0", + "fs-extra": "^9.0.8" + } + }, + "node_modules/@theia/playwright/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@theia/playwright/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@theia/playwright/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/@theia/plugin": { "version": "1.56.0", "resolved": "https://registry.npmjs.org/@theia/plugin/-/plugin-1.56.0.tgz", @@ -6291,10 +6413,9 @@ } }, "node_modules/@types/node": { - "version": "20.16.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz", - "integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==", - "license": "MIT", + "version": "20.16.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.10.tgz", + "integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==", "dependencies": { "undici-types": "~6.19.2" } @@ -7707,6 +7828,22 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "license": "MIT" }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -7796,6 +7933,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==" + }, "node_modules/axe-core": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", @@ -8123,6 +8273,14 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "license": "MIT" }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/bfj": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", @@ -8410,6 +8568,14 @@ "node": ">=10.16.0" } }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -8515,6 +8681,11 @@ "node": ">=4" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9089,6 +9260,24 @@ "node": ">=10" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -9499,6 +9688,17 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "license": "BSD-2-Clause" }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -10226,12 +10426,30 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "license": "MIT" }, + "node_modules/e2e-tests": { + "resolved": "e2e-tests", + "link": true + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -11952,6 +12170,19 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -12375,6 +12606,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", @@ -12779,6 +13018,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -12981,6 +13228,27 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "license": "MIT" }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/harmony-reflect": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", @@ -13321,6 +13589,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, "node_modules/http-status-codes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.4.0.tgz", @@ -14079,6 +14361,19 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -15390,6 +15685,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "5.9.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", + "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-sha256": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", @@ -15526,6 +15830,14 @@ } } }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -15568,6 +15880,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "license": "MIT" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -15606,6 +15923,23 @@ "underscore": "1.12.1" } }, + "node_modules/jsonpath-plus": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.2.0.tgz", + "integrity": "sha512-T9V+8iNYKFL2n2rF+w02LBOT2JjDnTjioaNFrxRy0Bv1y/hNsqR/EBK7Ojy2ythRHwmz2cRIls+9JitQGZC/sw==", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/jsonpath/node_modules/esprima": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", @@ -15627,6 +15961,20 @@ "node": ">=0.10.0" } }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -16304,6 +16652,65 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/minizlib/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minizlib/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minizlib/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -16696,6 +17103,23 @@ "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==", "license": "MIT" }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/oauth4webapi": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.1.3.tgz", + "integrity": "sha512-dik5wEMdFL5p3JlijYvM7wMNCgaPhblLIDCZtdXcaZp5wgu5Iwmsu7lMzgFhIDTi5d0BJo03LVoOoFQvXMeOeQ==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -16921,6 +17345,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openid-client": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.1.6.tgz", + "integrity": "sha512-ZvMKXUKKQ7AA2ZykmoA8C/1DovRY2Fzdp860xcZQIAZoWS7OGZcO9Z2FJ53jAAO505VTD7M2mgW0fdiJqaf8CQ==", + "optional": true, + "dependencies": { + "jose": "^5.9.6", + "oauth4webapi": "^3.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -17431,6 +17868,47 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", + "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", + "dependencies": { + "playwright-core": "1.47.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", + "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -19557,6 +20035,79 @@ "strip-ansi": "^6.0.1" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -19741,6 +20292,11 @@ "node": ">=0.10.0" } }, + "node_modules/rfc4648": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.3.tgz", + "integrity": "sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ==" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -20644,6 +21200,35 @@ "license": "BSD-3-Clause", "peer": true }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -20808,6 +21393,14 @@ "node": ">= 0.4" } }, + "node_modules/stream-buffers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz", + "integrity": "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==", + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", @@ -21536,6 +22129,22 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/tar-fs": { "version": "1.16.3", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", @@ -21577,6 +22186,36 @@ "node": ">= 0.8.0" } }, + "node_modules/tar/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "engines": { + "node": ">=18" + } + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -22103,7 +22742,6 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "license": "Apache-2.0", - "peer": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -22111,6 +22749,11 @@ "node": "*" } }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -22535,6 +23178,24 @@ "node": ">= 0.8" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, "node_modules/vhost": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/vhost/-/vhost-3.0.2.tgz", diff --git a/node/package.json b/node/package.json index 61d063e7..9ac231f8 100644 --- a/node/package.json +++ b/node/package.json @@ -31,6 +31,7 @@ }, "workspaces": [ "common", + "e2e-tests", "landing-page", "monitor-theia", "testing-page" diff --git a/node/tsconfig.json b/node/tsconfig.json index a8ad2644..d5b07273 100644 --- a/node/tsconfig.json +++ b/node/tsconfig.json @@ -7,7 +7,8 @@ "common/src", "landing-page/src", "monitor/src", - "monitor-theia/src" + "monitor-theia/src", + "e2e-tests/src" ], "exclude": ["node_modules"] } diff --git a/terraform/ci-configurations/.terraform.lock.hcl b/terraform/ci-configurations/.terraform.lock.hcl new file mode 100644 index 00000000..7df1a1bb --- /dev/null +++ b/terraform/ci-configurations/.terraform.lock.hcl @@ -0,0 +1,83 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/gavinbunney/kubectl" { + version = "1.14.0" + constraints = ">= 1.14.0" + hashes = [ + "h1:gLFn+RvP37sVzp9qnFCwngRjjFV649r6apjxvJ1E/SE=", + "zh:0350f3122ff711984bbc36f6093c1fe19043173fad5a904bce27f86afe3cc858", + "zh:07ca36c7aa7533e8325b38232c77c04d6ef1081cb0bac9d56e8ccd51f12f2030", + "zh:0c351afd91d9e994a71fe64bbd1662d0024006b3493bb61d46c23ea3e42a7cf5", + "zh:39f1a0aa1d589a7e815b62b5aa11041040903b061672c4cfc7de38622866cbc4", + "zh:428d3a321043b78e23c91a8d641f2d08d6b97f74c195c654f04d2c455e017de5", + "zh:4baf5b1de2dfe9968cc0f57fd4be5a741deb5b34ee0989519267697af5f3eee5", + "zh:6131a927f9dffa014ab5ca5364ac965fe9b19830d2bbf916a5b2865b956fdfcf", + "zh:c62e0c9fd052cbf68c5c2612af4f6408c61c7e37b615dc347918d2442dd05e93", + "zh:f0beffd7ce78f49ead612e4b1aefb7cb6a461d040428f514f4f9cc4e5698ac65", + ] +} + +provider "registry.terraform.io/hashicorp/helm" { + version = "2.9.0" + constraints = ">= 2.9.0" + hashes = [ + "h1:D5BLFN82WndhQZQleXE5rO0hUDnlyqb60XeUJKDhuo4=", + "zh:1471cb45908b426104687c962007b2980cfde294fa3530fabc4798ce9fb6c20c", + "zh:1572e9cec20591ec08ece797b3630802be816a5adde36ca91a93359f2430b130", + "zh:1b10ae03cf5ab1ae21ffaac2251de99797294ae4242b156b3b0beebbdbcb7e0f", + "zh:3bd043b68de967d8d0b549d3f71485193d81167d5656f5507d743dedfe60e352", + "zh:538911921c729185900176cc22eb8edcb822bc8d22b9ebb48103a1d9bb53cc38", + "zh:69a6a2d40c0463662c3fb1621e37a3ee65024ea4479adf4d5f7f19fb0dea48c2", + "zh:94b58daa0c351a49d01f6d8f1caae46c95c2d6c3f29753e2b9ea3e3c0e7c9ab4", + "zh:9d0543331a4a32241e1ab5457f30b41df745acb235a0391205c725a5311e4809", + "zh:a6789306524ca121512a95e873e3949b4175114a6c5db32bed2df2551a79368f", + "zh:d146b94cd9502cca7f2044797a328d71c7ec2a98e2d138270d8a28c872f04289", + "zh:d14ccd14511f0446eacf43a9243f22de7c1427ceb059cf67d7bf9803be2cb15d", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.19.0" + hashes = [ + "h1:soxnBBEH2yLFS0xyALi7J8KQ4u7eQzEYIGvkDyyYCcU=", + "zh:028d346460de2d1d19b4c863dfc36be51c7bcd97d372b54a3a946bcb19f3f613", + "zh:391d0b38c455437d0a2ab1beb6ce6e1230aa4160bbae11c58b2810b258b44280", + "zh:40ea742f91b67f66e71d7091cfd40cc604528c4947651924bd6d8bd8d9793708", + "zh:48a99d341c8ba3cadaafa7cb99c0f11999f5e23f5cfb0f8469b4e352d9116e74", + "zh:4a5ade940eff267cbf7dcd52c1a7ac3999e7cc24996a409bd8b37bdb48a97f02", + "zh:5063742016a8249a4be057b9cc0ef24a684ec76d0ae5463d4b07e9b2d21e047e", + "zh:5d36b3a5662f840a6788f5e2a19d02139e87318feb3c5d82c7d076be1366fec4", + "zh:75edd9960cb30e54ef7de1b7df2761a274f17d4d41f54e72f86b43f41af3eb6d", + "zh:b85cadef3e6f25f1a10a617472bf5e8449decd61626733a1bc723de5edc08f64", + "zh:dc565b17b4ea6dde6bd1b92bc37e5e850fcbf9400540eec00ad3d9552a76ac2e", + "zh:deb665cc2123f2701aa3d653987b2ca35fb035a08a76a2382efb215c209f19a5", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/mrparkers/keycloak" { + version = "4.2.0" + constraints = ">= 4.2.0" + hashes = [ + "h1:0Z10wRvT3mrlm10AzTQIQy0h1QN6u8qAnePJKXxmvHw=", + "zh:0158a265b74b3e800864a78e24a822278aca250e97d5b4ac7ceb3fba9ff8d468", + "zh:1067bda8ec35e5f77de9fa6296fbb17d4f931692d624c50a6e97486982185422", + "zh:29ca6d1f647d82cf64641946b128ed90e2d3702fb7e79a432a692766d9bf66cc", + "zh:2d1fd51770fe5bc8b9b9188a1cffe34c108ca53678f77ccd369b7f0f8ea7ae28", + "zh:43e8dc93845c01b2b2b27fb245fd2de2daf391d239285201ab0ed294e7a8d104", + "zh:49f72033a1b48312c5c6e865043745e98ec11f564c80da780d5cfd02271a9dff", + "zh:4b18a3438d30553b9300d347452f81699aff2cbe8523116346da7bf51e7cc505", + "zh:613a73f6d3c196ebbdfcd451939d441afffb64bc514c94d91849231018f33df7", + "zh:6e0c20a937f1911820580fd2c3d933d19b38bdd8377ad1de5464c0e653f04f02", + "zh:87edc21042b62c7536e198354bc06843d4e10e83288db2e35a3829f719c44319", + "zh:aa9a5535c6e7e7b572bcba8a0eeaf5c0ed8cb5844eabc1aa8a96aab1cd685060", + "zh:b0d6e8f454b16d62bedd2122c4526c8f0c4c22ca78188b4e2eeaf78f3b7ec95a", + "zh:b35ab93aeffcca42574d87e77f43a6f394a64d41564d650f43f631d988b1d404", + "zh:b9f2d50549001bb801f1f99e4926e52c5de7e858c6758580a9845ca815a2606d", + "zh:da1ce72055cb7602a07bf2ecb0901292449b3b1261c8282236ac74d780c26659", + "zh:ebfe41b6b6a443482e66d08d544030e9d2c689290ff664d88839a9b80cee859c", + "zh:fb32217f92008e52ce57d6d9e5ffc8ec135a262dcac8fe47e10771e42b878178", + ] +} diff --git a/terraform/ci-configurations/e2e_tests.tf b/terraform/ci-configurations/e2e_tests.tf new file mode 100644 index 00000000..58c2cad4 --- /dev/null +++ b/terraform/ci-configurations/e2e_tests.tf @@ -0,0 +1,230 @@ +variable "ingress_ip" { + description = "The IP of the running Minikube Cluster" + type = string +} + +variable "use_paths" { + description = "Whether to use Theia Cloud with paths or subdomains" + type = bool +} + +variable "use_ephemeral_storage" { + description = "Whether to use ephemeral storage" + type = bool +} + +variable "enable_keycloak" { + description = "Whether to enable keycloak" + type = bool +} + +provider "kubernetes" { + config_path = "~/.kube/config" +} + +provider "helm" { + kubernetes { + config_path = "~/.kube/config" + } +} + +resource "kubernetes_persistent_volume" "minikube" { + metadata { + name = "minikube-volume" + } + spec { + storage_class_name = "manual" + capacity = { + storage = "16Gi" + } + access_modes = ["ReadWriteOnce"] + persistent_volume_source { + host_path { + path = "/data/theiacloud" + } + } + } +} + +module "helm" { + source = "../modules/helm" + + depends_on = [kubernetes_persistent_volume.minikube] + + install_ingress_controller = false + install_theia_cloud_base = false + install_theia_cloud_crds = false + install_theia_cloud = false + install_selfsigned_issuer = true + cert_manager_issuer_email = "jdoe@theia-cloud.io" + cert_manager_cluster_issuer = "keycloak-selfsigned-issuer" + cert_manager_common_name = "${var.ingress_ip}.nip.io" + hostname = "${var.ingress_ip}.nip.io" + service_type = "ClusterIP" + postgresql_storageClass = "manual" + postgresql_volumePermissions = true + keycloak_admin_password = "admin" + postgresql_enabled = true + postgres_postgres_password = "admin" + postgres_password = "admin" + loadBalancerIP = "" + cloudProvider = "MINIKUBE" +} + +provider "keycloak" { + client_id = "admin-cli" + username = "admin" + password = "admin" + url = "https://${var.ingress_ip}.nip.io/keycloak" + tls_insecure_skip_verify = true # only for minikube self signed + initial_login = false + client_timeout = 60 +} + +module "keycloak" { + source = "../modules/keycloak" + + depends_on = [module.helm] + + hostname = "${var.ingress_ip}.nip.io" + keycloak_test_user_foo_password = "foo" + keycloak_test_user_bar_password = "bar" + valid_redirect_uri = "*" +} + +resource "helm_release" "theia-cloud-crds" { + depends_on = [module.keycloak] + + name = "theia-cloud-crds" + chart = "../../../theia-cloud-helm/charts/theia-cloud-crds" + namespace = "theiacloud" + create_namespace = true + + set { + name = "conversion.image" + value = "theiacloud/theia-cloud-conversion-webhook:minikube-ci-e2e" + } +} + +resource "helm_release" "theia-cloud-base" { + depends_on = [module.keycloak] + + name = "theia-cloud-base" + chart = "../../../theia-cloud-helm/charts/theia-cloud-base" + namespace = "theiacloud" + create_namespace = true + + set { + name = "issuer.email" + value = "jdoe@theia-cloud.io" + } +} + +resource "helm_release" "theia-cloud" { + depends_on = [helm_release.theia-cloud-crds, helm_release.theia-cloud-base] + + name = "theia-cloud" + chart = "../../../theia-cloud-helm/charts/theia-cloud" + namespace = "theiacloud" + create_namespace = true + + values = [ + "${file("${path.module}/valuesE2ECI.yaml")}" + ] + + set { + name = "hosts.usePaths" + value = var.use_paths + } + + set { + name = "hosts.configuration.baseHost" + value = "${var.ingress_ip}.nip.io" + } + + set { + name = "landingPage.ephemeralStorage" + value = var.use_ephemeral_storage + } + + set { + name = "keycloak.enable" + value = var.enable_keycloak + } + + set { + name = "keycloak.authUrl" + value = "https://${var.ingress_ip}.nip.io/keycloak/" + } +} + +resource "kubectl_manifest" "theia-cloud-monitor-theia" { + depends_on = [helm_release.theia-cloud] + yaml_body = <<-EOF + apiVersion: theia.cloud/v1beta10 + kind: AppDefinition + metadata: + name: theia-cloud-monitor-theia + namespace: theiacloud + spec: + name: theia-cloud-monitor-theia + image: theiacloud/theia-cloud-activity-demo-theia:minikube-ci-e2e + imagePullPolicy: IfNotPresent + uid: 101 + port: 3000 + ingressname: theia-cloud-demo-ws-ingress + ingressHostnamePrefixes: [] + minInstances: 0 + maxInstances: 10 + timeout: 6 + requestsMemory: 1000M + requestsCpu: 100m + limitsMemory: 1200M + limitsCpu: "2" + downlinkLimit: 30000 + uplinkLimit: 30000 + mountPath: /home/project/persisted + monitor: + port: 3000 + activityTracker: + timeoutAfter: 4 + notifyAfter: 2 + EOF +} + +resource "kubectl_manifest" "theia-cloud-monitor-vscode" { + depends_on = [helm_release.theia-cloud] + yaml_body = <<-EOF + apiVersion: theia.cloud/v1beta10 + kind: AppDefinition + metadata: + name: theia-cloud-monitor-vscode + namespace: theiacloud + spec: + name: theia-cloud-monitor-vscode + image: theiacloud/theia-cloud-activity-demo:minikube-ci-e2e + imagePullPolicy: IfNotPresent + uid: 101 + port: 3000 + ingressname: theia-cloud-demo-ws-ingress + ingressHostnamePrefixes: [] + minInstances: 0 + maxInstances: 10 + timeout: 6 + requestsMemory: 1000M + requestsCpu: 100m + limitsMemory: 1200M + limitsCpu: "2" + downlinkLimit: 30000 + uplinkLimit: 30000 + mountPath: /home/project/persisted + monitor: + port: 8081 + activityTracker: + timeoutAfter: 4 + notifyAfter: 2 + EOF +} + + + diff --git a/terraform/ci-configurations/outputs.tf b/terraform/ci-configurations/outputs.tf new file mode 100644 index 00000000..a3034db9 --- /dev/null +++ b/terraform/ci-configurations/outputs.tf @@ -0,0 +1,4 @@ +output "ingress_ip" { + description = "The IP of the running Minikube Cluster" + value = var.ingress_ip +} diff --git a/terraform/ci-configurations/run-locally.md b/terraform/ci-configurations/run-locally.md new file mode 100644 index 00000000..776ac78d --- /dev/null +++ b/terraform/ci-configurations/run-locally.md @@ -0,0 +1,60 @@ +# Run and Develop CI E2E Tests Locally + +See the `e2e-tests.yml` Workflow file for all steps. Below will just be a short summary with adaptions that need to be done locally. + +Start Minikube, enable the required plugins, and patch the ingress. + +```sh +minikube start --memory=8192 --cpus=4 --driver=virtualbox + +minikube addons enable dashboard +minikube addons enable default-storageclass +minikube addons enable ingress +minikube addons enable metrics-server + +kubectl -n ingress-nginx patch cm ingress-nginx-controller --patch '{"data":{"allow-snippet-annotations":"true"}}' +kubectl -n ingress-nginx delete pod -l app.kubernetes.io/name=ingress-nginx +``` + +Adapt your environment so that all docker images are built in minikube. Build all Theia Cloud docker images + Demos with tag `minikube-ci-e2e`, e.g. `theiacloud/theia-cloud-service:minikube-ci-e2e`. + +```sh +eval $(minikube docker-env) + +docker build --no-cache -t theiacloud/theia-cloud-service:minikube-ci-e2e -f dockerfiles/service/Dockerfile . +docker build --no-cache -t theiacloud/theia-cloud-operator:minikube-ci-e2e -f dockerfiles/operator/Dockerfile . +docker build --no-cache -t theiacloud/theia-cloud-landing-page:minikube-ci-e2e -f dockerfiles/landing-page/Dockerfile . +docker build --no-cache -t theiacloud/theia-cloud-wondershaper:minikube-ci-e2e -f dockerfiles/wondershaper/Dockerfile . +docker build --no-cache -t theiacloud/theia-cloud-conversion-webhook:minikube-ci-e2e -f dockerfiles/conversion-webhook/Dockerfile . +docker build --no-cache -t theiacloud/theia-cloud-demo:latest -f demo/dockerfiles/demo-theia-docker/Dockerfile demo/dockerfiles/demo-theia-docker/. +docker tag theiacloud/theia-cloud-demo:latest theiacloud/theia-cloud-demo:minikube-ci-e2e +docker build --no-cache -t theiacloud/theia-cloud-activity-demo:minikube-ci-e2e -f demo/dockerfiles/demo-theia-monitor-vscode/Dockerfile demo/dockerfiles/demo-theia-monitor-vscode/. +docker build --no-cache -t theiacloud/theia-cloud-activity-demo-theia:minikube-ci-e2e -f demo/dockerfiles/demo-theia-monitor-theia/Dockerfile . +``` + +Get the IP of Minikube's ingress controller + +```sh +export INGRESS_HOST=$(minikube ip) +export MATRIX_PATHS=false +export MATRIX_EPHEMERAL=true +export MATRIX_KEYCLOAK=true +``` + +Run Terraform + +```sh +terraform init +terraform apply \ + -var="ingress_ip=$INGRESS_HOST" \ + -var="use_paths=$MATRIX_PATHS" \ + -var="use_ephemeral_storage=$MATRIX_EPHEMERAL" \ + -var="enable_keycloak=$MATRIX_KEYCLOAK" \ + -auto-approve +``` + +Run Tests + +```sh +npm run ui-tests -w e2e-tests +``` diff --git a/terraform/ci-configurations/valuesE2ECI.yaml b/terraform/ci-configurations/valuesE2ECI.yaml new file mode 100644 index 00000000..1e7e66c4 --- /dev/null +++ b/terraform/ci-configurations/valuesE2ECI.yaml @@ -0,0 +1,58 @@ +imagePullPolicy: IfNotPresent + +app: + name: "Theia with Theia Extension Monitor" + +issuer: + email: jdoe@theia-cloud.io + +demoApplication: + install: false + +hosts: + usePaths: false + allWildcardInstances: ["*.webview."] + configuration: + baseHost: 192.168.39.173.nip.io + +landingPage: + image: theiacloud/theia-cloud-landing-page:minikube-ci-e2e + appDefinition: "theia-cloud-monitor-theia" + ephemeralStorage: true + additionalApps: + theia-cloud-monitor-vscode: + label: "Theia with VSCode Monitor" + +keycloak: + enable: false + authUrl: "https://keycloak.url/auth/" + +operator: + image: theiacloud/theia-cloud-operator:minikube-ci-e2e + cloudProvider: "MINIKUBE" + bandwidthLimiter: "WONDERSHAPER" + wondershaperImage: theiacloud/theia-cloud-wondershaper:minikube-ci-e2e + sessionsPerUser: "1" + storageClassName: "default" + requestedStorage: "250Mi" + replicas: 2 + +service: + image: theiacloud/theia-cloud-service:minikube-ci-e2e + +ingress: + certManagerAnnotations: true + clusterIssuer: theia-cloud-selfsigned-issuer + theiaCloudCommonName: true + tls: true + addTLSSecretName: true + +monitor: + enable: true + activityTracker: + enable: true + interval: 1 + +preloading: + enable: true + images: [] diff --git a/terraform/ci-configurations/versions.tf b/terraform/ci-configurations/versions.tf new file mode 100644 index 00000000..7dff4f5a --- /dev/null +++ b/terraform/ci-configurations/versions.tf @@ -0,0 +1,22 @@ +terraform { + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.18.1" + } + helm = { + source = "hashicorp/helm" + version = ">= 2.9.0" + } + keycloak = { + source = "mrparkers/keycloak" + version = ">= 4.2.0" + } + kubectl = { + source = "gavinbunney/kubectl" + version = ">= 1.14.0" + } + } + + required_version = ">= 1.4.0" +}