diff --git a/.github/actions/run-cypress-tests/action.yaml b/.github/actions/run-cypress-tests/action.yaml index d4711f0fd..2f1e223f4 100644 --- a/.github/actions/run-cypress-tests/action.yaml +++ b/.github/actions/run-cypress-tests/action.yaml @@ -1,9 +1,9 @@ -name: 'Runs the cypress test suite' -description: 'Re-usable workflow to run cypress tests against a cluster with or without security' +name: "Runs the cypress test suite" +description: "Re-usable workflow to run cypress tests against a cluster with or without security" inputs: with-security: - description: 'Whether security should be installed on the cluster the tests are run with' + description: "Whether security should be installed on the cluster the tests are run with" required: true runs: @@ -14,52 +14,69 @@ runs: with: # TODO: Parse this from index management plugin java-version: 21 - distribution: 'temurin' + distribution: temurin + - name: Checkout index management uses: actions/checkout@v2 with: path: index-management repository: opensearch-project/index-management - ref: 'main' - - name: Run opensearch with plugin + ref: "main" + + - name: Run opensearch with plugin (no security) shell: bash if: ${{ inputs.with-security == 'false' }} run: | cd index-management ./gradlew run & - sleep 300 - # timeout 300 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9200)" != "200" ]]; do sleep 5; done' - - name: Run opensearch with plugin + # Wait for 200 OK from OpenSearch (up to 10 min) + timeout 600 bash -lc ' + until [[ "$(curl -s -o /dev/null -w "%{http_code}" http://localhost:9200)" == "200" ]]; do + echo "Waiting for OpenSearch..." + sleep 5 + done + ' + + - name: Run opensearch with plugin (security) shell: bash if: ${{ inputs.with-security == 'true' }} run: | cd index-management ./gradlew run -Dsecurity=true -Dhttps=true & - sleep 300 - # timeout 300 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9200)" != "200" ]]; do sleep 5; done' + # Wait for 200 or 401 (auth) on HTTPS (up to 10 min) + timeout 600 bash -lc ' + until code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:9200); [[ "$code" == "200" || "$code" == "401" ]]; do + echo "Waiting for OpenSearch (secure)..." + sleep 5 + done + ' + - name: Checkout Index Management Dashboards plugin uses: actions/checkout@v2 with: path: index-management-dashboards-plugin + - name: Checkout Security Dashboards plugin uses: actions/checkout@v2 with: repository: opensearch-project/security-dashboards-plugin path: security-dashboards-plugin ref: ${{ env.OPENSEARCH_DASHBOARDS_VERSION }} + - name: Checkout OpenSearch-Dashboards uses: actions/checkout@v2 with: repository: opensearch-project/OpenSearch-Dashboards path: OpenSearch-Dashboards ref: ${{ env.OPENSEARCH_DASHBOARDS_VERSION }} + - name: Setup Node uses: actions/setup-node@v3 with: - node-version-file: './OpenSearch-Dashboards/.nvmrc' - registry-url: 'https://registry.npmjs.org' + node-version-file: "./OpenSearch-Dashboards/.nvmrc" + registry-url: "https://registry.npmjs.org" + - name: Install Yarn - # Need to use bash to avoid having a windows/linux specific step shell: bash run: | YARN_VERSION=$(node -p "require('./OpenSearch-Dashboards/package.json').engines.yarn") @@ -69,7 +86,8 @@ runs: shell: bash - run: yarn -v shell: bash - - name: Configure OpenSearch Dashboards for cypress + + - name: Configure OpenSearch Dashboards for cypress (secure) shell: bash if: ${{ inputs.with-security == 'true' }} run: | @@ -85,67 +103,92 @@ runs: opensearch_security.readonly_mode.roles: ["kibana_read_only"] opensearch_security.cookie.secure: false EOT - - name: Print Dashboards Config + + - name: Print Dashboards Config (secure) shell: bash if: ${{ inputs.with-security == 'true' }} - run: | - cat ./OpenSearch-Dashboards/config/opensearch_dashboards.yml - - name: Bootstrap plugin/OpenSearch-Dashboards + run: cat ./OpenSearch-Dashboards/config/opensearch_dashboards.yml + + - name: Bootstrap plugin/OpenSearch-Dashboards (no security) shell: bash if: ${{ inputs.with-security == 'false' }} run: | mkdir -p OpenSearch-Dashboards/plugins mv index-management-dashboards-plugin OpenSearch-Dashboards/plugins - - name: Bootstrap plugin/OpenSearch-Dashboards + + - name: Bootstrap plugin/OpenSearch-Dashboards (secure) shell: bash if: ${{ inputs.with-security == 'true' }} run: | mkdir -p OpenSearch-Dashboards/plugins mv index-management-dashboards-plugin OpenSearch-Dashboards/plugins mv security-dashboards-plugin OpenSearch-Dashboards/plugins + - name: Bootstrap the OpenSearch Dashboard uses: nick-fields/retry@v2 with: timeout_minutes: 20 max_attempts: 2 command: yarn --cwd OpenSearch-Dashboards osd bootstrap --oss --single-version=loose + - name: Compile OpenSearch Dashboards shell: bash run: | cd OpenSearch-Dashboards node scripts/build_opensearch_dashboards_platform_plugins --no-examples --workers=10 --verbose + - name: Run OpenSearch-Dashboards server shell: bash run: | cd OpenSearch-Dashboards + # Start server yarn start --no-base-path --no-watch --server.host="0.0.0.0" & - sleep 30 - # in main branch, OSD server requires more time to bundle and bootstrap - # timeout 300 bash -c 'while [[ "$(curl -s localhost:5601/api/status | jq -r '.status.overall.state')" != "green" ]]; do sleep 5; done' - # for now just chrome, use matrix to do all browsers later - - name: Cypress tests - uses: cypress-io/github-action@v5 + # Wait until OSD is green (up to 10 min) + timeout 600 bash -lc ' + until curl -sf http://localhost:5601/api/status | jq -e ".status.overall.state==\"green\"" >/dev/null; do + echo "Waiting for OpenSearch Dashboards to be green..." + sleep 5 + done + ' + + # ---- Cypress runs ---- + - name: Cypress tests (no security) + uses: cypress-io/github-action@v2 if: ${{ inputs.with-security == 'false' }} with: working-directory: OpenSearch-Dashboards/plugins/index-management-dashboards-plugin - command: yarn run cypress run --config-file cypress.config.js - wait-on: 'http://localhost:5601' + command: yarn run cypress run --browser chrome --config-file cypress.config.js + wait-on: "http://localhost:5601" + wait-on-timeout: 600000 # 10 minutes browser: chrome - - name: Cypress tests - uses: cypress-io/github-action@v5 + env: + # IMPORTANT: do not leak NODE_OPTIONS to Cypress/Electron + NODE_OPTIONS: "" + # optional: keeps CI logs quieter + CI: 1 + TERM: xterm + + - name: Cypress tests (secure) + uses: cypress-io/github-action@v2 if: ${{ inputs.with-security == 'true' }} with: working-directory: OpenSearch-Dashboards/plugins/index-management-dashboards-plugin - command: yarn run cypress run --config-file cypress.config.js --env SECURITY_ENABLED=true,openSearchUrl=https://localhost:9200,WAIT_FOR_LOADER_BUFFER_MS=500 - wait-on: 'http://localhost:5601' + command: yarn run cypress run --browser chrome --config-file cypress.config.js --env SECURITY_ENABLED=true,openSearchUrl=https://localhost:9200,WAIT_FOR_LOADER_BUFFER_MS=500 + wait-on: "http://localhost:5601" + wait-on-timeout: 600000 # 10 minutes browser: chrome - # Screenshots are only captured on failure, will change this once we do visual regression tests + env: + NODE_OPTIONS: "" # unset for Cypress/Electron + CI: 1 + TERM: xterm + + # Artifacts - uses: actions/upload-artifact@v4 if: failure() with: name: cypress-screenshots path: OpenSearch-Dashboards/plugins/index-management-dashboards-plugin/cypress/screenshots - # Test run video was always captured, so this action uses "always()" condition + - uses: actions/upload-artifact@v4 if: always() with: diff --git a/.github/workflows/cypress-workflow.yml b/.github/workflows/cypress-workflow.yml index 4586f725d..74eb8b3ea 100644 --- a/.github/workflows/cypress-workflow.yml +++ b/.github/workflows/cypress-workflow.yml @@ -7,9 +7,9 @@ on: branches: - "*" env: - OPENSEARCH_DASHBOARDS_VERSION: 'main' - CYPRESS_MEMORY_LIMIT: '40960' - NODE_OPTIONS: '--max-old-space-size=40960' + OPENSEARCH_DASHBOARDS_VERSION: "main" + CYPRESS_MEMORY_LIMIT: "262144" + jobs: tests: name: Run Cypress E2E tests diff --git a/cypress.config.js b/cypress.config.js index 541043567..a15d926bc 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -3,14 +3,21 @@ const { defineConfig } = require("cypress"); module.exports = defineConfig({ e2e: { specPattern: "cypress/e2e/plugins/index-management-dashboards-plugin/*.{js,jsx,ts,tsx}", + baseUrl: "http://localhost:5601", + + // Timeouts defaultCommandTimeout: 60000, requestTimeout: 60000, responseTimeout: 60000, - baseUrl: "http://localhost:5601", - viewportWidth: 2000, - viewportHeight: 1320, - // Performance optimizations + // Lighter footprint in CI + viewportWidth: 1280, + viewportHeight: 900, + video: false, + screenshotOnRunFailure: true, + retries: 1, + + // Performance knobs numTestsKeptInMemory: 0, experimentalMemoryManagement: true, @@ -47,8 +54,25 @@ module.exports = defineConfig({ ], }, ], + setupNodeEvents(on, config) { - config.env.NODE_OPTIONS = "—max-old-space-size=40960"; + // Harden Chrome for CI; avoid Electron/Chrome renderer OOMs. + on("before:browser:launch", (browser = {}, launchOptions) => { + if (browser.family === "chromium") { + // Increase V8 heap for test app JS + launchOptions.args.push("--js-flags=--max-old-space-size=262144"); + // CI stability flags + launchOptions.args.push("--disable-dev-shm-usage"); + launchOptions.args.push("--no-sandbox"); + launchOptions.args.push("--disable-gpu"); + launchOptions.args.push("--disable-software-rasterizer"); + launchOptions.args.push("--disable-features=VizDisplayCompositor"); + // Modern headless is a bit leaner + launchOptions.args.push("--headless=new"); + } + return launchOptions; + }); + return config; }, },