diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 177aa6d04..6ec2a7d98 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -12,7 +12,7 @@ jobs: # Skip job based on the commit message, only works in push to branches for now if: contains(toJson(github.event.commits), '[skip ci]') == false name: Build & test Docker images with random user - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: matrix: use-random-user: [false, true] @@ -20,10 +20,11 @@ jobs: - uses: actions/checkout@v4 - name: Output Docker info run: docker info - - name: Set up Python 3.8 + - name: Set up Python uses: actions/setup-python@v4.7.1 with: - python-version: 3.8 + python-version: '3.11' + check-latest: true - name: Get branch name (only for push to branch) if: github.event_name == 'push' run: echo "BRANCH=$(echo ${PUSH_BRANCH##*/})" >> $GITHUB_ENV diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2ba5222d4..8b4c99084 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,7 +10,7 @@ jobs: # Only continue if the commit message has '[deploy]' in it if: contains(toJson(github.event.commits), '[deploy]') == true name: Deploy Docker images - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/helm-chart-test.yml b/.github/workflows/helm-chart-test.yml index 3f3b53f05..9d2c4a8fc 100644 --- a/.github/workflows/helm-chart-test.yml +++ b/.github/workflows/helm-chart-test.yml @@ -13,105 +13,47 @@ permissions: contents: read jobs: - lint-test: - name: "Lint Tests with ct" + build-and-test: + # Skip job based on the commit message, only works in push to branches for now + if: contains(toJson(github.event.commits), '[skip ci]') == false + name: Build & test Docker images with Helm charts runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Helm - uses: azure/setup-helm@v3 - with: - version: v3.13.2 - + - uses: actions/checkout@v4 + - name: Output Docker info + run: docker info - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v4.7.1 with: - python-version: '3.10' + python-version: '3.11' check-latest: true - - - name: Set up chart-testing - uses: helm/chart-testing-action@v2.6.1 - - - name: Run chart-testing (list-changed) - id: list-changed - run: | - changed=$(ct list-changed --config tests/chart-test.yaml) - if [[ -n "$changed" ]]; then - echo "{changed}={true}" >> $GITHUB_OUTPUT - fi - - - name: Run chart-testing (lint) - run: ct lint --config tests/chart-test.yaml - - - name: Create kind cluster - uses: helm/kind-action@v1.8.0 - with: - config: ./tests/kind-cluster-config.yaml - - - name: Run chart-testing (install) - run: ct install --all --config tests/chart-test.yaml - - deploy-grid-selenium-tests: - name: "Run Selenium Tests on K8s" - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - browser: [NodeChrome,NodeEdge,NodeFirefox] - steps: - - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Helm - uses: azure/setup-helm@v3 - with: - version: v3.13.2 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - check-latest: true - - - name: Create kind cluster - uses: helm/kind-action@v1.8.0 - with: - config: ./tests/kind-cluster-config.yaml - - # 👋 Documentation link for Ingress Installation on kind k8s cluster https://kind.sigs.k8s.io/docs/user/ingress - - name: Install ingress-nginx on kind kubernetes cluster - run: | - kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml - kubectl wait --namespace ingress-nginx \ - --for=condition=ready pod \ - --selector=app.kubernetes.io/component=controller \ - --timeout=90s - - - name: Deploy Selenium Grid Chart - run: | - helm repo add kedacore https://kedacore.github.io/charts - helm repo update - helm dependency build charts/selenium-grid - helm upgrade --install selenium-grid -f ./tests/override-kind-auth-${{matrix.browser}}-values.yaml charts/selenium-grid --namespace selenium-grid-test --create-namespace - kubectl get ingress --all-namespaces - - - name: Verify Post Deployment Grid Health and k8s pods status - run: | - sleep 20 # Allow Kubernetes to pull Docker Images and start Pods - python ./tests/K8sSmokeTest.py "http://localhost" - kubectl get pods -n selenium-grid-test - kubectl get events -n selenium-grid-test - - - name: Run Selenium Tests Against Kubernetes - run: | - export SELENIUM_GRID_HOST=localhost - export SELENIUM_GRID_PORT=80 - export RUN_IN_DOCKER_COMPOSE=true - ./tests/bootstrap.sh ${{matrix.browser}} \ No newline at end of file + - name: Get branch name (only for push to branch) + if: github.event_name == 'push' + run: echo "BRANCH=$(echo ${PUSH_BRANCH##*/})" >> $GITHUB_ENV + env: + PUSH_BRANCH: ${{ github.ref }} + - name: Get target branch name (only for PRs) + if: github.event_name == 'pull_request' + run: echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV + env: + TARGET_BRANCH: ${{ github.head_ref }} + - name: Output branch name + run: echo ${BRANCH} + - name: Sets build date + run: echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV + - name: Build Docker images + run: VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make build + - name: Setup Kubernetes environment + run: make chart_setup_env + - name: Setup Kubernetes cluster + run: VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make chart_cluster_setup + - name: Test Selenium Grid on Kubernetes + uses: nick-invision/retry@v2.9.0 + with: + timeout_minutes: 20 + max_attempts: 3 + command: | + VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make chart_test + - name: Cleanup Kubernetes cluster + if: always() + run: make chart_cluster_cleanup diff --git a/.github/workflows/label-commenter.yml b/.github/workflows/label-commenter.yml index 08861192a..bf2e02b25 100644 --- a/.github/workflows/label-commenter.yml +++ b/.github/workflows/label-commenter.yml @@ -11,7 +11,7 @@ permissions: jobs: comment: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Label Commenter diff --git a/.github/workflows/test-video.yml b/.github/workflows/test-video.yml index 3e7c34498..da59f8a8f 100644 --- a/.github/workflows/test-video.yml +++ b/.github/workflows/test-video.yml @@ -12,15 +12,16 @@ jobs: # Skip job based on the commit message, only works in push to branches for now if: contains(toJson(github.event.commits), '[skip ci]') == false name: Test video recorded through Docker Selenium - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Output Docker info run: docker info - - name: Set up Python 3.8 + - name: Set up Python uses: actions/setup-python@v4.7.1 with: - python-version: 3.8 + python-version: '3.11' + check-latest: true - name: Get branch name (only for push to branch) if: github.event_name == 'push' run: echo "BRANCH=$(echo ${PUSH_BRANCH##*/})" >> $GITHUB_ENV @@ -51,4 +52,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: firefox_video - path: ./tests/videos/firefox_video.mp4 \ No newline at end of file + path: ./tests/videos/firefox_video.mp4 diff --git a/Makefile b/Makefile index 5d4590d8e..e207c209e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ NAME := $(or $(NAME),$(NAME),selenium) CURRENT_DATE := $(shell date '+%Y%m%d') BUILD_DATE := $(or $(BUILD_DATE),$(BUILD_DATE),$(CURRENT_DATE)) -VERSION := $(or $(VERSION),$(VERSION),4.11.0) +VERSION := $(or $(VERSION),$(VERSION),4.15.0) TAG_VERSION := $(VERSION)-$(BUILD_DATE) NAMESPACE := $(or $(NAMESPACE),$(NAMESPACE),$(NAME)) AUTHORS := $(or $(AUTHORS),$(AUTHORS),SeleniumHQ) @@ -361,6 +361,32 @@ test_video: video hub chrome firefox edge docker run -v $$(pwd):$$(pwd) -w $$(pwd) jrottenberg/ffmpeg:6.0-alpine -v error -i ./tests/videos/firefox_video.mp4 -f null - 2>error.log docker run -v $$(pwd):$$(pwd) -w $$(pwd) jrottenberg/ffmpeg:6.0-alpine -v error -i ./tests/videos/edge_video.mp4 -f null - 2>error.log +chart_setup_env: + ./tests/K8s/chart_setup_env.sh + +chart_test: chart_lint \ + chart_install_chrome \ + chart_install_firefox \ + chart_install_edge + +chart_cluster_setup: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/K8s/chart_cluster_setup.sh + +chart_lint: + ./tests/K8s/chart_lint.sh + +chart_install_chrome: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/K8s/chart_install.sh NodeChrome + +chart_install_firefox: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/K8s/chart_install.sh NodeFirefox + +chart_install_edge: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/K8s/chart_install.sh NodeEdge + +chart_cluster_cleanup: + ./tests/K8s/chart_cluster_cleanup.sh + .PHONY: \ all \ base \ diff --git a/charts/selenium-grid/TESTING.md b/charts/selenium-grid/TESTING.md new file mode 100644 index 000000000..aee1c772b --- /dev/null +++ b/charts/selenium-grid/TESTING.md @@ -0,0 +1,44 @@ +# Testing Selenium Grid Helm Chart + +All related testing to this helm chart will be documented in this file. + +## Test Traceability Matrix + +| Features | TC Description | Coverage | +|------------------------|----------------------------------------------------------------------|----------| +| Basic Auth | Basic Auth is disabled | ✓ | +| | Basic Auth is enabled | ✗ | +| Auto scaling | Auto scaling with `enableWithExistingKEDA` is `true` | ✓ | +| | Auto scaling with `scalingType` is `job` | ✓ | +| | Auto scaling with `scalingType` is `deployment` | ✗ | +| | Auto scaling with `autoscaling.scaledOptions.minReplicaCount` is `0` | ✓ | +| Ingress | Ingress is enabled without `hostname` | ✓ | +| | Hub `sub-path` is set with Ingress `ImplementationSpecific` paths | ✓ | +| Distributed components | `isolateComponents` is enabled | ✓ | +| Browser Nodes | Node `nameOverride` is set | ✓ | +| | Sanity tests in node | ✓ | +| | Video recorder is enabled in node | ✗ | + +## Build & test Docker images with Helm charts +Noted: These `make` commands are composed and tested on Linux x86_64. +Run entire commands to build and test Docker images with Helm charts in local environment. + +```bash +# Back to root directory +cd ../.. + +# Build Docker images +make build + +# Setup Kubernetes environment +make chart_setup_env + +# Setup Kubernetes cluster +make chart_cluster_setup + +# Test Selenium Grid on Kubernetes +make chart_test + +# Cleanup Kubernetes cluster +make chart_cluster_cleanup +``` diff --git a/tests/override-kind-auth-NodeEdge-values.yaml b/charts/selenium-grid/ci/NodeChrome-values.yaml similarity index 52% rename from tests/override-kind-auth-NodeEdge-values.yaml rename to charts/selenium-grid/ci/NodeChrome-values.yaml index 936745c23..f3c30c48d 100644 --- a/tests/override-kind-auth-NodeEdge-values.yaml +++ b/charts/selenium-grid/ci/NodeChrome-values.yaml @@ -1,13 +1,10 @@ -# This is used in Helm chart testing. This disables the basic auth on seleneium grid -# Basic auth settings for Selenium Grid -basicAuth: - # Enable or disable basic auth - enabled: false +# This is used in Helm chart testing. This disables the basic auth on selenium grid # Configuration for chrome nodes chromeNode: - # Enable chrome nodes + nameOverride: my-chrome-name +# Configuration for edge nodes +edgeNode: enabled: false # Configuration for firefox nodes firefoxNode: - # Enable firefox nodes enabled: false diff --git a/tests/override-kind-auth-NodeChrome-values.yaml b/charts/selenium-grid/ci/NodeEdge-values.yaml similarity index 52% rename from tests/override-kind-auth-NodeChrome-values.yaml rename to charts/selenium-grid/ci/NodeEdge-values.yaml index e3628ac84..1d1c13f9c 100644 --- a/tests/override-kind-auth-NodeChrome-values.yaml +++ b/charts/selenium-grid/ci/NodeEdge-values.yaml @@ -1,13 +1,10 @@ -# This is used in Helm chart testing. This disables the basic auth on seleneium grid -# Basic auth settings for Selenium Grid -basicAuth: - # Enable or disable basic auth +# This is used in Helm chart testing. This disables the basic auth on selenium grid +# Configuration for chrome nodes +chromeNode: enabled: false # Configuration for edge nodes edgeNode: - # Enable edge nodes - enabled: false + nameOverride: my-edge-name # Configuration for firefox nodes firefoxNode: - # Enable firefox nodes enabled: false diff --git a/tests/override-kind-auth-NodeFirefox-values.yaml b/charts/selenium-grid/ci/NodeFirefox-values.yaml similarity index 51% rename from tests/override-kind-auth-NodeFirefox-values.yaml rename to charts/selenium-grid/ci/NodeFirefox-values.yaml index d679cbfa7..418d00b8a 100644 --- a/tests/override-kind-auth-NodeFirefox-values.yaml +++ b/charts/selenium-grid/ci/NodeFirefox-values.yaml @@ -1,13 +1,10 @@ -# This is used in Helm chart testing. This disables the basic auth on seleneium grid -# Basic auth settings for Selenium Grid -basicAuth: - # Enable or disable basic auth - enabled: false +# This is used in Helm chart testing. This disables the basic auth on selenium grid # Configuration for chrome nodes chromeNode: - # Enable chrome nodes - enabled: false + enabled: false # Configuration for edge nodes edgeNode: - # Enable edge nodes enabled: false +# Configuration for firefox nodes +firefoxNode: + nameOverride: my-firefox-name diff --git a/charts/selenium-grid/ci/customIngressPath-values.yaml b/charts/selenium-grid/ci/auth-ingress-values.yaml similarity index 55% rename from charts/selenium-grid/ci/customIngressPath-values.yaml rename to charts/selenium-grid/ci/auth-ingress-values.yaml index e7812bc4a..459d218d3 100644 --- a/charts/selenium-grid/ci/customIngressPath-values.yaml +++ b/charts/selenium-grid/ci/auth-ingress-values.yaml @@ -6,14 +6,14 @@ ingress: hostname: "" paths: - path: /selenium(/|$)(.*) - pathType: Prefix + pathType: ImplementationSpecific backend: service: name: '{{ template "seleniumGrid.router.fullname" $ }}' port: number: 4444 - path: /(/?)(session/.*/se/vnc) - pathType: Prefix + pathType: ImplementationSpecific backend: service: name: '{{ template "seleniumGrid.router.fullname" $ }}' @@ -29,24 +29,3 @@ hub: components: subPath: *gridAppRoot - -chromeNode: - enabled: true - extraEnvironmentVariables: - - name: SE_NODE_OVERRIDE_MAX_SESSIONS - value: "true" - - name: SE_NODE_MAX_SESSIONS - value: "5" - startupProbe: - exec: - command: - - bash - - -c - - if [ $(curl --write-out %{http_code} --silent --output /dev/null http://selenium-router:4444/selenium/wd/hub/status) -ne 200 ]; then exit 1; fi - failureThreshold: 15 - periodSeconds: 5 - -edgeNode: - enabled: false -firefoxNode: - enabled: false diff --git a/charts/selenium-grid/ci/autoscaling-values.yaml b/charts/selenium-grid/ci/autoscaling-values.yaml new file mode 100644 index 000000000..7fa80b2bb --- /dev/null +++ b/charts/selenium-grid/ci/autoscaling-values.yaml @@ -0,0 +1,5 @@ +autoscaling: + enableWithExistingKEDA: true + scalingType: job + scaledOptions: + minReplicaCount: 0 diff --git a/charts/selenium-grid/ci/overrideNameChrome-values.yaml b/charts/selenium-grid/ci/overrideNameChrome-values.yaml deleted file mode 100644 index 6fe143648..000000000 --- a/charts/selenium-grid/ci/overrideNameChrome-values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# These desired is used to test the overrideName of the component -hub: - nameOverride: my-hub-name -chromeNode: - nameOverride: my-chrome-name -edgeNode: - enabled: false -firefoxNode: - enabled: false diff --git a/charts/selenium-grid/ci/overrideNameEdge-values.yaml b/charts/selenium-grid/ci/overrideNameEdge-values.yaml deleted file mode 100644 index 11f20066e..000000000 --- a/charts/selenium-grid/ci/overrideNameEdge-values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# These desired is used to test the overrideName of the component -hub: - nameOverride: my-hub-name -chromeNode: - enabled: false -edgeNode: - nameOverride: my-edge-name -firefoxNode: - enabled: false diff --git a/charts/selenium-grid/ci/overrideNameFirefox-values.yaml b/charts/selenium-grid/ci/overrideNameFirefox-values.yaml deleted file mode 100644 index dcf4670ed..000000000 --- a/charts/selenium-grid/ci/overrideNameFirefox-values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# These desired is used to test the overrideName of the component -hub: - nameOverride: my-hub-name -chromeNode: - enabled: false -edgeNode: - enabled: false -firefoxNode: - nameOverride: my-firefox-name diff --git a/tests/chart-test.yaml b/tests/K8s/chart-testing.yaml old mode 100644 new mode 100755 similarity index 60% rename from tests/chart-test.yaml rename to tests/K8s/chart-testing.yaml index e54edd7a3..7c085d076 --- a/tests/chart-test.yaml +++ b/tests/K8s/chart-testing.yaml @@ -1,3 +1,5 @@ +# This is config file for chart-testing tool. It is used to test Helm charts. +# https://github.com/helm/chart-testing target-branch: trunk chart-dirs: - charts diff --git a/tests/K8s/chart_cluster_cleanup.sh b/tests/K8s/chart_cluster_cleanup.sh new file mode 100755 index 000000000..972a9e8b6 --- /dev/null +++ b/tests/K8s/chart_cluster_cleanup.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +echo "Set ENV variables" +CLUSTER_NAME=${CLUSTER_NAME:-"chart-testing"} + +cleanup() { + echo "Clean up kind cluster" + kind delete clusters ${CLUSTER_NAME} +} + +cleanup diff --git a/tests/K8s/chart_cluster_setup.sh b/tests/K8s/chart_cluster_setup.sh new file mode 100755 index 000000000..cfddcd754 --- /dev/null +++ b/tests/K8s/chart_cluster_setup.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +echo "Set ENV variables" +CLUSTER_NAME=${CLUSTER_NAME:-"chart-testing"} +RELEASE_NAME=${RELEASE_NAME:-"test"} +SELENIUM_NAMESPACE=${SELENIUM_NAMESPACE:-"selenium"} +KEDA_NAMESPACE=${KEDA_NAMESPACE:-"keda"} +INGRESS_NAMESPACE=${INGRESS_NAMESPACE:-"ingress-nginx"} +SUB_PATH=${SUB_PATH:-"/selenium"} +CHART_PATH=${CHART_PATH:-"charts/selenium-grid"} +TEST_VALUES_PATH=${TEST_VALUES_PATH:-"charts/selenium-grid/ci"} +SELENIUM_GRID_HOST=${SELENIUM_GRID_HOST:-"localhost"} +SELENIUM_GRID_PORT=${SELENIUM_GRID_PORT:-"80"} +WAIT_TIMEOUT=${WAIT_TIMEOUT:-"90s"} +SKIP_CLEANUP=${SKIP_CLEANUP:-"false"} # For debugging purposes, retain the cluster after the test run + +# Function to clean up for retry step on workflow +cleanup() { + if [ "${SKIP_CLEANUP}" = "false" ]; then + ./tests/K8s/chart_cluster_cleanup.sh + fi +} + +# Function to be executed on command failure +on_failure() { + echo "There is step failed with exit status $?" + cleanup + exit $? +} + +# Trap ERR signal and call on_failure function +trap 'on_failure' ERR + +echo "Create Kind cluster" +kind create cluster --wait ${WAIT_TIMEOUT} --name ${CLUSTER_NAME} --config tests/K8s/kind-cluster-config.yaml + +echo "Install KEDA core on kind kubernetes cluster" +kubectl apply --server-side -f https://github.com/kedacore/keda/releases/download/v2.12.1/keda-2.12.1-core.yaml + +echo "Install ingress-nginx on kind kubernetes cluster" +kubectl apply -n ${INGRESS_NAMESPACE} -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml +kubectl wait --namespace ${INGRESS_NAMESPACE} \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/component=controller \ + --timeout=${WAIT_TIMEOUT} + +echo "Load built local Docker Images into Kind Cluster" +image_list=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep ${NAMESPACE} | grep ${VERSION}) +for image in $image_list; do + kind load docker-image --name ${CLUSTER_NAME} "$image" +done diff --git a/tests/K8s/chart_install.sh b/tests/K8s/chart_install.sh new file mode 100755 index 000000000..a19113395 --- /dev/null +++ b/tests/K8s/chart_install.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +echo "Set ENV variables" +CLUSTER_NAME=${CLUSTER_NAME:-"chart-testing"} +RELEASE_NAME=${RELEASE_NAME:-"test"} +SELENIUM_NAMESPACE=${SELENIUM_NAMESPACE:-"selenium"} +KEDA_NAMESPACE=${KEDA_NAMESPACE:-"keda"} +INGRESS_NAMESPACE=${INGRESS_NAMESPACE:-"ingress-nginx"} +SUB_PATH=${SUB_PATH:-"/selenium"} +CHART_PATH=${CHART_PATH:-"charts/selenium-grid"} +TEST_VALUES_PATH=${TEST_VALUES_PATH:-"charts/selenium-grid/ci"} +SELENIUM_GRID_HOST=${SELENIUM_GRID_HOST:-"localhost"} +SELENIUM_GRID_PORT=${SELENIUM_GRID_PORT:-"80"} +MATRIX_BROWSER=${1:-"NodeChrome"} +SELENIUM_GRID_AUTOSCALING=${2:-"true"} +SELENIUM_GRID_AUTOSCALING_MIN_REPLICA=${3:-"0"} +WAIT_TIMEOUT=${WAIT_TIMEOUT:-"90s"} +SLEEP_INTERVAL=${SLEEP_INTERVAL:-45} +SKIP_CLEANUP=${SKIP_CLEANUP:-"false"} # For debugging purposes, retain the cluster after the test run + +cleanup() { + if [ "${SKIP_CLEANUP}" = "false" ]; then + echo "Clean up chart release and namespace" + helm delete ${RELEASE_NAME} --namespace ${SELENIUM_NAMESPACE} + kubectl delete namespace ${SELENIUM_NAMESPACE} + fi +} + +# Function to be executed on command failure +on_failure() { + echo "There is step failed with exit status $?" + cleanup + exit $? +} + +# Trap ERR signal and call on_failure function +trap 'on_failure' ERR + +echo "Deploy Selenium Grid Chart" +helm upgrade --install ${RELEASE_NAME} \ +-f ${TEST_VALUES_PATH}/auth-ingress-values.yaml \ +-f ${TEST_VALUES_PATH}/autoscaling-values.yaml \ +-f ${TEST_VALUES_PATH}/${MATRIX_BROWSER}-values.yaml \ +--set autoscaling.enableWithExistingKEDA=${SELENIUM_GRID_AUTOSCALING} \ +--set autoscaling.scaledOptions.minReplicaCount=${SELENIUM_GRID_AUTOSCALING_MIN_REPLICA} \ +--set global.seleniumGrid.imageTag=${VERSION} \ +${CHART_PATH} --namespace ${SELENIUM_NAMESPACE} --create-namespace + +echo "Verify Post Deployment Grid Health and Pod Status" +kubectl get pods -n ${SELENIUM_NAMESPACE} + +echo "Run Tests" +export SELENIUM_GRID_HOST=${SELENIUM_GRID_HOST} +export SELENIUM_GRID_PORT=${SELENIUM_GRID_PORT}""${SUB_PATH} +export SELENIUM_GRID_AUTOSCALING=${SELENIUM_GRID_AUTOSCALING} +export SELENIUM_GRID_AUTOSCALING_MIN_REPLICA=${SELENIUM_GRID_AUTOSCALING_MIN_REPLICA} +export RUN_IN_DOCKER_COMPOSE=true +export SLEEP_INTERVAL=${SLEEP_INTERVAL} +./tests/bootstrap.sh ${MATRIX_BROWSER} + +cleanup diff --git a/tests/K8s/chart_lint.sh b/tests/K8s/chart_lint.sh new file mode 100755 index 000000000..25cd5df49 --- /dev/null +++ b/tests/K8s/chart_lint.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Function to be executed on command failure +on_failure() { + echo "There is step failed with exit status $?" + exit $? +} + +# Trap ERR signal and call on_failure function +trap 'on_failure' ERR + +cd tests || true + +if [ "${CI:-false}" = "false" ]; then + pip3 install virtualenv | grep -v 'Requirement already satisfied' + virtualenv docker-selenium-tests + source docker-selenium-tests/bin/activate +fi + +python -m pip install yamale==4.0.4 \ + yamllint==1.33.0 \ + | grep -v 'Requirement already satisfied' + +cd .. +ct lint --all --config tests/K8s/chart-testing.yaml + +if [ "${CI:-false}" = "false" ]; then + deactivate +fi diff --git a/tests/K8s/chart_setup_env.sh b/tests/K8s/chart_setup_env.sh new file mode 100755 index 000000000..c4bf6e3bc --- /dev/null +++ b/tests/K8s/chart_setup_env.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# Function to be executed on command failure +on_failure() { + echo "There is step failed with exit status $?" + exit $? +} + +# Trap ERR signal and call on_failure function +trap 'on_failure' ERR + +if [ "$(uname -m)" = "x86_64" ]; then + echo "Installing kind for AMD64 / x86_64" + curl -fsSL -o ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + chmod +x ./kind + sudo cp -frp ./kind /usr/local/bin/kind + sudo ln -sf /usr/local/bin/kind /usr/bin/kind + rm -rf kind + kind version + echo "===============================" + + echo "Installing kubectl for AMD64 / x86_64" + curl -fsSL -o ./kubectl "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + chmod +x ./kubectl + sudo cp -frp ./kubectl /usr/local/bin/kubectl + sudo ln -sf /usr/local/bin/kubectl /usr/bin/kubectl + rm -rf kubectl + kubectl version --client + echo "===============================" + + echo "Installing Helm for AMD64 / x86_64" + curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 + chmod 700 get_helm.sh + ./get_helm.sh + rm -rf get_helm.sh + helm version + echo "===============================" + + echo "Installing chart-testing for AMD64 / x86_64" + curl -fsSL -o ct.tar.gz https://github.com/helm/chart-testing/releases/download/v3.10.1/chart-testing_3.10.1_linux_amd64.tar.gz + sudo mkdir -p /opt/ct + sudo tar -xzf ct.tar.gz -C /opt/ct + sudo chmod +x /opt/ct/ct + sudo ln -sf /opt/ct/ct /usr/bin/ct + sudo cp -frp /opt/ct/ct /usr/local/bin/ct + sudo cp -frp /opt/ct/etc /etc/ct + rm -rf ct.tar.gz + ct version + echo "===============================" +fi diff --git a/tests/kind-cluster-config.yaml b/tests/K8s/kind-cluster-config.yaml old mode 100644 new mode 100755 similarity index 100% rename from tests/kind-cluster-config.yaml rename to tests/K8s/kind-cluster-config.yaml diff --git a/tests/K8sSmokeTest.py b/tests/K8sSmokeTest.py deleted file mode 100644 index 57f118be3..000000000 --- a/tests/K8sSmokeTest.py +++ /dev/null @@ -1,47 +0,0 @@ -import json -import sys -import time - -try: - from urllib2 import urlopen -except ImportError: - from urllib.request import urlopen - -SELENIUM_GRID_URL = sys.argv[1] -max_attempts = 6 -sleep_interval = 10 - - -def get_grid_status(): - try: - response = urlopen('%s/status' % (SELENIUM_GRID_URL)) - print("Response code: " + str(response.getcode())) - response = urlopen('%s/status' % (SELENIUM_GRID_URL)) - encoded_response = response.read() - encoding = response.headers.get_content_charset('utf-8') - decoded_response = encoded_response.decode(encoding) - print("Response: " + decoded_response) - response_json = json.loads(decoded_response) - return response_json['value']['ready'] - except Exception as e: - print(e) - return False - - -def wait_for_grid_to_get_ready(): - result = get_grid_status() - ctr=0 - while(not result): - ctr=ctr+1 - if(ctr>max_attempts): - print("Timed out. Grid is still not in ready state") - sys.exit(1) - - print("Grid is not in ready state. Waiting for {0} secs....".format(sleep_interval)) - time.sleep(sleep_interval) - result = get_grid_status() - print("Grid Status: " + str(result)) - print("Grid is in Ready state now") - - -wait_for_grid_to_get_ready() diff --git a/tests/SmokeTests/__init__.py b/tests/SmokeTests/__init__.py index 6a672d2af..2e5ef768b 100644 --- a/tests/SmokeTests/__init__.py +++ b/tests/SmokeTests/__init__.py @@ -10,27 +10,38 @@ SELENIUM_GRID_HOST = os.environ.get('SELENIUM_GRID_HOST', 'localhost') SELENIUM_GRID_PORT = os.environ.get('SELENIUM_GRID_PORT', '4444') +SELENIUM_GRID_AUTOSCALING = os.environ.get('SELENIUM_GRID_AUTOSCALING', 'false') +SELENIUM_GRID_AUTOSCALING_MIN_REPLICA = os.environ.get('SELENIUM_GRID_AUTOSCALING_MIN_REPLICA', 0) +SLEEP_INTERVAL = os.environ.get('SLEEP_INTERVAL', 3) class SmokeTests(unittest.TestCase): def smoke_test_container(self, port): current_attempts = 0 max_attempts = 3 - sleep_interval = 3 + sleep_interval = int(SLEEP_INTERVAL) status_fetched = False status_json = None + auto_scaling = SELENIUM_GRID_AUTOSCALING == 'true' + auto_scaling_min_replica = int(SELENIUM_GRID_AUTOSCALING_MIN_REPLICA) while current_attempts < max_attempts: current_attempts = current_attempts + 1 try: response = urlopen('http://%s:%s/status' % (SELENIUM_GRID_HOST, port)) status_json = json.loads(response.read()) - self.assertTrue(status_json['value']['ready'], "Container is not ready on port %s" % port) + if not auto_scaling or (auto_scaling and auto_scaling_min_replica > 0): + self.assertTrue(status_json['value']['ready'], "Container is not ready on port %s" % port) + else: + self.assertFalse(status_json['value']['ready'], "Container is autoscaling with min replica set to 0") status_fetched = True except Exception as e: time.sleep(sleep_interval) - self.assertTrue(status_fetched, "Container status was not fetched on port %s" % port) - self.assertTrue(status_json['value']['ready'], "Container is not ready on port %s" % port) + if not auto_scaling or (auto_scaling and auto_scaling_min_replica > 0): + self.assertTrue(status_fetched, "Container status was not fetched on port %s" % port) + self.assertTrue(status_json['value']['ready'], "Container is not ready on port %s" % port) + else: + self.assertFalse(status_json['value']['ready'], "Container is autoscaling with min replica set to 0") class GridTest(SmokeTests):