diff --git a/.circleci/config.yml b/.circleci/config.yml index be6808bd9..6d1529358 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,23 +2,23 @@ version: 2.1 executors: - ubuntu2004arm64: + ubuntu2204arm64: machine: - image: ubuntu-2004:current + image: ubuntu-2204:current resource_class: arm-medium - ubuntu2004amd64: + ubuntu2204amd64: machine: - image: ubuntu-2004:current + image: ubuntu-2204:current resource_class: medium - ubuntu2004arm64large: + ubuntu2204arm64large: machine: - image: ubuntu-2004:current + image: ubuntu-2204:current resource_class: arm-large jobs: build-multi-arch: parameters: - platforms: + platforms: type: string machine-type: type: executor @@ -26,7 +26,6 @@ jobs: environment: NAMESPACE: seleniarm BUILD_DATE: today - PLATFORMS: << parameters.platforms >> steps: - run: name: "Prepare workflow environment variables" @@ -43,34 +42,69 @@ jobs: name: "Build Docker images" command: | echo "Branch is $BRANCH" - NAME=${NAMESPACE} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} PLATFORMS=${PLATFORMS} BUILD_ARGS=${BUILD_ARGS} make build_multi + ARCH=$(echo << parameters.platforms >> | sed 's/linux\///') + NAME=${NAMESPACE} VERSION=${BRANCH}_${ARCH} BUILD_DATE=${BUILD_DATE} BUILD_ARGS=${BUILD_ARGS} make all_${ARCH} + - run: + name: "Inspect Docker Images" + command: | + VERSION=${CIRCLE_BRANCH//\//-} + ARCH=$(echo << parameters.platforms >> | sed 's/linux\///') + export TAG=${VERSION}_${ARCH}-${BUILD_DATE} + echo "TAG is ${TAG}" + docker image inspect $NAMESPACE/base:$TAG + docker image inspect $NAMESPACE/node-base:$TAG + docker image inspect $NAMESPACE/hub:$TAG + docker image inspect $NAMESPACE/node-chromium:$TAG + docker image inspect $NAMESPACE/standalone-chromium:$TAG + docker image inspect $NAMESPACE/node-firefox:$TAG + docker image inspect $NAMESPACE/standalone-firefox:$TAG + if [ "${ARCH}" = "amd64" ]; then + docker image inspect $NAMESPACE/node-chrome:$TAG + docker image inspect $NAMESPACE/standalone-chrome:$TAG + docker image inspect $NAMESPACE/node-edge:$TAG + docker image inspect $NAMESPACE/standalone-edge:$TAG + fi - run: name: "Save Docker Images in Cache" command: | export VERSION=${CIRCLE_BRANCH//\//-} - echo $NAMESPACE/base:$VERSION-$BUILD_DATE - echo $NAMESPACE/node-base:$VERSION-$BUILD_DATE - echo $NAMESPACE/hub:$VERSION-$BUILD_DATE - echo $NAMESPACE/node-chromium:$VERSION-$BUILD_DATE - echo $NAMESPACE/standalone-chromium:$VERSION-$BUILD_DATE - echo $NAMESPACE/node-firefox:$VERSION-$BUILD_DATE - echo $NAMESPACE/standalone-firefox:$VERSION-$BUILD_DATE - docker save -o multi-arch-images.tar \ - $NAMESPACE/base:$VERSION-$BUILD_DATE \ - $NAMESPACE/node-base:$VERSION-$BUILD_DATE \ - $NAMESPACE/hub:$VERSION-$BUILD_DATE \ - $NAMESPACE/node-chromium:$VERSION-$BUILD_DATE \ - $NAMESPACE/standalone-chromium:$VERSION-$BUILD_DATE \ - $NAMESPACE/node-firefox:$VERSION-$BUILD_DATE \ - $NAMESPACE/standalone-firefox:$VERSION-$BUILD_DATE + ARCH=$(echo << parameters.platforms >> | sed 's/linux\///') + export TAG=${VERSION}_${ARCH}-${BUILD_DATE} + mkdir images + echo "TAG is ${TAG}" + echo $NAMESPACE/base:$TAG + echo $NAMESPACE/node-base:$TAG + echo $NAMESPACE/hub:$TAG + echo $NAMESPACE/node-chromium:$TAG + echo $NAMESPACE/standalone-chromium:$TAG + echo $NAMESPACE/node-firefox:$TAG + echo $NAMESPACE/standalone-firefox:$TAG + if [ "${ARCH}" = "amd64" ]; then + echo $NAMESPACE/node-chrome:$TAG + echo $NAMESPACE/standalone-chrome:$TAG + echo $NAMESPACE/node-edge:$TAG + echo $NAMESPACE/standalone-edge:$TAG + fi + docker save -o images/${ARCH}.tar \ + $NAMESPACE/base:$TAG\ + $NAMESPACE/node-base:$TAG\ + $NAMESPACE/hub:$TAG\ + $NAMESPACE/node-chromium:$TAG\ + $NAMESPACE/standalone-chromium:$TAG\ + $NAMESPACE/node-firefox:$TAG\ + $NAMESPACE/standalone-firefox:$TAG\ + $(if [ "${ARCH}" = "amd64" ]; then echo "$NAMESPACE/node-chrome:$TAG $NAMESPACE/standalone-chrome:$TAG"; fi) \ + $(if [ "${ARCH}" = "amd64" ]; then echo "$NAMESPACE/node-edge:$TAG $NAMESPACE/standalone-edge:$TAG"; fi) - save_cache: key: multi-arch-images-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}-<< parameters.platforms >> - paths: - - multi-arch-images.tar + paths: + - images + - store_artifacts: + path: images test-multi-arch: parameters: - platforms: + platforms: type: string machine-type: type: executor @@ -81,7 +115,7 @@ jobs: steps: - checkout - restore_cache: - keys: + keys: - multi-arch-images-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}-<< parameters.platforms >> - run: uname -a - run: docker info @@ -89,7 +123,8 @@ jobs: name: "Load built images from cache into Docker" command: | echo "CIRCLE_WORKFLOW_ID = " $CIRCLE_WORKFLOW_ID - docker load -i multi-arch-images.tar + ARCH=$(echo << parameters.platforms >> | sed 's/linux\///') + docker load -i images/${ARCH}.tar - run: name: "Use Python3 and pip3 instead of Python2.7" command: | @@ -97,17 +132,218 @@ jobs: sed -i 's/pip /pip3 /g' tests/bootstrap.sh sed -i 's/python /python3 /g' tests/bootstrap.sh sed -i 's/-m pip3 /-m pip /g' tests/bootstrap.sh - - run: + - run: name: "Test Docker images" no_output_timeout: 2m command: | export USE_RANDOM_USER=false export BRANCH=${CIRCLE_BRANCH//\//-} - USE_RANDOM_USER_ID=${USE_RANDOM_USER} NAMESPACE=${NAMESPACE} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} SKIP_BUILD=true make test_multi_arch + ARCH=$(echo << parameters.platforms >> | sed 's/linux\///') + USE_RANDOM_USER_ID=${USE_RANDOM_USER} NAMESPACE=${NAMESPACE} VERSION=${BRANCH}_${ARCH} BUILD_DATE=${BUILD_DATE} SKIP_BUILD=true make test_${ARCH} + + test-video: + parameters: + platforms: + type: string + machine-type: + type: executor + executor: << parameters.machine-type >> + environment: + NAMESPACE: fhoeben + BUILD_DATE: today + steps: + - checkout + - run: uname -a + - run: docker info + - run: + name: "Use Python3 and pip3 instead of Python2.7" + command: | + echo "Use Python3 and pip3 instead of Python2.7" + sed -i 's/pip /pip3 /g' tests/bootstrap.sh + sed -i 's/python /python3 /g' tests/bootstrap.sh + sed -i 's/-m pip3 /-m pip /g' tests/bootstrap.sh + - run: + name: "Test and Build Docker images" + no_output_timeout: 2m + command: | + export USE_RANDOM_USER=false + export BRANCH=${CIRCLE_BRANCH//\//-} + ARCH=$(echo << parameters.platforms >> | sed 's/linux\///') + NAME=${NAMESPACE} USE_RANDOM_USER_ID=${USE_RANDOM_USER} NAMESPACE=${NAMESPACE} FFMPEG_TAG_VERSION=${BRANCH}_${ARCH} VERSION=${BRANCH}_${ARCH} BUILD_DATE=${BUILD_DATE} make test_video + - store_artifacts: + path: tests/videos + - run: + name: "Inspect Docker Images" + command: | + VERSION=${CIRCLE_BRANCH//\//-} + ARCH=$(echo << parameters.platforms >> | sed 's/linux\///') + export TAG=${VERSION}_${ARCH}-${BUILD_DATE} + echo "TAG is ${TAG}" + docker image inspect $NAMESPACE/video:$TAG + - run: + name: "Save Docker Image in Cache" + command: | + export VERSION=${CIRCLE_BRANCH//\//-} + ARCH=$(echo << parameters.platforms >> | sed 's/linux\///') + export TAG=${VERSION}_${ARCH}-${BUILD_DATE} + mkdir images + echo "TAG is ${TAG}" + echo $NAMESPACE/video:$TAG + docker save -o images/video.tar \ + $NAMESPACE/video:$TAG + - save_cache: + key: video-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}-<< parameters.platforms >> + paths: + - images + - store_artifacts: + path: images + + manifest-multi-arch: + parameters: + image-name: + type: string + machine-type: + type: executor + executor: << parameters.machine-type >> + environment: + NAMESPACE: seleniarm + BUILD_DATE: today + steps: + - checkout + - restore_cache: + keys: + - multi-arch-images-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}-linux/arm64 + - restore_cache: + keys: + - multi-arch-images-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}-linux/amd64 + - run: uname -a + - run: docker info + - run: + name: "Load built images from cache into Docker" + command: | + echo "CIRCLE_WORKFLOW_ID = " $CIRCLE_WORKFLOW_ID + docker load -i images/arm64.tar + docker load -i images/amd64.tar + - run: + name: "Create manifest" + command: | + VERSION=${CIRCLE_BRANCH//\//-} + IMAGE_NAME=$NAMESPACE/<< parameters.image-name >> + MANIFEST_TAG=${VERSION}-${BUILD_DATE} + ARM_TAG=${VERSION}_arm64-${BUILD_DATE} + AMD_TAG=${VERSION}_amd64-${BUILD_DATE} + echo "Creating ${IMAGE_NAME}:${MANIFEST_TAG} based on ${IMAGE_NAME}:${ARM_TAG} and ${IMAGE_NAME}:${AMD_TAG}" + MANIFEST_IMAGE=${IMAGE_NAME}:${MANIFEST_TAG} + ARM_IMAGE=${IMAGE_NAME}_arm64:${MANIFEST_TAG} + AMD_IMAGE=${IMAGE_NAME}_amd64:${MANIFEST_TAG} + docker tag ${IMAGE_NAME}:${ARM_TAG} ${ARM_IMAGE} + docker tag ${IMAGE_NAME}:${AMD_TAG} ${AMD_IMAGE} + echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin + docker push ${ARM_IMAGE} + docker push ${AMD_IMAGE} + docker manifest create ${MANIFEST_IMAGE} ${ARM_IMAGE} ${AMD_IMAGE} + docker manifest push ${MANIFEST_IMAGE} + - run: + name: "Inspect manifest" + command: | + VERSION=${CIRCLE_BRANCH//\//-} + IMAGE_NAME=$NAMESPACE/<< parameters.image-name >> + MANIFEST_TAG=${VERSION}-${BUILD_DATE} + docker manifest inspect ${IMAGE_NAME}:${MANIFEST_TAG} + + manifest-amd-only-arch: + parameters: + image-name: + type: string + machine-type: + type: executor + executor: << parameters.machine-type >> + environment: + NAMESPACE: seleniarm + BUILD_DATE: today + steps: + - checkout + - restore_cache: + keys: + - multi-arch-images-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}-linux/amd64 + - run: uname -a + - run: docker info + - run: + name: "Load built images from cache into Docker" + command: | + echo "CIRCLE_WORKFLOW_ID = " $CIRCLE_WORKFLOW_ID + docker load -i images/amd64.tar + - run: + name: "Create manifest" + command: | + VERSION=${CIRCLE_BRANCH//\//-} + IMAGE_NAME=$NAMESPACE/<< parameters.image-name >> + MANIFEST_TAG=${VERSION}-${BUILD_DATE} + AMD_TAG=${VERSION}_amd64-${BUILD_DATE} + echo "Creating ${IMAGE_NAME}:${MANIFEST_TAG} based on ${IMAGE_NAME}:${AMD_TAG}" + MANIFEST_IMAGE=${IMAGE_NAME}:${MANIFEST_TAG} + AMD_IMAGE=${IMAGE_NAME}_amd64:${MANIFEST_TAG} + docker tag ${IMAGE_NAME}:${AMD_TAG} ${AMD_IMAGE} + echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin + docker push ${AMD_IMAGE} + docker manifest create ${MANIFEST_IMAGE} ${AMD_IMAGE} + docker manifest push ${MANIFEST_IMAGE} + - run: + name: "Inspect manifest" + command: | + VERSION=${CIRCLE_BRANCH//\//-} + IMAGE_NAME=$NAMESPACE/<< parameters.image-name >> + MANIFEST_TAG=${VERSION}-${BUILD_DATE} + docker manifest inspect ${IMAGE_NAME}:${MANIFEST_TAG} + + manifest-amd-image: + parameters: + image-name: + type: string + machine-type: + type: executor + executor: << parameters.machine-type >> + environment: + NAMESPACE: fhoeben + BUILD_DATE: today + steps: + - checkout + - restore_cache: + keys: + - << parameters.image-name >>-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}-linux/amd64 + - run: uname -a + - run: docker info + - run: + name: "Load built images from cache into Docker" + command: | + echo "CIRCLE_WORKFLOW_ID = " $CIRCLE_WORKFLOW_ID + docker load -i images/<< parameters.image-name >>.tar + - run: + name: "Create manifest" + command: | + VERSION=${CIRCLE_BRANCH//\//-} + IMAGE_NAME=$NAMESPACE/<< parameters.image-name >> + MANIFEST_TAG=${VERSION}-${BUILD_DATE} + AMD_TAG=${VERSION}_amd64-${BUILD_DATE} + echo "Creating ${IMAGE_NAME}:${MANIFEST_TAG} based on ${IMAGE_NAME}:${AMD_TAG}" + MANIFEST_IMAGE=${IMAGE_NAME}:${MANIFEST_TAG} + AMD_IMAGE=${IMAGE_NAME}_amd64:${MANIFEST_TAG} + docker tag ${IMAGE_NAME}:${AMD_TAG} ${AMD_IMAGE} + echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin + docker push ${AMD_IMAGE} + docker manifest create ${MANIFEST_IMAGE} ${AMD_IMAGE} + docker manifest push ${MANIFEST_IMAGE} + - run: + name: "Inspect manifest" + command: | + VERSION=${CIRCLE_BRANCH//\//-} + IMAGE_NAME=$NAMESPACE/<< parameters.image-name >> + MANIFEST_TAG=${VERSION}-${BUILD_DATE} + docker manifest inspect ${IMAGE_NAME}:${MANIFEST_TAG} deploy-multi-arch: parameters: - platforms: + platforms: type: string build-args: type: string @@ -187,7 +423,7 @@ jobs: deploy-multi-arch-full-grid: parameters: - platforms: + platforms: type: string build-args: type: string @@ -314,21 +550,47 @@ workflows: - build-multi-arch: name: build-multi-arch-arm64 platforms: linux/arm64 - machine-type: ubuntu2004arm64 + machine-type: ubuntu2204arm64 - build-multi-arch: name: build-multi-arch-amd64 platforms: linux/amd64 - machine-type: ubuntu2004amd64 + machine-type: ubuntu2204amd64 - test-multi-arch: name: test-multi-arch-arm64 requires: [build-multi-arch-arm64] platforms: linux/arm64 - machine-type: ubuntu2004arm64 + machine-type: ubuntu2204arm64 - test-multi-arch: name: test-multi-arch-amd64 requires: [build-multi-arch-amd64] platforms: linux/amd64 - machine-type: ubuntu2004amd64 + machine-type: ubuntu2204amd64 + - manifest-multi-arch: + name: manifest-multi-arch-<< matrix.image-name >> + requires: [test-multi-arch-arm64, test-multi-arch-amd64] + machine-type: ubuntu2204amd64 + matrix: + parameters: + image-name: [ base, node-base, hub, node-chromium, standalone-chromium, node-firefox, standalone-firefox ] + - manifest-amd-only-arch: + name: manifest-amd-arch-<< matrix.image-name >> + requires: [test-multi-arch-amd64] + machine-type: ubuntu2204amd64 + matrix: + parameters: + image-name: [ node-chrome, standalone-chrome, node-edge, standalone-edge ] + + build-and-test-video: + jobs: + - test-video: + name: test-video-amd64 + platforms: linux/amd64 + machine-type: ubuntu2204amd64 + - manifest-amd-image: + name: manifest-video-amd64 + requires: [test-video-amd64] + machine-type: ubuntu2204amd64 + image-name: video deploy-multi-arch-full-grid: jobs: @@ -337,10 +599,10 @@ workflows: platforms: linux/arm64,linux/amd64,linux/arm/v7 build-args: --push make-targets: base_multi - machine-type: ubuntu2004arm64large - filters: - branches: - only: + machine-type: ubuntu2204arm64large + filters: + branches: + only: - trunk - deploy-multi-arch-full-grid: name: deploy-multi-arch-full-grid @@ -348,10 +610,10 @@ workflows: platforms: linux/arm64,linux/amd64,linux/arm/v7 build-args: --push make-targets: grid_multi - machine-type: ubuntu2004arm64 - filters: - branches: - only: + machine-type: ubuntu2204arm64 + filters: + branches: + only: - trunk - deploy-multi-arch-full-grid: name: deploy-multi-arch-node-base @@ -359,10 +621,10 @@ workflows: platforms: linux/arm64,linux/amd64,linux/arm/v7 build-args: --push make-targets: node_base_multi - machine-type: ubuntu2004arm64large - filters: - branches: - only: + machine-type: ubuntu2204arm64large + filters: + branches: + only: - trunk - deploy-multi-arch-full-grid: name: deploy-multi-arch-firefox @@ -370,10 +632,10 @@ workflows: platforms: linux/arm64,linux/amd64,linux/arm/v7 build-args: --push make-targets: firefox_multi - machine-type: ubuntu2004arm64large - filters: - branches: - only: + machine-type: ubuntu2204arm64large + filters: + branches: + only: - trunk - deploy-multi-arch-full-grid: name: deploy-multi-arch-chromium @@ -381,10 +643,10 @@ workflows: platforms: linux/arm64,linux/amd64,linux/arm/v7 build-args: --push make-targets: chromium_multi - machine-type: ubuntu2004arm64large - filters: - branches: - only: + machine-type: ubuntu2204arm64large + filters: + branches: + only: - trunk - deploy-multi-arch-full-grid: name: deploy-multi-arch-release-notes @@ -392,8 +654,8 @@ workflows: platforms: linux/arm64,linux/amd64,linux/arm/v7 build-args: --push make-targets: tag_and_push_multi_arch_browser_images - machine-type: ubuntu2004arm64 - filters: - branches: - only: - - trunk \ No newline at end of file + machine-type: ubuntu2204arm64 + filters: + branches: + only: + - trunk diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 88d78879e..d750dd44c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -19,14 +19,15 @@ body: validations: required: true - type: textarea - id: repro-command + id: reproduce-command attributes: - label: Command used to start Selenium Grid with Docker + label: Command used to start Selenium Grid with Docker (or Kubernetes) description: | - What command do you use to start Selenium Grid with Docker? + What command do you use to start Selenium Grid with Docker (or Kubernetes)? placeholder: | Please share the script or docker-compose file used. This will be automatically formatted into code, so no need for backticks. + If Kubernetes used, please share the YAML file, or chart values used to deploy the cluster. Be sure to include an SSCCE (Short, Self Contained, Correct [compilable] example) http://sscce.org/ render: shell @@ -47,14 +48,22 @@ body: attributes: label: Operating System description: What host operating system are you using to run docker-selenium? - placeholder: Windows 10? macOS BigSur? Ubuntu? + placeholder: Windows 10? macOS BigSur? Ubuntu? Kubernetes (Minikube, EKS, GKE, AKS, OpenShift, Rancher, etc.) version? validations: required: true - type: input id: version attributes: - label: Docker Selenium version (tag) + label: Docker Selenium version (image tag) description: What version of Docker Selenium are you using? - placeholder: 4.12.1-20230920? Please use the full tag, avoid "latest" + placeholder: 4.16.1-20231212? Please use the full tag, avoid "latest" validations: - required: true \ No newline at end of file + required: true + - type: input + id: chart-version + attributes: + label: Selenium Grid chart version (chart version) + description: What version of Selenium Grid chart are you using? + placeholder: 0.26.2? + validations: + required: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 637f89b29..5aec98bec 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,12 @@ updates: interval: daily time: '08:00' open-pull-requests-limit: 99 +- package-ecosystem: docker + directory: "/StandaloneChromium" + schedule: + interval: daily + time: '08:00' + open-pull-requests-limit: 99 - package-ecosystem: docker directory: "/StandaloneFirefox" schedule: @@ -36,6 +42,12 @@ updates: interval: daily time: '08:00' open-pull-requests-limit: 99 +- package-ecosystem: docker + directory: "/NodeChromium" + schedule: + interval: daily + time: '08:00' + open-pull-requests-limit: 99 - package-ecosystem: docker directory: "/" schedule: diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index faef306c6..63b94252c 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -2,17 +2,19 @@ name: Build & test on: push: + paths-ignore: + - '.circleci/**' pull_request: + paths-ignore: + - '.circleci/**' permissions: contents: read jobs: 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 random user - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: matrix: use-random-user: [false, true] @@ -20,10 +22,11 @@ jobs: - uses: actions/checkout@v4 - name: Output Docker info run: docker info - - name: Set up Python 3.8 - uses: actions/setup-python@v4.7.0 + - name: Set up Python + uses: actions/setup-python@v5.0.0 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 @@ -41,7 +44,11 @@ jobs: - name: Build Docker images run: VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make build - name: Test Docker images - run: USE_RANDOM_USER_ID=${USE_RANDOM_USER} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make test + uses: nick-invision/retry@v2.9.0 + with: + timeout_minutes: 20 + max_attempts: 3 + command: | + USE_RANDOM_USER_ID=${USE_RANDOM_USER} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make test env: USE_RANDOM_USER: ${{ matrix.use-random-user }} - diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6ea166dbe..21d0b60a7 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 @@ -51,25 +51,25 @@ jobs: DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}} DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}} - name: Deploy new images - uses: nick-invision/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd # v2 + uses: nick-invision/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2 with: timeout_minutes: 20 max_attempts: 3 command: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make release - name: Tag images as latest - uses: nick-invision/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd # v2 + uses: nick-invision/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2 with: timeout_minutes: 20 max_attempts: 3 command: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make tag_latest - name: Deploy latest tag - uses: nick-invision/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd # v2 + uses: nick-invision/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2 with: timeout_minutes: 20 max_attempts: 3 command: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make release_latest - name: Tag browser images - uses: nick-invision/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd # v2 + uses: nick-invision/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2 with: timeout_minutes: 20 max_attempts: 3 @@ -78,9 +78,9 @@ jobs: run: | git config --local user.email "selenium-ci@users.noreply.github.com" git config --local user.name "Selenium CI Bot" - git commit -m "Update tag in docs and files [skip ci]" -a + git commit -m "Update tag in docs and files" -a - name: Push changes - uses: ad-m/github-push-action@40bf560936a8022e68a3c00e7d2abefaf01305a6 # master + uses: ad-m/github-push-action@master with: github_token: ${{ secrets.SELENIUM_CI_TOKEN }} branch: trunk diff --git a/.github/workflows/helm-chart-release.yml b/.github/workflows/helm-chart-release.yml index 6a5722f3e..252ca1840 100644 --- a/.github/workflows/helm-chart-release.yml +++ b/.github/workflows/helm-chart-release.yml @@ -6,6 +6,7 @@ on: - trunk paths: - 'charts/selenium-grid/Chart.yaml' + workflow_dispatch: jobs: release: @@ -23,6 +24,6 @@ jobs: git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Run chart-releaser - uses: helm/chart-releaser-action@v1.5.0 + uses: helm/chart-releaser-action@v1.6.0 env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/helm-chart-test.yml b/.github/workflows/helm-chart-test.yml new file mode 100644 index 000000000..6bd18a4ec --- /dev/null +++ b/.github/workflows/helm-chart-test.yml @@ -0,0 +1,80 @@ +name: Lint and Test Helm Charts + +on: + push: + paths-ignore: + - '.circleci/**' + pull_request: + paths-ignore: + - '.circleci/**' + workflow_dispatch: + +permissions: + contents: read + +jobs: + build-and-test: + name: Test Helm charts + runs-on: ubuntu-latest + strategy: + matrix: + test-strategy: [chart_test, chart_test_parallel_autoscaling] + steps: + - uses: actions/checkout@v4 + - name: Output Docker info + run: docker info + - name: Set up Python + uses: actions/setup-python@v5.0.0 + with: + 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 + 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 + echo "IMAGE_REGISTRY=artifactory/selenium" >> $GITHUB_ENV + - name: Setup Kubernetes environment + run: make chart_setup_env + - name: Build Docker images + run: NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make build + - name: Build and lint charts + run: | + BUILD_DATE=${BUILD_DATE} make chart_build + echo "CHART_PACKAGE_PATH=$(cat /tmp/selenium_chart_version)" >> $GITHUB_ENV + echo "CHART_FILE_NAME=$(basename $(cat /tmp/selenium_chart_version))" >> $GITHUB_ENV + - name: Setup Kubernetes cluster + run: NAME=${IMAGE_REGISTRY} 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: 30 + max_attempts: 3 + command: | + NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make ${{ matrix.test-strategy }} + - name: Cleanup Kubernetes cluster + if: always() + run: make chart_cluster_cleanup + - name: Upload Helm chart package + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.test-strategy }}_${{ env.CHART_FILE_NAME }} + path: ${{ env.CHART_PACKAGE_PATH }} + - name: Upload Helm chart template rendered + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.test-strategy }}_chart_template_rendered.yaml + path: ./tests/tests/output_deployment.yaml + if-no-files-found: ignore 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/lock.yml b/.github/workflows/lock.yml index 599e1c54f..7abaecd80 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -14,12 +14,12 @@ jobs: action: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@be8aa5be94131386884a6da4189effda9b14aa21 # v3 + - uses: dessant/lock-threads@v5 with: process-only: 'issues' - issue-lock-inactive-days: '30' + issue-inactive-days: '30' issue-lock-reason: '' - issue-lock-comment: > + issue-comment: > This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. diff --git a/.github/workflows/scan-dockerfile.yml b/.github/workflows/scan-dockerfile.yml new file mode 100644 index 000000000..259a900dd --- /dev/null +++ b/.github/workflows/scan-dockerfile.yml @@ -0,0 +1,53 @@ +name: Scan Dockerfile vulnerabilities + +on: + push: + paths: + - '**/Dockerfile' + pull_request: + paths: + - '**/Dockerfile' + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + +jobs: + build-and-scan: + name: Scan Dockerfile vulnerabilities + permissions: write-all + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set severity for PRs + if: github.event_name == 'pull_request' || github.event_name == 'push' + run: | + echo "SEVERITY=HIGH,CRITICAL" >> $GITHUB_ENV + echo "EXIT_CODE=1" >> $GITHUB_ENV + - name: Set severity for others + if: github.event_name != 'pull_request' && github.event_name != 'push' + run: | + echo "SEVERITY=LOW,MEDIUM,HIGH,CRITICAL" >> $GITHUB_ENV + echo "EXIT_CODE=0" >> $GITHUB_ENV + - name: Scan source code + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + scan-ref: '.' + format: 'sarif' + output: 'source-results.sarif' + scanners: 'vuln,secret,misconfig' + skip-dirs: 'tests,Video' + exit-code: '${{ env.EXIT_CODE }}' + severity: '${{ env.SEVERITY }}' + limit-severities-for-sarif: true + - name: Upload source scan results to annotations + if: always() + uses: Ayrx/sarif_to_github_annotations@master + with: + sarif_file: 'source-results.sarif' + - name: Upload source scan results to GitHub Security tab + if: github.event_name != 'pull_request' + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: 'source-results.sarif' + category: source-results diff --git a/.github/workflows/test-video.yml b/.github/workflows/test-video.yml index b5de436e2..4982447f5 100644 --- a/.github/workflows/test-video.yml +++ b/.github/workflows/test-video.yml @@ -2,25 +2,28 @@ name: Test video files on: push: + paths-ignore: + - '.circleci/**' pull_request: + paths-ignore: + - '.circleci/**' permissions: contents: read jobs: 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: 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 - uses: actions/setup-python@v4.7.0 + - name: Set up Python + uses: actions/setup-python@v5.0.0 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 @@ -38,17 +41,22 @@ jobs: - name: Run Docker Compose to record video run: USE_RANDOM_USER_ID=${USE_RANDOM_USER} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make test_video - name: Upload recorded Chrome video - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: chrome_video path: ./tests/videos/chrome_video.mp4 + - name: Upload recorded Chromium video + uses: actions/upload-artifact@v4 + with: + name: chromium_video + path: ./tests/videos/chromium_video.mp4 - name: Upload recorded Edge video - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: edge_video path: ./tests/videos/edge_video.mp4 - name: Upload recorded Firefox video - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 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/.github/workflows/update-chart-changelog.yaml b/.github/workflows/update-chart-changelog.yaml new file mode 100644 index 000000000..3ac45a214 --- /dev/null +++ b/.github/workflows/update-chart-changelog.yaml @@ -0,0 +1,33 @@ +name: Update Chart CHANGELOG + +on: + release: + types: [published] + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-latest + permissions: write-all + if: ${{ contains(github.event.release.tag_name, 'selenium-grid') || github.event_name == 'workflow_dispatch' }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Update chart CHANGELOG + run: ./generate_chart_changelog.sh + + - name: Commit files + run: | + git config --local user.email "selenium-ci@users.noreply.github.com" + git config --local user.name "Selenium CI Bot" + git commit -m "Update chart CHANGELOG [skip ci]" -a + + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.SELENIUM_CI_TOKEN }} + branch: trunk diff --git a/.github/workflows/update-dev-beta-browser-images.yml b/.github/workflows/update-dev-beta-browser-images.yml index 6349a71a0..4bcef2c5b 100644 --- a/.github/workflows/update-dev-beta-browser-images.yml +++ b/.github/workflows/update-dev-beta-browser-images.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup environment variables run: | - export SELENIUM_VERSION=$(grep selenium-server Base/Dockerfile | sed 's/.*-\([^-]*\)\.jar \\/\1/' | head -n 1) + export SELENIUM_VERSION=$(grep BASE_VERSION Makefile | sed 's/.*,\([^)]*\))/\1/p' | head -n 1) echo "SELENIUM_VERSION="$SELENIUM_VERSION >> $GITHUB_ENV export BUILD_DATE=$(date '+%Y%m%d') echo "BUILD_DATE="$BUILD_DATE >> $GITHUB_ENV diff --git a/.gitignore b/.gitignore index 7377b135b..f77615fd4 100644 --- a/.gitignore +++ b/.gitignore @@ -147,4 +147,6 @@ ENV/ # End of https://www.gitignore.io/api/python .DS_Store - +/charts/*/charts +/charts/*/**.lock +/charts/*.tgz diff --git a/Base/Dockerfile b/Base/Dockerfile index d8a0f1808..a5189f675 100644 --- a/Base/Dockerfile +++ b/Base/Dockerfile @@ -1,28 +1,34 @@ -# FROM ubuntu:focal-20210416 -#FROM ubuntu:focal-20210827 -#FROM ubuntu:focal-20211006 -#FROM ubuntu:focal-20220302 -#FROM ubuntu:focal-20220415 -#FROM ubuntu:focal-20220531 -#FROM ubuntu:focal-20230301 -FROM debian:bullseye +FROM ubuntu:jammy-20231128 LABEL authors="Selenium " +# Arguments to define the version of dependencies to download +ARG VERSION +ARG RELEASE=selenium-${VERSION} +# Default value should be aligned with upstream Selenium (https://github.com/SeleniumHQ/selenium/blob/trunk/java/maven_deps.bzl) +ARG OPENTELEMETRY_VERSION=1.28.0 +ARG GRPC_VERSION=1.57.1 + +#Arguments to define the user running Selenium +ARG SEL_USER=seluser +ARG SEL_PASSWD=secret +ARG SEL_UID=1200 +ARG SEL_GID=1201 + +USER root #================================================ # Customize sources for apt-get #================================================ -#RUN echo "deb http://archive.ubuntu.com/ubuntu focal main universe\n" > /etc/apt/sources.list \ -# && echo "deb http://archive.ubuntu.com/ubuntu focal-updates main universe\n" >> /etc/apt/sources.list \ -# && echo "deb http://security.ubuntu.com/ubuntu focal-security main universe\n" >> /etc/apt/sources.list - -ARG TARGETARCH -ARG TARGETVARIANT +RUN echo "deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main universe\n" > /etc/apt/sources.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy main universe\n" >> /etc/apt/sources.list \ + && echo "deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main universe\n" >> /etc/apt/sources.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main universe\n" >> /etc/apt/sources.list \ + && echo "deb [arch=amd64,i386] http://security.ubuntu.com/ubuntu jammy-security main universe\n" >> /etc/apt/sources.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main universe\n" >> /etc/apt/sources.list # No interactive frontend during docker build ENV DEBIAN_FRONTEND=noninteractive \ DEBCONF_NONINTERACTIVE_SEEN=true - #======================== # Miscellaneous packages # Includes minimal runtime used for executing non GUI Java programs @@ -42,45 +48,67 @@ RUN apt-get -qqy update \ curl \ supervisor \ gnupg2 \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* - -#=================== - -RUN if [ $TARGETARCH = "arm" ] && [ $TARGETVARIANT = "v7" ]; then \ - export ARCH=armhf ; \ - else \ - export ARCH=$TARGETARCH ; \ - fi \ - && sed -i 's/securerandom\.source=file:\/dev\/random/securerandom\.source=file:\/dev\/urandom/' ./usr/lib/jvm/java-11-openjdk-$ARCH/conf/security/java.security - + libnss3-tools \ + && rm -rf /var/lib/apt/lists/* /var/cache/apt/* \ + && ARCH=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \ + && sed -i 's/securerandom\.source=file:\/dev\/random/securerandom\.source=file:\/dev\/urandom/' ./usr/lib/jvm/java-11-openjdk-${ARCH}/conf/security/java.security #=================== # Timezone settings # Possible alternative: https://github.com/docker/docker/issues/3359#issuecomment-32150214 #=================== ENV TZ "UTC" -RUN echo "${TZ}" > /etc/timezone \ - && dpkg-reconfigure --frontend noninteractive tzdata +RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime && \ + dpkg-reconfigure -f noninteractive tzdata && \ + cat /etc/timezone + +#====================================== +# Configure environement +#====================================== +ENV SEL_USER=${SEL_USER} +ENV SEL_UID=${SEL_UID} +ENV SEL_GID=${SEL_GID} +ENV HOME=/home/${SEL_USER} +ENV SEL_DIR=/opt/selenium +ENV EXTERNAL_JARS=/external_jars +ENV SE_DOWNLOAD_DIR=${HOME}/Downloads + +# Copy a script that we will use to correct permissions after running certain commands +COPY fix-permissions /usr/local/bin/fix-permissions +RUN chmod a+rx /usr/local/bin/fix-permissions #======================================== # Add normal user and group with passwordless sudo #======================================== -RUN groupadd seluser \ - --gid 1201 \ - && useradd seluser \ - --create-home \ - --gid 1201 \ - --shell /bin/bash \ - --uid 1200 \ - && usermod -a -G sudo seluser \ - && echo 'ALL ALL = (ALL) NOPASSWD: ALL' >> /etc/sudoers \ - && echo 'seluser:secret' | chpasswd -ENV HOME=/home/seluser +RUN echo "auth requisite pam_deny.so" >> /etc/pam.d/su \ + && sed -i.bak -e 's/^%admin/#%admin/' /etc/sudoers \ + && sed -i.bak -e 's/^%sudo/#%sudo/' /etc/sudoers \ + && echo 'ALL ALL = (ALL) NOPASSWD: ALL' >> /etc/sudoers + +RUN groupadd ${SEL_USER} \ + --gid ${SEL_GID} \ + && useradd ${SEL_USER} \ + --no-log-init \ + --create-home \ + --gid ${SEL_GID} \ + --shell /bin/bash \ + --uid ${SEL_UID} \ + && chmod g+w /etc/passwd \ + && echo "${SEL_USER}:${SEL_PASSWD}" | chpasswd + +#====================================== +# Create directories needed +#====================================== +RUN mkdir -p ${HOME}/.mozilla ${HOME}/.cache \ + ${SEL_DIR} ${SEL_DIR}/assets \ + /var/run/supervisor /var/log/supervisor \ + ${EXTERNAL_JARS} \ + ${SE_DOWNLOAD_DIR} #====================================== # Add Grid check script #====================================== -COPY check-grid.sh entry_point.sh /opt/bin/ +COPY --chown="${SEL_UID}:${SEL_GID}" check-grid.sh entry_point.sh /opt/bin/ #====================================== # Add Supervisor configuration file @@ -88,44 +116,50 @@ COPY check-grid.sh entry_point.sh /opt/bin/ COPY supervisord.conf /etc #========== -# Selenium & relaxing permissions for OpenShift and other non-sudo environments +# Selenium #========== -RUN mkdir -p /opt/selenium /opt/selenium/assets /var/run/supervisor /var/log/supervisor \ - && touch /opt/selenium/config.toml \ - && chmod -R 777 /opt/selenium /opt/selenium/assets /var/run/supervisor /var/log/supervisor /etc/passwd \ - && wget --no-verbose https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.10.0/selenium-server-4.10.0.jar \ - -O /opt/selenium/selenium-server.jar \ - && wget --no-verbose https://repo1.maven.org/maven2/org/seleniumhq/selenium/selenium-http-jdk-client/4.10.0/selenium-http-jdk-client-4.10.0.jar \ - -O /opt/selenium/selenium-http-jdk-client.jar \ - && chgrp -R 0 /opt/selenium ${HOME} /opt/selenium/assets /var/run/supervisor /var/log/supervisor \ - && chmod -R g=u /opt/selenium ${HOME} /opt/selenium/assets /var/run/supervisor /var/log/supervisor \ - && setfacl -Rm u:seluser:rwx /opt /opt/selenium ${HOME} /opt/selenium/assets /var/run/supervisor /var/log/supervisor +RUN touch ${SEL_DIR}/config.toml \ + && wget --no-verbose https://github.com/SeleniumHQ/selenium/releases/download/${RELEASE}/selenium-server-${VERSION}.jar \ + -O ${SEL_DIR}/selenium-server.jar \ + && echo "${SEL_PASSWD}" > ${SEL_DIR}/initialPasswd #===== # Download observability related jaegar jars and make them available in a separate directory # so that the container can skip downloading them everytime it comes up #===== -#RUN curl -fLo /tmp/cs https://github.com/coursier/launchers/raw/master/coursier \ -# RUN if [ `arch` = "aarch64" ] || [ `arch` = "x86_64" ]; then \ -RUN if [ `arch` = "aarch64" ]; then \ - curl -fL https://github.com/coursier/launchers/raw/master/cs-`arch`-pc-linux.gz | gzip -d > /tmp/cs \ - && chmod +x /tmp/cs \ - && mkdir -p /external_jars \ - && chmod -R 775 /external_jars ; \ - fi +RUN curl -fLo /tmp/cs https://github.com/coursier/launchers/raw/master/coursier \ + && chmod +x /tmp/cs -RUN if [ -f "/tmp/cs" ]; then \ - /tmp/cs fetch --classpath --cache /external_jars io.opentelemetry:opentelemetry-exporter-otlp:1.28.0 io.opentelemetry:opentelemetry-exporter-jaeger:1.28.0 io.grpc:grpc-netty:1.57.1 > /external_jars/.classpath.txt ; \ - fi +RUN /tmp/cs fetch --classpath --cache ${EXTERNAL_JARS} \ + io.opentelemetry:opentelemetry-exporter-otlp:${OPENTELEMETRY_VERSION} \ + io.opentelemetry:opentelemetry-exporter-jaeger:${OPENTELEMETRY_VERSION} \ + io.grpc:grpc-netty:${GRPC_VERSION} > ${EXTERNAL_JARS}/.classpath.txt +RUN rm -fr /root/.cache/* -RUN if [ -f "/tmp/cs" ]; then chmod 664 /external_jars/.classpath.txt ; fi +# Change ownership of directories +RUN fix-permissions ${HOME} ${SEL_DIR} ${SEL_DIR}/assets ${EXTERNAL_JARS} ${SE_DOWNLOAD_DIR} /var/run/supervisor /var/log/supervisor + +#========== +# Relaxing permissions for OpenShift and other non-sudo environments +#========== +RUN chmod g=u /etc/passwd #=================================================== # Run the following commands as non-privileged user #=================================================== -USER 1200:1201 +USER ${SEL_UID}:${SEL_GID} +VOLUME ${SE_DOWNLOAD_DIR} # Boolean value, maps "--bind-host" ENV SE_BIND_HOST false +# A too high maximum number of file descriptors (with the default value +# inherited from the docker host) can cause issues with some of our tools: +# - sanitizers hanging: https://github.com/google/sanitizers/issues/1662 +# - valgrind crashing: https://stackoverflow.com/a/75293014 +# This is not be a problem on our CI hosts, but developers who run the image +# on their machines may run into this (e.g., on Arch Linux), so warn them. +# (Note that .bashrc is only executed in interactive bash shells.) +RUN echo 'if [[ $(ulimit -n) -gt 200000 ]]; then echo "WARNING: Very high value reported by \"ulimit -n\". Consider passing \"--ulimit nofile=32768\" to \"docker run\"."; fi' >> ${HOME}/.bashrc + CMD ["/opt/bin/entry_point.sh"] diff --git a/Base/entry_point.sh b/Base/entry_point.sh index 0fd0b314e..0e8ef12f3 100755 --- a/Base/entry_point.sh +++ b/Base/entry_point.sh @@ -1,14 +1,34 @@ #!/usr/bin/env bash +_log () { + if [[ "$*" == "ERROR:"* ]] || [[ "$*" == "WARNING:"* ]] || [[ "${CONTAINER_LOGS_QUIET}" == "" ]]; then + echo "$@" + fi +} -#============================================== -# OpenShift or non-sudo environments support -# https://docs.openshift.com/container-platform/3.11/creating_images/guidelines.html#openshift-specific-guidelines -#============================================== - -if ! whoami &> /dev/null; then - if [ -w /etc/passwd ]; then - echo "${USER_NAME:-default}:x:$(id -u):0:${USER_NAME:-default} user:${HOME}:/sbin/nologin" >> /etc/passwd - fi +# If the container started as the root user +if [ "$(id -u)" == 0 ]; then + fix-permissions "${SE_DOWNLOAD_DIR}" +elif [ "$(id -u)" == "$(id -u ${SEL_USER})" ] && [ "$(id -g)" == "$(id -g ${SEL_USER})" ]; then + # Trust SEL_USER is the desired non-root user to execute with sudo + sudo -E fix-permissions "${SE_DOWNLOAD_DIR}" +else + # For non-root user to change ownership + # Relaxing permissions for OpenShift and other non-sudo environments + # (https://docs.openshift.com/container-platform/latest/openshift_images/create-images.html#use-uid_create-images) + if ! whoami &> /dev/null; then + _log "There is no entry in /etc/passwd for our UID=$(id -u). Attempting to fix..." + if [ -w /etc/passwd ]; then + _log "Renaming user to ${USER_NAME:-default} ($(id -u):$(id -g)" + # We cannot use "sed --in-place" since sed tries to create a temp file in + # /etc/ and we may not have write access. Apply sed on our own temp file: + sed --expression="s/^${SEL_USER}:/${USER_NAME:-default}:/" /etc/passwd > /tmp/passwd + echo "${USER_NAME:-default}:x:$(id -u):$(id -g):${USER_NAME:-default} user:${HOME}:/bin/bash" >> /tmp/passwd + cat /tmp/passwd > /etc/passwd + rm /tmp/passwd + _log "Added new ${USER_NAME:-default} user ($(id -u):$(id -g)). Fixed UID!" + fi + fi + fix-permissions "${SE_DOWNLOAD_DIR}" fi /usr/bin/supervisord --configuration /etc/supervisord.conf & diff --git a/Base/fix-permissions b/Base/fix-permissions new file mode 100644 index 000000000..e20ab2dbc --- /dev/null +++ b/Base/fix-permissions @@ -0,0 +1,46 @@ +#!/bin/bash +set -e + +if [[ "${SE_DOWNLOAD_DIR}" != "${HOME}/Downloads" ]] && [[ -d "${SE_DOWNLOAD_DIR}" ]]; then + mkdir -p "${SE_DOWNLOAD_DIR}" +fi + +if [ "$(id -u)" == 0 ]; then + # For root user to change ownership + for d in "$@"; do + find "${d}" \ + ! \( \ + -group "${SEL_GID}" \ + -a -perm -g+rwX \ + \) \ + -exec chown -R "${SEL_UID}:${SEL_GID}" -- {} \+ \ + -exec chgrp -R "${SEL_GID}" -- {} \+ \ + -exec chmod -R g+rwX -- {} \+ + find "${d}" \ + \( \ + -type d \ + -a ! -perm -775 \ + \) \ + -exec chmod -R 775 -- {} \+ + # Relaxing permissions for OpenShift and other non-sudo environments + chmod -R u+x "${d}" + chgrp -R 0 "${d}" + chmod -R g=u "${d}" + done + + # Only give write access for app data in case running of read-only filesystem + dirs=( + "${HOME}" + "${SEL_DIR}" + "${SE_DOWNLOAD_DIR}" + "/var/run/supervisor" + "/var/log/supervisor" + "/etc/passwd" + "/tmp/.X11-unix" + ) + for d in "${dirs[@]}"; do + if [[ -d $d ]]; then + chmod 777 -R "${d}" + fi + done +fi diff --git a/Distributor/Dockerfile b/Distributor/Dockerfile index 75e75468a..ce901ceeb 100644 --- a/Distributor/Dockerfile +++ b/Distributor/Dockerfile @@ -4,7 +4,7 @@ ARG AUTHORS FROM ${NAMESPACE}/base:${VERSION} LABEL authors=${AUTHORS} -USER 1200 +USER ${SEL_UID} #======================== # Selenium Distributor Configuration @@ -12,7 +12,7 @@ USER 1200 EXPOSE 5553 -COPY start-selenium-grid-distributor.sh \ +COPY --chown="${SEL_UID}:${SEL_GID}" start-selenium-grid-distributor.sh \ /opt/bin/ COPY selenium-grid-distributor.conf /etc/supervisor/conf.d/ diff --git a/Distributor/start-selenium-grid-distributor.sh b/Distributor/start-selenium-grid-distributor.sh index 0a653e81e..b51340c91 100755 --- a/Distributor/start-selenium-grid-distributor.sh +++ b/Distributor/start-selenium-grid-distributor.sh @@ -54,20 +54,21 @@ if [ ! -z "$SE_DISTRIBUTOR_PORT" ]; then PORT_CONFIG="--port ${SE_DISTRIBUTOR_PORT}" fi -EXTRA_LIBS="/opt/selenium/selenium-http-jdk-client.jar" +EXTRA_LIBS="" if [ ! -z "$SE_ENABLE_TRACING" ]; then EXTERNAL_JARS=$( .env ; \ echo TAG=$(TAG_VERSION) >> .env ; \ + echo NAME=$(NAME) >> .env ; \ echo NODE=$$node >> .env ; \ if [ $$node = "NodeChrome" ] ; then \ echo BROWSER=chrome >> .env ; \ echo VIDEO_FILE_NAME=chrome_video.mp4 >> .env ; \ fi ; \ + if [ $$node = "NodeChromium" ] ; then \ + echo BROWSER=chromium >> .env ; \ + echo VIDEO_FILE_NAME=chromium_video.mp4 >> .env ; \ + fi ; \ if [ $$node = "NodeEdge" ] ; then \ echo BROWSER=edge >> .env ; \ echo VIDEO_FILE_NAME=edge_video.mp4 >> .env ; \ @@ -498,16 +425,56 @@ test_video: video hub chrome firefox edge done # Using ffmpeg to verify file integrity # https://superuser.com/questions/100288/how-can-i-check-the-integrity-of-a-video-file-avi-mpeg-mp4 - docker run -v $$(pwd):$$(pwd) -w $$(pwd) jrottenberg/ffmpeg:4.3.1-ubuntu2004 -v error -i ./tests/videos/chrome_video.mp4 -f null - 2>error.log - docker run -v $$(pwd):$$(pwd) -w $$(pwd) jrottenberg/ffmpeg:4.3.1-ubuntu2004 -v error -i ./tests/videos/firefox_video.mp4 -f null - 2>error.log - docker run -v $$(pwd):$$(pwd) -w $$(pwd) jrottenberg/ffmpeg:4.3.1-ubuntu2004 -v error -i ./tests/videos/edge_video.mp4 -f null - 2>error.log + docker run -v $$(pwd):$$(pwd) -w $$(pwd) $(FFMPEG_BASED_NAME)/ffmpeg:$(FFMPEG_BASED_TAG) -v error -i ./tests/videos/chrome_video.mp4 -f null - 2>error.log + docker run -v $$(pwd):$$(pwd) -w $$(pwd) $(FFMPEG_BASED_NAME)/ffmpeg:$(FFMPEG_BASED_TAG) -v error -i ./tests/videos/firefox_video.mp4 -f null - 2>error.log + docker run -v $$(pwd):$$(pwd) -w $$(pwd) $(FFMPEG_BASED_NAME)/ffmpeg:$(FFMPEG_BASED_TAG) -v error -i ./tests/videos/edge_video.mp4 -f null - 2>error.log + docker run -v $$(pwd):$$(pwd) -w $$(pwd) $(FFMPEG_BASED_NAME)/ffmpeg:$(FFMPEG_BASED_TAG) -v error -i ./tests/videos/chromium_video.mp4 -f null - 2>error.log + +chart_setup_env: + ./tests/charts/make/chart_setup_env.sh + +chart_cluster_setup: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/charts/make/chart_cluster_setup.sh + +chart_cluster_cleanup: + ./tests/charts/make/chart_cluster_cleanup.sh + +chart_build: + VERSION=$(TAG_VERSION) ./tests/charts/make/chart_build.sh + +chart_test: chart_test_template \ + chart_test_chrome \ + chart_test_firefox \ + chart_test_edge \ + chart_test_chromium + +chart_test_template: + ./tests/charts/bootstrap.sh + +chart_test_chrome: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/charts/make/chart_test.sh NodeChrome + +chart_test_chromium: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/charts/make/chart_test.sh NodeChromium + +chart_test_firefox: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/charts/make/chart_test.sh NodeFirefox + +chart_test_edge: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/charts/make/chart_test.sh NodeEdge + +chart_test_parallel_autoscaling: + VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/charts/make/chart_test.sh ParallelAutoscaling .PHONY: \ all \ + all_amd64 \ + all_arm64 \ base \ build \ ci \ chrome \ + chromium \ edge \ firefox \ docker \ @@ -520,10 +487,13 @@ test_video: video hub chrome firefox edge node_base \ release \ standalone_chrome \ + standalone_chromium \ standalone_edge \ standalone_firefox \ standalone_docker \ tag_latest \ tag_and_push_browser_images \ test \ + test_amd64 \ + test_arm64 \ video diff --git a/NodeBase/Dockerfile b/NodeBase/Dockerfile index dda04b9c8..b5770fb0a 100644 --- a/NodeBase/Dockerfile +++ b/NodeBase/Dockerfile @@ -4,13 +4,16 @@ ARG AUTHORS FROM ${NAMESPACE}/base:${VERSION} LABEL authors=${AUTHORS} +ARG NOVNC_VERSION="1.4.0" +ARG WEBSOCKIFY_VERSION="0.11.0" + USER root #============== # Xvfb #============== RUN apt-get update -qqy \ - && apt-get -qqy install \ + && apt-get -qqy --no-install-recommends install \ xvfb \ pulseaudio \ && rm -rf /var/lib/apt/lists/* /var/cache/apt/* @@ -27,7 +30,7 @@ ENV LANG ${LANGUAGE} # Layer size: small: ~9 MB MB (with --no-install-recommends) RUN apt-get -qqy update \ && apt-get -qqy --no-install-recommends install \ - #language-pack-en \ + language-pack-en \ tzdata \ locales \ && locale-gen ${LANGUAGE} \ @@ -40,7 +43,7 @@ RUN apt-get -qqy update \ # VNC #===== RUN apt-get update -qqy \ - && apt-get -qqy install \ + && apt-get -qqy --no-install-recommends install \ x11vnc \ && rm -rf /var/lib/apt/lists/* /var/cache/apt/* @@ -49,7 +52,7 @@ RUN apt-get update -qqy \ # A fast, lightweight and responsive window manager #========= RUN apt-get update -qqy \ - && apt-get -qqy install \ + && apt-get -qqy --no-install-recommends install \ fluxbox \ && rm -rf /var/lib/apt/lists/* /var/cache/apt/* @@ -64,7 +67,7 @@ RUN apt-get update -qqy \ # fonts-ipafont-gothic ~13 MB # fonts-wqy-zenhei ~17 MB # fonts-tlwg-loma-otf ~300 KB -# ttf-ubuntu-font-family ~5 MB +# fonts-ubuntu ~5 MB # Ubuntu Font Family, sans-serif typeface hinted for clarity # Removed packages: # xfonts-100dpi ~6 MB @@ -84,7 +87,7 @@ RUN apt-get -qqy update \ fonts-ipafont-gothic \ fonts-wqy-zenhei \ fonts-tlwg-loma-otf \ - # ttf-ubuntu-font-family \ + fonts-ubuntu \ fonts-noto-color-emoji \ && rm -rf /var/lib/apt/lists/* \ && apt-get -qyy clean @@ -92,8 +95,6 @@ RUN apt-get -qqy update \ ######################################## # noVNC exposes VNC through a web page # ######################################## -ENV NOVNC_VERSION="1.4.0" \ - WEBSOCKIFY_VERSION="0.11.0" RUN wget -nv -O noVNC.zip \ "https://github.com/novnc/noVNC/archive/refs/tags/v${NOVNC_VERSION}.zip" \ && unzip -x noVNC.zip \ @@ -104,24 +105,38 @@ RUN wget -nv -O noVNC.zip \ "https://github.com/novnc/websockify/archive/refs/tags/v${WEBSOCKIFY_VERSION}.zip" \ && unzip -x websockify.zip \ && rm websockify.zip \ - && rm -rf websockify-${WEBSOCKIFY_VERSION}/tests \ - && mv websockify-${WEBSOCKIFY_VERSION} /opt/bin/noVNC/utils/websockify + && mv websockify-${WEBSOCKIFY_VERSION} /opt/bin/noVNC/utils/websockify \ + && rm -rf /opt/bin/noVNC/utils/websockify/docker /opt/bin/noVNC/utils/websockify/tests #========================================================================================================================================= # Run this command for executable file permissions for /dev/shm when this is a "child" container running in Docker Desktop and WSL2 distro #========================================================================================================================================= RUN chmod +x /dev/shm +# Creating base directory for Xvfb +RUN mkdir -p /tmp/.X11-unix && \ + fix-permissions /tmp/.X11-unix + +#============================== +# Generating the VNC password using initial password in Base image +# Changing ownership to ${SEL_USER}, so the service can be started +#============================== + +RUN mkdir -p ${HOME}/.vnc \ + && x11vnc -storepasswd $(cat ${SEL_DIR}/initialPasswd) ${HOME}/.vnc/passwd \ + && chown -R "${SEL_UID}:${SEL_GID}" ${HOME}/.vnc \ + && fix-permissions ${HOME}/.vnc + #=================================================== # Run the following commands as non-privileged user #=================================================== -USER 1200 +USER ${SEL_UID} #============================== # Scripts to run Selenium Node and XVFB #============================== -COPY start-selenium-node.sh \ +COPY --chown="${SEL_UID}:${SEL_GID}" start-selenium-node.sh \ start-xvfb.sh \ /opt/bin/ @@ -130,30 +145,10 @@ COPY start-selenium-node.sh \ #============================== COPY selenium.conf /etc/supervisor/conf.d/ -#============================== -# Generating the VNC password as seluser -# So the service can be started with seluser -#============================== - -RUN mkdir -p ${HOME}/.vnc \ - && x11vnc -storepasswd secret ${HOME}/.vnc/passwd - -# Added to avoid the following sudo error while running multi-arch builds: -# "sudo: effective uid is not 0, is /usr/bin/sudo on a file system with -# the 'nosuid' option set or an NFS file system without root privileges?" -USER root - -#========== -# Relaxing permissions for OpenShift and other non-sudo environments -#========== -RUN sudo chmod -R 775 ${HOME} \ - && sudo chgrp -R 0 ${HOME} \ - && sudo chmod -R g=u ${HOME} - #============================== # Scripts to run fluxbox, x11vnc and noVNC #============================== -COPY start-vnc.sh \ +COPY --chown="${SEL_UID}:${SEL_GID}" start-vnc.sh \ start-novnc.sh \ /opt/bin/ @@ -185,8 +180,6 @@ ENV SE_DRAIN_AFTER_SESSION_COUNT 0 # Setting Selenium Manager to work offline ENV SE_OFFLINE true - - #======================== # Selenium Configuration #======================== @@ -200,16 +193,7 @@ ENV SE_NODE_OVERRIDE_MAX_SESSIONS false # Following line fixes https://github.com/SeleniumHQ/docker-selenium/issues/87 ENV DBUS_SESSION_BUS_ADDRESS=/dev/null -# Added to avoid sudo-related multi-arch build errors -USER root - -# Creating base directory for Xvfb -RUN mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix - -# Added to avoid sudo-related multi-arch build errors -USER 1200 - # Copying configuration script generator -COPY generate_config /opt/bin/generate_config +COPY --chown="${SEL_UID}:${SEL_GID}" generate_config /opt/bin/generate_config EXPOSE 5900 diff --git a/NodeBase/generate_config b/NodeBase/generate_config index 953743592..4e5276666 100755 --- a/NodeBase/generate_config +++ b/NodeBase/generate_config @@ -52,19 +52,11 @@ echo "max-sessions = ${SE_NODE_MAX_SESSIONS} " >> "$FILENAME" SE_NODE_BROWSER_NAME=$(cat /opt/selenium/browser_name) -if [[ "${SE_NODE_BROWSER_NAME}" == "chrome" ]]; then - SE_NODE_BROWSER_VERSION=$(short_version $(google-chrome --version | awk '{print $3}')) -elif [[ "${SE_NODE_BROWSER_NAME}" == "chromium" ]]; then - SE_NODE_BROWSER_NAME=chrome - SE_NODE_BROWSER_VERSION=$(short_version $(chromium --version | awk '{print $2}')) -elif [[ "${SE_NODE_BROWSER_NAME}" == "firefox" ]]; then - SE_NODE_BROWSER_VERSION=$(short_version $(firefox --version | awk '{print $3}')) -elif [[ "${SE_NODE_BROWSER_NAME}" == "MicrosoftEdge" ]]; then - SE_NODE_BROWSER_VERSION=$(short_version $(microsoft-edge --version | awk '{print $3}')) -fi +SE_NODE_BROWSER_VERSION=$(short_version $(cat /opt/selenium/browser_version)) +SE__BROWSER_BINARY_LOCATION=$(cat /opt/selenium/browser_binary_location) if [[ -z "$SE_NODE_STEREOTYPE" ]]; then -SE_NODE_STEREOTYPE="{\"browserName\": \"${SE_NODE_BROWSER_NAME}\", \"browserVersion\": \"${SE_NODE_BROWSER_VERSION}\", \"platformName\": \"Linux\"}" +SE_NODE_STEREOTYPE="{\"browserName\": \"${SE_NODE_BROWSER_NAME}\", \"browserVersion\": \"${SE_NODE_BROWSER_VERSION}\", \"platformName\": \"Linux\", ${SE__BROWSER_BINARY_LOCATION}}" else SE_NODE_STEREOTYPE="$SE_NODE_STEREOTYPE" fi diff --git a/NodeBase/start-selenium-node.sh b/NodeBase/start-selenium-node.sh index fc5dc2f55..4b069c7dd 100755 --- a/NodeBase/start-selenium-node.sh +++ b/NodeBase/start-selenium-node.sh @@ -31,16 +31,22 @@ if [ ! -z "$SE_OPTS" ]; then echo "Appending Selenium options: ${SE_OPTS}" fi +if [ ! -z "$SE_NODE_SESSION_TIMEOUT" ]; then + SE_OPTS="$SE_OPTS --session-timeout $SE_NODE_SESSION_TIMEOUT" + echo "Appending Selenium node session timeout via SE_OPTS: ${SE_OPTS}" +fi + if [ "$GENERATE_CONFIG" = true ]; then echo "Generating Selenium Config" /opt/bin/generate_config fi -EXTRA_LIBS="/opt/selenium/selenium-http-jdk-client.jar" +EXTRA_LIBS="" if [ ! -z "$SE_ENABLE_TRACING" ]; then EXTERNAL_JARS=$(/dev/null \ && echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \ && apt-get update -qqy \ - && apt-get -qqy install \ + && apt-get -qqy --no-install-recommends install \ ${CHROME_VERSION:-google-chrome-stable} \ && rm /etc/apt/sources.list.d/google-chrome.list \ && rm -rf /var/lib/apt/lists/* /var/cache/apt/* @@ -29,8 +29,6 @@ RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key COPY wrap_chrome_binary /opt/bin/wrap_chrome_binary RUN /opt/bin/wrap_chrome_binary -USER 1200 - #============================================ # Chrome webdriver #============================================ @@ -49,13 +47,16 @@ RUN if [ ! -z "$CHROME_DRIVER_VERSION" ]; \ && echo "Using ChromeDriver version: "$CHROME_DRIVER_VERSION \ && wget --no-verbose -O /tmp/chromedriver_linux64.zip $CHROME_DRIVER_URL \ && rm -rf /opt/selenium/chromedriver \ - && sudo unzip /tmp/chromedriver_linux64.zip -d /opt/selenium \ + && unzip /tmp/chromedriver_linux64.zip -d /opt/selenium \ && rm /tmp/chromedriver_linux64.zip \ - && sudo mv /opt/selenium/chromedriver-linux64/chromedriver /opt/selenium/chromedriver-$CHROME_DRIVER_VERSION \ - && sudo chmod 755 /opt/selenium/chromedriver-$CHROME_DRIVER_VERSION \ - && sudo ln -fs /opt/selenium/chromedriver-$CHROME_DRIVER_VERSION /usr/bin/chromedriver + && mv /opt/selenium/chromedriver-linux64/chromedriver /opt/selenium/chromedriver-$CHROME_DRIVER_VERSION \ + && ln -fs /opt/selenium/chromedriver-$CHROME_DRIVER_VERSION /usr/bin/chromedriver + +USER ${SEL_UID} #============================================ -# Dumping Browser name and version for config +# Dumping Browser information for config #============================================ RUN echo "chrome" > /opt/selenium/browser_name +RUN google-chrome --version | awk '{print $3}' > /opt/selenium/browser_version +RUN echo "\"goog:chromeOptions\": {\"binary\": \"/usr/bin/google-chrome\"}" > /opt/selenium/browser_binary_location diff --git a/NodeChromium/Dockerfile b/NodeChromium/Dockerfile index 93e6a9d36..191ba7b8d 100644 --- a/NodeChromium/Dockerfile +++ b/NodeChromium/Dockerfile @@ -6,17 +6,38 @@ LABEL authors=${AUTHORS} USER root -# Install Chromium -# RUN echo "deb http://http.us.debian.org/debian/ stable non-free contrib main" >> /etc/apt/sources.list \ -RUN echo "deb http://deb.debian.org/debian/ sid main" >> /etc/apt/sources.list \ +#============================================ +# Chromium and chromedriver from Debian +# Available versions at: https://packages.debian.org/search?keywords=chromium +#============================================ +COPY chromium.pref /etc/apt/preferences.d/chromium.pref + +ARG DEBIAN_VERSION=bullseye +ARG REPO_FILE=/etc/apt/sources.list.d/debian.list +ARG KEY_SERVER=keyserver.ubuntu.com +ARG DEBIAN_KEY_LOCATION=/usr/share/keyrings/debian +ARG CHROMIUM_VERSION=120.0.6099.109-1~deb11u1 +ARG CHROMEDIVER_VERSION=${CHROMIUM_VERSION} +RUN mkdir /home/seluser/.gnupg && mkdir -p ${DEBIAN_KEY_LOCATION} \ + && gpg --no-default-keyring --keyring ${DEBIAN_KEY_LOCATION}/${DEBIAN_VERSION}.gpg \ + --keyserver ${KEY_SERVER} \ + --recv-keys 0E98404D386FA1D9 \ + && echo "deb [signed-by=${DEBIAN_KEY_LOCATION}/${DEBIAN_VERSION}.gpg] http://deb.debian.org/debian ${DEBIAN_VERSION} main" \ + > ${REPO_FILE} \ + && gpg --no-default-keyring --keyring ${DEBIAN_KEY_LOCATION}/${DEBIAN_VERSION}-updates.gpg \ + --keyserver ${KEY_SERVER} \ + --recv-keys 6ED0E7B82643E131 \ + && echo "deb [signed-by=${DEBIAN_KEY_LOCATION}/${DEBIAN_VERSION}-updates.gpg] http://deb.debian.org/debian ${DEBIAN_VERSION}-updates main" \ + >> ${REPO_FILE} \ + && gpg --no-default-keyring --keyring ${DEBIAN_KEY_LOCATION}/security-${DEBIAN_VERSION}.gpg \ + --keyserver ${KEY_SERVER} \ + --recv-keys 112695A0E562B32A \ + && echo "deb [signed-by=${DEBIAN_KEY_LOCATION}/security-${DEBIAN_VERSION}.gpg] http://deb.debian.org/debian-security ${DEBIAN_VERSION}-security main contrib non-free" \ + >> ${REPO_FILE} \ && apt-get update -qqy \ - # && apt-get -qqy install chromium=89.0.4389.82-1 \ - # && apt-get -qqy install chromium=90.0.4430.212-1 \ - # && apt-get -qqy install chromium=93.0.4577.82-1 \ - # && apt-get -qqy install chromium=97.0.4692.71-0.1 \ - # && apt-get -qqy install chromium=98.0.4758.102-1+b1 \ - && apt-get -qqy install chromium \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* + && apt-get -qqy --no-install-recommends install chromium=${CHROMIUM_VERSION} chromium-driver=${CHROMEDIVER_VERSION} \ + && rm ${REPO_FILE} \ + && rm -rf /home/seluser/.gnupg ${DEBIAN_KEY_LOCATION}/ /var/lib/apt/lists/* /var/cache/apt/* #================================= # Chromium Launch Script Wrapper @@ -24,14 +45,11 @@ RUN echo "deb http://deb.debian.org/debian/ sid main" >> /etc/apt/sources.list \ COPY wrap_chromium_binary /opt/bin/wrap_chromium_binary RUN /opt/bin/wrap_chromium_binary +USER ${SEL_UID} + #============================================ -# Chromium webdriver +# Dumping Browser information for config #============================================ -RUN apt-get update -qqy \ - && apt-get -qqy install chromium-driver \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* - -RUN echo "chromium" > /opt/selenium/browser_name - -USER 1200 - +RUN echo "chrome" > /opt/selenium/browser_name +RUN chromium --version | awk '{print $2}' > /opt/selenium/browser_version +RUN echo "\"goog:chromeOptions\": {\"binary\": \"/usr/bin/chromium\"}" > /opt/selenium/browser_binary_location diff --git a/NodeChromium/chromium.pref b/NodeChromium/chromium.pref new file mode 100644 index 000000000..33fb9ace5 --- /dev/null +++ b/NodeChromium/chromium.pref @@ -0,0 +1,14 @@ +# Note: 2 blank lines are required between entries +Package: * +Pin: release a=eoan +Pin-Priority: 500 + +Package: * +Pin: origin "deb.debian.org" +Pin-Priority: 300 + +# Pattern includes 'chromium', 'chromium-browser' and similarly +# named dependencies: +Package: chromium* +Pin: origin "deb.debian.org" +Pin-Priority: 700 diff --git a/NodeChromium/wrap_chromium_binary b/NodeChromium/wrap_chromium_binary index 0d5947cad..0cf5c2533 100755 --- a/NodeChromium/wrap_chromium_binary +++ b/NodeChromium/wrap_chromium_binary @@ -6,6 +6,21 @@ mv "$WRAPPER_PATH" "$BASE_PATH" cat > "$WRAPPER_PATH" <<_EOF #!/bin/bash + +# umask 002 ensures default permissions of files are 664 (rw-rw-r--) and directories are 775 (rwxrwxr-x). +umask 002 + +# Debian/Ubuntu seems to not respect --lang, it instead needs to be a LANGUAGE environment var +# See: https://stackoverflow.com/a/41893197/359999 +for var in "\$@"; do + if [[ \$var == --lang=* ]]; then + LANGUAGE=\${var//--lang=} + fi +done + +# Set language environment variable +export LANGUAGE="\$LANGUAGE" + # Note: exec -a below is a bashism. exec -a "\$0" "$BASE_PATH" --no-sandbox "\$@" _EOF diff --git a/NodeDocker/Dockerfile b/NodeDocker/Dockerfile index 249b91adc..b65820cec 100644 --- a/NodeDocker/Dockerfile +++ b/NodeDocker/Dockerfile @@ -10,10 +10,10 @@ USER root # Socat to proxy docker.sock when mounted #============== RUN apt-get update -qqy \ - && apt-get -qqy install socat \ + && apt-get -qqy --no-install-recommends install socat \ && rm -rf /var/lib/apt/lists/* /var/cache/apt/* -USER 1200 +USER ${SEL_UID} #======================== # Selenium Standalone Docker Configuration @@ -21,7 +21,7 @@ USER 1200 EXPOSE 4444 -COPY start-selenium-grid-docker.sh \ +COPY --chown="${SEL_UID}:${SEL_GID}" start-selenium-grid-docker.sh \ config.toml \ start-socat.sh \ /opt/bin/ diff --git a/NodeDocker/config.toml b/NodeDocker/config.toml index e15b1e5e6..83c1b640a 100644 --- a/NodeDocker/config.toml +++ b/NodeDocker/config.toml @@ -2,9 +2,10 @@ # Configs have a mapping between the Docker image to use and the capabilities that need to be matched to # start a container with the given image. configs = [ - "selenium/standalone-firefox:4.12.1-20230920", '{"browserName": "firefox", "platformName": "linux"}', - "selenium/standalone-chrome:4.12.1-20230920", '{"browserName": "chrome", "platformName": "linux"}', - "selenium/standalone-edge:4.12.1-20230920", '{"browserName": "MicrosoftEdge", "platformName": "linux"}' + "selenium/standalone-firefox:4.16.1-20231212", '{"browserName": "firefox", "platformName": "linux"}', + "selenium/standalone-chrome:4.16.1-20231212", '{"browserName": "chrome", "platformName": "linux"}', + "selenium/standalone-chromium:4.16.1-20231212", '{"browserName": "chromium", "platformName": "linux"}', + "selenium/standalone-edge:4.16.1-20231212", '{"browserName": "MicrosoftEdge", "platformName": "linux"}' ] # URL for connecting to the docker daemon @@ -14,7 +15,7 @@ configs = [ # socat -4 TCP-LISTEN:2375,fork UNIX-CONNECT:/var/run/docker.sock url = "http://127.0.0.1:2375" # Docker image used for video recording -video-image = "selenium/video:ffmpeg-4.3.1-20230920" +video-image = "selenium/video:ffmpeg-6.1-20231212" # Uncomment the following section if you are running the node on a separate VM # Fill out the placeholders with appropriate values diff --git a/NodeDocker/start-selenium-grid-docker.sh b/NodeDocker/start-selenium-grid-docker.sh index 760c34a85..0220a1ec9 100755 --- a/NodeDocker/start-selenium-grid-docker.sh +++ b/NodeDocker/start-selenium-grid-docker.sh @@ -29,20 +29,21 @@ if [ ! -z "$SE_NODE_GRID_URL" ]; then SE_GRID_URL="--grid-url ${SE_NODE_GRID_URL}" fi -EXTRA_LIBS="/opt/selenium/selenium-http-jdk-client.jar" +EXTRA_LIBS="" if [ ! -z "$SE_ENABLE_TRACING" ]; then EXTERNAL_JARS=$(/dev/null \ && echo "deb https://packages.microsoft.com/repos/edge stable main" >> /etc/apt/sources.list.d/microsoft-edge.list \ && apt-get update -qqy \ - && apt-get -qqy install ${EDGE_VERSION} \ + && apt-get -qqy --no-install-recommends install ${EDGE_VERSION} \ && rm /etc/apt/sources.list.d/microsoft-edge.list \ && rm -rf /var/lib/apt/lists/* /var/cache/apt/* @@ -26,8 +26,6 @@ RUN wget -q -O - https://packages.microsoft.com/keys/microsoft.asc | apt-key add COPY wrap_edge_binary /opt/bin/wrap_edge_binary RUN /opt/bin/wrap_edge_binary -USER 1200 - #============================================ # Edge webdriver #============================================ @@ -45,10 +43,13 @@ RUN if [ -z "$EDGE_DRIVER_VERSION" ]; \ && unzip /tmp/msedgedriver_linux64.zip -d /opt/selenium \ && rm /tmp/msedgedriver_linux64.zip \ && mv /opt/selenium/msedgedriver /opt/selenium/msedgedriver-$EDGE_DRIVER_VERSION \ - && chmod 755 /opt/selenium/msedgedriver-$EDGE_DRIVER_VERSION \ - && sudo ln -fs /opt/selenium/msedgedriver-$EDGE_DRIVER_VERSION /usr/bin/msedgedriver + && ln -fs /opt/selenium/msedgedriver-$EDGE_DRIVER_VERSION /usr/bin/msedgedriver + +USER ${SEL_UID} #============================================ -# Dumping Browser name and version for config +# Dumping Browser information for config #============================================ RUN echo "MicrosoftEdge" > /opt/selenium/browser_name +RUN microsoft-edge --version | awk '{print $3}' > /opt/selenium/browser_version +RUN echo "\"ms:edgeOptions\": {\"binary\": \"/usr/bin/microsoft-edge\"}" > /opt/selenium/browser_binary_location diff --git a/NodeFirefox/Dockerfile b/NodeFirefox/Dockerfile index 0590f1678..39c946189 100644 --- a/NodeFirefox/Dockerfile +++ b/NodeFirefox/Dockerfile @@ -8,19 +8,15 @@ USER root #========= # Firefox +# Available versions at https://launchpad.net/~mozillateam/+archive/ubuntu/ppa #========= -ARG FIREFOX_VERSION=latest -RUN FIREFOX_DOWNLOAD_URL=$(if [ $FIREFOX_VERSION = "latest" ] || [ $FIREFOX_VERSION = "beta-latest" ] || [ $FIREFOX_VERSION = "nightly-latest" ] || [ $FIREFOX_VERSION = "devedition-latest" ] || [ $FIREFOX_VERSION = "esr-latest" ]; then echo "https://download.mozilla.org/?product=firefox-$FIREFOX_VERSION-ssl&os=linux64&lang=en-US"; else echo "https://download-installer.cdn.mozilla.net/pub/firefox/releases/$FIREFOX_VERSION/linux-x86_64/en-US/firefox-$FIREFOX_VERSION.tar.bz2"; fi) \ - && apt-get update -qqy \ - && apt-get -qqy --no-install-recommends install firefox libavcodec-extra \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* \ - && wget --no-verbose -O /tmp/firefox.tar.bz2 $FIREFOX_DOWNLOAD_URL \ - && apt-get -y purge firefox \ - && rm -rf /opt/firefox \ - && tar -C /opt -xjf /tmp/firefox.tar.bz2 \ - && rm /tmp/firefox.tar.bz2 \ - && mv /opt/firefox /opt/firefox-$FIREFOX_VERSION \ - && ln -fs /opt/firefox-$FIREFOX_VERSION/firefox /usr/bin/firefox +ARG FIREFOX_VERSION=121.0+build1-0ubuntu0.22.04.1~mt1 +RUN apt-get -qqy update \ + && apt-get -qqy --no-install-recommends install libavcodec-extra software-properties-common \ + && add-apt-repository ppa:mozillateam/ppa \ + && apt-get -qqy update \ + && apt-get -qqy --no-install-recommends install -t 'o=LP-PPA-mozillateam' firefox=${FIREFOX_VERSION} \ + && rm -rf /var/lib/apt/lists/* /var/cache/apt/* #============ # GeckoDriver @@ -28,7 +24,13 @@ RUN FIREFOX_DOWNLOAD_URL=$(if [ $FIREFOX_VERSION = "latest" ] || [ $FIREFOX_VERS ARG GECKODRIVER_VERSION=latest RUN GK_VERSION=$(if [ ${GECKODRIVER_VERSION:-latest} = "latest" ]; then echo "0.33.0"; else echo $GECKODRIVER_VERSION; fi) \ && echo "Using GeckoDriver version: "$GK_VERSION \ - && wget --no-verbose -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GK_VERSION/geckodriver-v$GK_VERSION-linux64.tar.gz \ + && ARCH=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \ + && if [ "$ARCH" = "arm64" ]; then \ + GK_DOWNLOAD_URL="https://github.com/mozilla/geckodriver/releases/download/v$GK_VERSION/geckodriver-v$GK_VERSION-linux-aarch64.tar.gz" ; \ + else \ + GK_DOWNLOAD_URL="https://github.com/mozilla/geckodriver/releases/download/v$GK_VERSION/geckodriver-v$GK_VERSION-linux64.tar.gz" ; \ + fi \ + && wget --no-verbose -O /tmp/geckodriver.tar.gz $GK_DOWNLOAD_URL \ && rm -rf /opt/geckodriver \ && tar -C /opt -zxf /tmp/geckodriver.tar.gz \ && rm /tmp/geckodriver.tar.gz \ @@ -36,9 +38,11 @@ RUN GK_VERSION=$(if [ ${GECKODRIVER_VERSION:-latest} = "latest" ]; then echo "0. && chmod 755 /opt/geckodriver-$GK_VERSION \ && ln -fs /opt/geckodriver-$GK_VERSION /usr/bin/geckodriver -USER 1200 +USER ${SEL_UID} #============================================ -# Dumping Browser name and version for config +# Dumping Browser information for config #============================================ RUN echo "firefox" > /opt/selenium/browser_name +RUN firefox --version | awk '{print $3}' > /opt/selenium/browser_version +RUN echo "\"moz:firefoxOptions\": {\"binary\": \"/usr/bin/firefox\"}" > /opt/selenium/browser_binary_location diff --git a/README.md b/README.md index 12a4d86ed..ec9e6b535 100644 --- a/README.md +++ b/README.md @@ -1,94 +1,6 @@ -# Docker images for Selenium, built for Debian ARM64, ARM/v7, and AMD64 - -[![seleniumhq-community](https://circleci.com/gh/seleniumhq-community/docker-seleniarm.svg?style=shield)](https://app.circleci.com/pipelines/github/seleniumhq-community/docker-seleniarm) - -This is a fork of [SeleniumHQ/docker-selenium](https://github.com/SeleniumHQ/docker-selenium) for building and maintaining docker-selenium ARM images. This fork is inspired by and based on changes from [sj26/docker-selenium](https://github.com/sj26/docker-selenium) and [rows/docker-selenium](https://github.com/rows/docker-selenium). - -NOTE: If you only need the Intel/amd64 images, please see the official upstream [SeleniumHQ/docker-selenium](https://github.com/SeleniumHQ/docker-selenium) repository for best results. - -## Running the ARM Container Images - -The primary motivation for creating this fork and updating the images was so I can use the noVNC client on the latest Selenium versions on the Mac M1, an arm64 architecture. To use noVNC, make sure you open port 7900, and visit localhost:7900 in your browser. - -The images are also successfully tested on AWS graviton nodes, resulting in better price-performance ratio and lower carbon footprint. - -To start the standalone container images, run: - -![Chromium](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chromium/chromium_24x24.png) Chromium -``` -$ docker run --rm -it -p 4444:4444 -p 5900:5900 -p 7900:7900 --shm-size 2g seleniarm/standalone-chromium:latest -``` -> NOTE: Google does not build Chrome for Linux ARM platforms. Instead, docker-seleniarm uses the open source Chromium browser instead, which _is_ built for ARM. - -![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png) Firefox -``` -$ docker run --rm -it -p 4444:4444 -p 5900:5900 -p 7900:7900 --shm-size 2g seleniarm/standalone-firefox:latest -``` - -Use your traditional VNC client via port 5900, and noVNC in the browser via port 7900. - -The following multi-arch Seleniarm container images are available on [Docker Hub](https://hub.docker.com/u/seleniarm): - -- [Standalone Chromium](https://hub.docker.com/r/seleniarm/standalone-chromium) -- [Standalone Firefox](https://hub.docker.com/r/seleniarm/standalone-firefox) -- [Node Chromium](https://hub.docker.com/r/seleniarm/node-chromium) -- [Node Firefox](https://hub.docker.com/r/seleniarm/node-firefox) -- [Selenium Hub](https://hub.docker.com/r/seleniarm/hub) -- [Distributor](https://hub.docker.com/r/seleniarm/distributor) -- [Router](https://hub.docker.com/r/seleniarm/router) -- [Node Docker](https://hub.docker.com/r/seleniarm/node-docker) -- [Standalone Docker](https://hub.docker.com/r/seleniarm/standalone-docker) -- [Event Bus](https://hub.docker.com/r/seleniarm/event-bus) -- [Session Queue](https://hub.docker.com/r/seleniarm/session-queue) -- [Sessions](https://hub.docker.com/r/seleniarm/sessions) -- [NodeBase](https://hub.docker.com/r/seleniarm/node-base) -- [Base](https://hub.docker.com/r/seleniarm/base) - - -All of the images are here in order to run in standalone mode, full grid mode, and dynamic grid mode. However, browser binaries are only available for Chromium and Firefox. - -> NOTE: Google does not build Chrome for ARM on Linux. Instead, we use Chromium ARM. - -## Building the ARM Images - -The entire build process is managed via a Makefile. If you want to build the images locally, without pushing to any registry, then use `make`. - -### Structure of container images: - -- The Standalone folder is the base for all Standalone${browser} images and includes a script that starts the selenium server in standalone mode. -- The NodeBase folder is the base for all Node${browser} images and includes a script that starts the selenium server in node mode. - -To build with a different version of Chromium, change it in NodeChromium/Dockerfile. - -To build the images, run the following make command from the root directory of this repo, and specify your architecture, either arm64, arm/v7, or amd64: - -**To build all arm64 images:** -``` -$ NAME=local-seleniarm VERSION=4.5.0 BUILD_DATE=$(date '+%Y%m%d') PLATFORMS=linux/arm64 BUILD_ARGS=--load make build_multi -``` - -**To build standalone/firefox for arm64:** - -``` -$ NAME=local-seleniarm VERSION=4.5.0 BUILD_DATE=$(date '+%Y%m%d') PLATFORMS=linux/arm64 BUILD_ARGS=--load make standalone_firefox_multi -``` - -**To build standalone/chromium for arm64:** - -``` -$ NAME=local-seleniarm VERSION=4.5.0 BUILD_DATE=$(date '+%Y%m%d') PLATFORMS=linux/arm64 BUILD_ARGS=--load make standalone_chromium_multi -``` - -To build for armv7l/armhf, replace PLATFORMS environment variable with `linux/arm/v7` like so: - -``` -$ NAME=local-seleniarm VERSION=4.5.0 BUILD_DATE=$(date '+%Y%m%d') PLATFORMS=linux/arm/v7 BUILD_ARGS=--load make standalone_chromium_multi -``` - ----- -# -- The official documentation from seleniumHQ begins here -- ----- - +![Build & test](https://github.com/SeleniumHQ/docker-selenium/workflows/Build%20&%20test/badge.svg?branch=trunk) +![Deployments](https://github.com/SeleniumHQ/docker-selenium/workflows/Deploys/badge.svg) +![Helm Charts](https://github.com/SeleniumHQ/docker-selenium/workflows/Lint%20and%20Test%20Helm%20Charts/badge.svg) # Docker images for the Selenium Grid Server @@ -96,7 +8,7 @@ The project is made possible by volunteer contributors who have put in thousands and made the source code freely available under the [Apache License 2.0](LICENSE.md). These Docker images come with a handful of tags to simplify its usage, have a look at them in one of -our [releases](https://github.com/SeleniumHQ/docker-selenium/releases/tag/4.12.1-20230920). +our [releases](https://github.com/SeleniumHQ/docker-selenium/releases/tag/4.16.1-20231212). To get notifications of new releases, add yourself as a "Releases only" watcher. @@ -153,7 +65,7 @@ Talk to us at https://www.selenium.dev/support/ 1. Start a Docker container with Firefox ```bash -docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" selenium/standalone-firefox:4.12.1-20230920 +docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" selenium/standalone-firefox:4.16.1-20231212 ``` 2. Point your WebDriver tests to http://localhost:4444 @@ -340,17 +252,17 @@ For more information on the Dev and Beta channel container images, see the blog ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png) Firefox ```bash -docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-firefox:4.12.1-20230920 +docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-firefox:4.16.1-20231212 ``` ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png) Chrome ```bash -docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-chrome:4.12.1-20230920 +docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-chrome:4.16.1-20231212 ``` ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge_24x24.png) Edge ```bash -docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-edge:4.12.1-20230920 +docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-edge:4.16.1-20231212 ``` _Note: Only one Standalone container can run on port_ `4444` _at the same time._ @@ -369,44 +281,44 @@ A Docker [network](https://docs.docker.com/engine/reference/commandline/network_ ```bash $ docker network create grid -$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.12.1-20230920 +$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-chrome:4.12.1-20230920 + selenium/node-chrome:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-edge:4.12.1-20230920 + selenium/node-edge:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-firefox:4.12.1-20230920 + selenium/node-firefox:4.16.1-20231212 ``` ##### Windows PowerShell ```powershell $ docker network create grid -$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.12.1-20230920 +$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub ` --shm-size="2g" ` -e SE_EVENT_BUS_PUBLISH_PORT=4442 ` -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 ` - selenium/node-chrome:4.12.1-20230920 + selenium/node-chrome:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub ` --shm-size="2g" ` -e SE_EVENT_BUS_PUBLISH_PORT=4442 ` -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 ` - selenium/node-edge:4.12.1-20230920 + selenium/node-edge:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub ` --shm-size="2g" ` -e SE_EVENT_BUS_PUBLISH_PORT=4442 ` -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 ` - selenium/node-firefox:4.12.1-20230920 + selenium/node-firefox:4.16.1-20231212 ``` When you are done using the Grid, and the containers have exited, the network can be removed with the following command: @@ -423,7 +335,7 @@ configured to expose different ports. ##### Hub - Machine/VM 1 ```bash -$ docker run -d -p 4442-4444:4442-4444 --name selenium-hub selenium/hub:4.12.1-20230920 +$ docker run -d -p 4442-4444:4442-4444 --name selenium-hub selenium/hub:4.16.1-20231212 ``` ##### Node Chrome - Machine/VM 2 @@ -437,7 +349,7 @@ $ docker run -d -p 5555:5555 \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -e SE_NODE_HOST= \ - selenium/node-chrome:4.12.1-20230920 + selenium/node-chrome:4.16.1-20231212 ``` ###### Windows PowerShell @@ -449,7 +361,7 @@ $ docker run -d -p 5555:5555 ` -e SE_EVENT_BUS_PUBLISH_PORT=4442 ` -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 ` -e SE_NODE_HOST= ` - selenium/node-chrome:4.12.1-20230920 + selenium/node-chrome:4.16.1-20231212 ``` @@ -464,7 +376,7 @@ $ docker run -d -p 5555:5555 \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -e SE_NODE_HOST= \ - selenium/node-edge:4.12.1-20230920 + selenium/node-edge:4.16.1-20231212 ``` ###### Windows PowerShell @@ -476,7 +388,7 @@ $ docker run -d -p 5555:5555 ` -e SE_EVENT_BUS_PUBLISH_PORT=4442 ` -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 ` -e SE_NODE_HOST= ` - selenium/node-edge:4.12.1-20230920 + selenium/node-edge:4.16.1-20231212 ``` ##### Node Firefox - Machine/VM 4 @@ -490,7 +402,7 @@ $ docker run -d -p 5555:5555 \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -e SE_NODE_HOST= \ - selenium/node-firefox:4.12.1-20230920 + selenium/node-firefox:4.16.1-20231212 ``` ###### Windows PowerShell @@ -502,7 +414,7 @@ $ docker run -d -p 5555:5555 ` -e SE_EVENT_BUS_PUBLISH_PORT=4442 ` -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 ` -e SE_NODE_HOST= ` - selenium/node-firefox:4.12.1-20230920 + selenium/node-firefox:4.16.1-20231212 ``` ##### Node Chrome - Machine/VM 4 @@ -517,7 +429,7 @@ $ docker run -d -p 5556:5556 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -e SE_NODE_HOST= \ -e SE_NODE_PORT=5556 \ - selenium/node-chrome:4.12.1-20230920 + selenium/node-chrome:4.16.1-20231212 ``` ###### Windows PowerShell @@ -530,7 +442,7 @@ $ docker run -d -p 5556:5556 ` -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 ` -e SE_NODE_HOST= ` -e SE_NODE_PORT=5556 ` - selenium/node-chrome:4.12.1-20230920 + selenium/node-chrome:4.16.1-20231212 ``` #### Docker Compose @@ -562,7 +474,7 @@ ___ ## Video recording -Tests execution can be recorded by using the `selenium/video:ffmpeg-4.3.1-20230920` +Tests execution can be recorded by using the `selenium/video:ffmpeg-6.1-20231212` Docker image. One container is needed per each container where a browser is running. This means if you are running 5 Nodes/Standalone containers, you will need 5 video containers, the mapping is 1-1. @@ -588,8 +500,8 @@ This example shows how to start the containers manually: ``` bash $ docker network create grid -$ docker run -d -p 4444:4444 -p 6900:5900 --net grid --name selenium --shm-size="2g" selenium/standalone-chrome:4.12.1-20230920 -$ docker run -d --net grid --name video -v /tmp/videos:/videos selenium/video:ffmpeg-4.3.1-20230920 +$ docker run -d -p 4444:4444 -p 6900:5900 --net grid --name selenium --shm-size="2g" selenium/standalone-chrome:4.16.1-20231212 +$ docker run -d --net grid --name video -v /tmp/videos:/videos selenium/video:ffmpeg-6.1-20231212 # Run your tests $ docker stop video && docker rm video $ docker stop selenium && docker rm selenium @@ -621,9 +533,9 @@ You can save this file locally and name it, for example, `config.toml`. # Configs have a mapping between the Docker image to use and the capabilities that need to be matched to # start a container with the given image. configs = [ - "selenium/standalone-firefox:4.12.1-20230920", '{"browserName": "firefox"}', - "selenium/standalone-chrome:4.12.1-20230920", '{"browserName": "chrome"}', - "selenium/standalone-edge:4.12.1-20230920", '{"browserName": "MicrosoftEdge"}' + "selenium/standalone-firefox:4.16.1-20231212", '{"browserName": "firefox"}', + "selenium/standalone-chrome:4.16.1-20231212", '{"browserName": "chrome"}', + "selenium/standalone-edge:4.16.1-20231212", '{"browserName": "MicrosoftEdge"}' ] # URL for connecting to the docker daemon @@ -636,7 +548,7 @@ configs = [ # Linux: varies from machine to machine, please mount /var/run/docker.sock. If this does not work, please create an issue. url = "http://127.0.0.1:2375" # Docker image used for video recording -video-image = "selenium/video:ffmpeg-4.3.1-20230920" +video-image = "selenium/video:ffmpeg-6.1-20231212" # Uncomment the following section if you are running the node on a separate VM # Fill out the placeholders with appropriate values @@ -655,28 +567,28 @@ virtual machines. ```bash $ docker network create grid -$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.12.1-20230920 +$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -v ${PWD}/config.toml:/opt/bin/config.toml \ -v ${PWD}/assets:/opt/selenium/assets \ -v /var/run/docker.sock:/var/run/docker.sock \ - selenium/node-docker:4.12.1-20230920 + selenium/node-docker:4.16.1-20231212 ``` #### Windows PowerShell ```powershell $ docker network create grid -$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.12.1-20230920 +$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub ` -e SE_EVENT_BUS_PUBLISH_PORT=4442 ` -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 ` -v ${PWD}/config.toml:/opt/bin/config.toml ` -v ${PWD}/assets:/opt/selenium/assets ` -v /var/run/docker.sock:/var/run/docker.sock ` - selenium/node-docker:4.12.1-20230920 + selenium/node-docker:4.16.1-20231212 ``` To have the assets saved on your host, please mount your host path to `/opt/selenium/assets`. @@ -697,7 +609,7 @@ docker run --rm --name selenium-docker -p 4444:4444 \ -v ${PWD}/config.toml:/opt/bin/config.toml \ -v ${PWD}/assets:/opt/selenium/assets \ -v /var/run/docker.sock:/var/run/docker.sock \ - selenium/standalone-docker:4.12.1-20230920 + selenium/standalone-docker:4.16.1-20231212 ``` #### Windows PowerShell @@ -707,7 +619,7 @@ docker run --rm --name selenium-docker -p 4444:4444 ` -v ${PWD}/config.toml:/opt/bin/config.toml ` -v ${PWD}/assets:/opt/selenium/assets ` -v /var/run/docker.sock:/var/run/docker.sock ` - selenium/standalone-docker:4.12.1-20230920 + selenium/standalone-docker:4.16.1-20231212 ``` ### Using Dynamic Grid in different machines/VMs @@ -715,7 +627,7 @@ docker run --rm --name selenium-docker -p 4444:4444 ` #### Hub - Machine/VM 1 ```bash -$ docker run -d -p 4442-4444:4442-4444 --name selenium-hub selenium/hub:4.12.1-20230920 +$ docker run -d -p 4442-4444:4442-4444 --name selenium-hub selenium/hub:4.16.1-20231212 ``` #### Node Chrome - Machine/VM 2 @@ -730,7 +642,7 @@ $ docker run -d -p 5555:5555 \ -v ${PWD}/config.toml:/opt/bin/config.toml \ -v ${PWD}/assets:/opt/selenium/assets \ -v /var/run/docker.sock:/var/run/docker.sock \ - selenium/node-docker:4.12.1-20230920 + selenium/node-docker:4.16.1-20231212 ``` #### Windows PowerShell @@ -743,7 +655,7 @@ $ docker run -d -p 5555:5555 ` -v ${PWD}/config.toml:/opt/bin/config.toml ` -v ${PWD}/assets:/opt/selenium/assets ` -v /var/run/docker.sock:/var/run/docker.sock ` - selenium/node-docker:4.12.1-20230920 + selenium/node-docker:4.16.1-20231212 ``` Complete the `[server]` section in the `config.toml` file. @@ -752,9 +664,9 @@ Complete the `[server]` section in the `config.toml` file. # Configs have a mapping between the Docker image to use and the capabilities that need to be matched to # start a container with the given image. configs = [ - "selenium/standalone-firefox:4.12.1-20230920", "{\"browserName\": \"firefox\"}", - "selenium/standalone-chrome:4.12.1-20230920", "{\"browserName\": \"chrome\"}", - "selenium/standalone-edge:4.12.1-20230920", "{\"browserName\": \"MicrosoftEdge\"}" + "selenium/standalone-firefox:4.16.1-20231212", "{\"browserName\": \"firefox\"}", + "selenium/standalone-chrome:4.16.1-20231212", "{\"browserName\": \"chrome\"}", + "selenium/standalone-edge:4.16.1-20231212", "{\"browserName\": \"MicrosoftEdge\"}" ] # URL for connecting to the docker daemon @@ -767,7 +679,7 @@ configs = [ # Linux: varies from machine to machine, please mount /var/run/docker.sock. If this does not work, please create an issue. url = "http://127.0.0.1:2375" # Docker image used for video recording -video-image = "selenium/video:ffmpeg-4.3.1-20230920" +video-image = "selenium/video:ffmpeg-6.1-20231212" # Uncomment the following section if you are running the node on a separate VM # Fill out the placeholders with appropriate values @@ -801,7 +713,7 @@ docker run --rm --name selenium-docker -p 4444:4444 \ -v ${PWD}/config.toml:/opt/bin/config.toml \ -v ${PWD}/assets:/opt/selenium/assets \ -v /var/run/docker.sock:/var/run/docker.sock \ - selenium/standalone-docker:4.12.1-20230920 + selenium/standalone-docker:4.16.1-20231212 ``` #### Windows PowerShell @@ -812,7 +724,7 @@ docker run --rm --name selenium-docker -p 4444:4444 ` -v ${PWD}/config.toml:/opt/bin/config.toml ` -v ${PWD}/assets:/opt/selenium/assets ` -v /var/run/docker.sock:/var/run/docker.sock ` - selenium/standalone-docker:4.12.1-20230920 + selenium/standalone-docker:4.16.1-20231212 ``` @@ -850,7 +762,7 @@ ___ You can pass `SE_OPTS` variable with additional command line parameters for starting a hub or a node. ``` bash -$ docker run -d -p 4444:4444 -e SE_OPTS="--log-level FINE" --name selenium-hub selenium/hub:4.12.1-20230920 +$ docker run -d -p 4444:4444 -e SE_OPTS="--log-level FINE" --name selenium-hub selenium/hub:4.16.1-20231212 ``` ### SE_JAVA_OPTS Java Environment Options @@ -858,7 +770,7 @@ $ docker run -d -p 4444:4444 -e SE_OPTS="--log-level FINE" --name selenium-hub s You can pass `SE_JAVA_OPTS` environment variable to the Java process. ``` bash -$ docker run -d -p 4444:4444 -e SE_JAVA_OPTS=-Xmx512m --name selenium-hub selenium/hub:4.12.1-20230920 +$ docker run -d -p 4444:4444 -e SE_JAVA_OPTS=-Xmx512m --name selenium-hub selenium/hub:4.16.1-20231212 ``` ### Node configuration options @@ -880,7 +792,7 @@ $ docker run -d \ -e SE_EVENT_BUS_HOST= \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 -e SE_NODE_STEREOTYPE="{\"browserName\":\"${SE_NODE_BROWSER_NAME}\",\"browserVersion\":\"${SE_NODE_BROWSER_VERSION}\",\"platformName\": \"Linux\"}" \ - --shm-size="2g" selenium/node-chrome:4.12.1-20230920 + --shm-size="2g" selenium/node-chrome:4.16.1-20231212 ``` ### Setting Sub Path @@ -899,7 +811,7 @@ These settings can be adjusted by specifying `SE_SCREEN_WIDTH`, `SE_SCREEN_HEIGH environmental variables when starting the container. ``` bash -docker run -d -e SE_SCREEN_WIDTH=1366 -e SE_SCREEN_HEIGHT=768 -e SE_SCREEN_DEPTH=24 -e SE_SCREEN_DPI=74 selenium/standalone-firefox:4.12.1-20230920 +docker run -d -e SE_SCREEN_WIDTH=1366 -e SE_SCREEN_HEIGHT=768 -e SE_SCREEN_DEPTH=24 -e SE_SCREEN_DPI=74 selenium/standalone-firefox:4.16.1-20231212 ``` ### Grid Url and Session Timeout @@ -949,7 +861,7 @@ To avoid starting the server you can set the `START_XVFB` environment variable t ``` bash $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ - -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 -e START_XVFB=false --shm-size="2g" selenium/node-chrome:4.12.1-20230920 + -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 -e START_XVFB=false --shm-size="2g" selenium/node-chrome:4.16.1-20231212 ``` For more information, see this GitHub [issue](https://github.com/SeleniumHQ/docker-selenium/issues/567). @@ -962,7 +874,7 @@ pod and then scale a new one after N sessions. Set the environment variable `SE_ a value higher than zero to enable this behaviour. ``` bash -$ docker run -e SE_DRAIN_AFTER_SESSION_COUNT=5 --shm-size="2g" selenium/standalone-firefox:4.12.1-20230920 +$ docker run -e SE_DRAIN_AFTER_SESSION_COUNT=5 --shm-size="2g" selenium/standalone-firefox:4.16.1-20231212 ``` With the previous command, the Standalone container will shut down after 5 sessions have been executed. @@ -1054,22 +966,22 @@ $ docker network create grid $ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub \ --health-cmd='/opt/bin/check-grid.sh --host 0.0.0.0 --port 4444' \ --health-interval=15s --health-timeout=30s --health-retries=5 \ - selenium/hub:4.12.1-20230920 + selenium/hub:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-chrome:4.12.1-20230920 + selenium/node-chrome:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-edge:4.12.1-20230920 + selenium/node-edge:4.16.1-20231212 $ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-firefox:4.12.1-20230920 + selenium/node-firefox:4.16.1-20231212 ``` **Note:** The `\` line delimiter won't work on Windows-based terminals, try either `^` or a backtick. @@ -1202,7 +1114,7 @@ RUN mkdir -p -m755 /seluser/.pki/nssdb \ && chown -R 1200:1201 /seluser # Start from Selenium image and add relevant files from build image -FROM selenium/node-chrome:4.12.1-20230920 +FROM selenium/node-chrome:4.16.1-20231212 USER root COPY --from=build /seluser/ /home/seluser/ USER seluser @@ -1217,7 +1129,7 @@ RUN mkdir -p "/distribution" "/certs" && \ echo '{ "policies": { "Certificates": { "Install": ["/opt/firefox-latest/YOUR_CA.pem"] }} }' >"/distribution/policies.json" # Start from Selenium image and add relevant files from build image -FROM selenium/node-firefox:4.12.1-20230920 +FROM selenium/node-firefox:4.16.1-20231212 USER root COPY --from=build /certs /opt/firefox-latest COPY --from=build /distribution /opt/firefox-latest/distribution @@ -1240,9 +1152,9 @@ You can override it with the `SE_VNC_PORT` environment variable in case you want Here is an example with the standalone images, the same concept applies to the node images. ``` bash -$ docker run -d -p 4444:4444 -p 5900:5900 --shm-size="2g" selenium/standalone-chrome:4.12.1-20230920 -$ docker run -d -p 4445:4444 -p 5901:5900 --shm-size="2g" selenium/standalone-edge:4.12.1-20230920 -$ docker run -d -p 4446:4444 -p 5902:5900 --shm-size="2g" selenium/standalone-firefox:4.12.1-20230920 +$ docker run -d -p 4444:4444 -p 5900:5900 --shm-size="2g" selenium/standalone-chrome:4.16.1-20231212 +$ docker run -d -p 4445:4444 -p 5901:5900 --shm-size="2g" selenium/standalone-edge:4.16.1-20231212 +$ docker run -d -p 4446:4444 -p 5902:5900 --shm-size="2g" selenium/standalone-firefox:4.16.1-20231212 ``` Then, you would use in your VNC client: @@ -1257,6 +1169,8 @@ If you want to run VNC without password authentication you can set the environme If you want to run VNC in view-only mode you can set the environment variable `SE_VNC_VIEW_ONLY=1`. +If you want to modify the open file descriptor limit for the VNC server process you can set the environment variable `SE_VNC_ULIMIT=4096`. + ### Using your browser (no VNC client is needed) This project uses [noVNC](https://github.com/novnc/noVNC) to allow users to inspect visually container activity with @@ -1268,9 +1182,9 @@ You can also override it with the `SE_NO_VNC_PORT` environment variable in case Here is an example with the standalone images, the same concept applies to the node images. ``` bash -$ docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" selenium/standalone-chrome:4.12.1-20230920 -$ docker run -d -p 4445:4444 -p 7901:7900 --shm-size="2g" selenium/standalone-edge:4.12.1-20230920 -$ docker run -d -p 4446:4444 -p 7902:7900 --shm-size="2g" selenium/standalone-firefox:4.12.1-20230920 +$ docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" selenium/standalone-chrome:4.16.1-20231212 +$ docker run -d -p 4445:4444 -p 7901:7900 --shm-size="2g" selenium/standalone-edge:4.16.1-20231212 +$ docker run -d -p 4446:4444 -p 7902:7900 --shm-size="2g" selenium/standalone-firefox:4.16.1-20231212 ``` Then, you would use in your browser: @@ -1295,28 +1209,28 @@ In order to enable tracing in the Selenium Grid container, the following command ```bash docker network create grid docker run -d -p 16686:16686 -p 14250:14250 --net grid --name jaeger jaegertracing/all-in-one:1.17 -docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.12.1-20230920 +docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.16.1-20231212 docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_ENABLE_TRACING=true \ - -e JAVA_OPTS="-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-hub" \ + -e JAVA_OPTS="-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-chrome" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-chrome:4.12.1-20230920 + selenium/node-chrome:4.16.1-20231212 docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_ENABLE_TRACING=true \ -e JAVA_OPTS="-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-edge" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-edge:4.12.1-20230920 + selenium/node-edge:4.16.1-20231212 docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \ --shm-size="2g" \ -e SE_ENABLE_TRACING=true \ -e JAVA_OPTS="-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-firefox" \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - selenium/node-firefox:4.12.1-20230920 + selenium/node-firefox:4.16.1-20231212 ``` You can also refer to the below docker-compose yaml files to be able to start a simple grid (or) a dynamic grid. @@ -1375,19 +1289,20 @@ container in the following way: ```bash docker run -d -p 4444:4444 --shm-size="2g" \ - -v /home/ubuntu/files:/home/seluser/files \ - selenium/standalone-chrome:4.12.1-20230920 + -v /home/ubuntu/files:/home/seluser/Downloads \ + selenium/standalone-chrome:4.16.1-20231212 ``` That will mount the host `/home/ubuntu/files` directory -to the `/home/seluser/files` inside the container. The +to the `/home/seluser/Downloads` inside the container +(default browser's downloads directory). The problem happens because the volume will be mounted as `root`; therefore, the browser cannot write a file to that directory because it is running under the user `seluser`. This happens because that is how Docker mounts volumes in Linux, more details in this [issue](https://github.com/moby/moby/issues/2259). -A workaround for this is to create a directory on the +A workaround (to be done manually) for this is to create a directory on the host and change its permissions **before mounting the volume**. Depending on your user permissions, you might need to use `sudo` for some of these commands: @@ -1398,7 +1313,61 @@ chown 1200:1201 /home/ubuntu/files ``` After doing this, you should be able to download files -to the mounted directory. If you have a better workaround, -please send us a pull request! +to the mounted directory. + +--- +Another introduced feature [#1947](https://github.com/SeleniumHQ/docker-selenium/issues/1947) +that take action to change ownership when staring the container. + +You are able to configure another default browser download directory and mount the host with it in container by overriding `SE_DOWNLOAD_DIR`. +For example, in test you might be scripting something +```groovy +ChromeOptions options = new ChromeOptions(); +HashMap chromePrefs = new HashMap(); +chromePrefs.put("download.default_directory", "/path/to/your/downloads"); +options.setExperimentalOption("prefs", chromePrefs); +options.add_argument('disable-features=DownloadBubble,DownloadBubbleV2') +WebDriver driver = new ChromeDriver(options); +``` + +When running the container, you set the `SE_DOWNLOAD_DIR` and mount the host with that directory in container. +```bash +docker run -d -p 4444:4444 --shm-size="2g" \ + -e SE_DOWNLOAD_DIR=/path/to/your/downloads \ + -v /home/ubuntu/files:/path/to/your/downloads \ + selenium/standalone-chrome:4.16.1-20231212 +``` + +**Note:** The changing ownership when starting container is not supported well when both overriding `SE_DOWNLOAD_DIR` and running non-root (e.g. Podman) or specifying user ids different from `1200` (OpenShift arbitrary user ids). + +In this case, you can use above workaround to create and set permissions for the directory on the host before mounting the volume. + +You also can run the container with `--user root` once to initialize and change ownership of the directory on the host, then run the container with non-root user again. + +For example, the first run with root user: +```bash +docker run -d -p 4444:4444 --shm-size="2g" \ + --user root \ + -e SE_DOWNLOAD_DIR=/path/to/your/downloads \ + -v /home/ubuntu/files:/path/to/your/downloads \ + selenium/standalone-chrome:4.16.1-20231212 +``` + +Then stop it, rerun with switching to non-root user: +```bash +docker run -d -p 4444:4444 --shm-size="2g" \ + --user 4496 \ + -e SE_DOWNLOAD_DIR=/path/to/your/downloads \ + -v /home/ubuntu/files:/path/to/your/downloads \ + selenium/standalone-chrome:4.16.1-20231212 +``` +Summarize the supported use case for changing ownership when starting container: + +| User (uid) | Mount `SE_DOWNLOAD_DIR` in container | Auto changing | +|----------------------|--------------------------------------|---------------| +| seluser (uid `1200`) | default `/home/seluser/Downloads` | Yes | +| seluser (uid `1200`) | any `/path/to/downloads` | Yes | +| any (uid != `1200`) | default `/home/seluser/Downloads` | Yes | +| any (uid != `1200`) | any `/path/to/downloads` | No | diff --git a/Router/Dockerfile b/Router/Dockerfile index ccce7a2ff..3410b932f 100644 --- a/Router/Dockerfile +++ b/Router/Dockerfile @@ -4,7 +4,7 @@ ARG AUTHORS FROM ${NAMESPACE}/base:${VERSION} LABEL authors=${AUTHORS} -USER 1200 +USER ${SEL_UID} #======================== # Selenium Router Configuration @@ -17,7 +17,7 @@ ENV SE_SESSION_RETRY_INTERVAL 0 EXPOSE 4444 -COPY start-selenium-grid-router.sh \ +COPY --chown="${SEL_UID}:${SEL_GID}" start-selenium-grid-router.sh \ /opt/bin/ COPY selenium-grid-router.conf /etc/supervisor/conf.d/ diff --git a/Router/start-selenium-grid-router.sh b/Router/start-selenium-grid-router.sh index b99f675e5..1c3103dfc 100755 --- a/Router/start-selenium-grid-router.sh +++ b/Router/start-selenium-grid-router.sh @@ -54,20 +54,21 @@ if [ ! -z "$SE_ROUTER_PORT" ]; then PORT_CONFIG="--port ${SE_ROUTER_PORT}" fi -EXTRA_LIBS="/opt/selenium/selenium-http-jdk-client.jar" +EXTRA_LIBS="" if [ ! -z "$SE_ENABLE_TRACING" ]; then EXTERNAL_JARS=$(> "$FILENAME" SE_NODE_BROWSER_NAME=$(cat /opt/selenium/browser_name) -if [[ "${SE_NODE_BROWSER_NAME}" == "chrome" ]]; then - SE_NODE_BROWSER_VERSION=$(short_version $(google-chrome --version | awk '{print $3}')) -elif [[ "${SE_NODE_BROWSER_NAME}" == "chromium" ]]; then - SE_NODE_BROWSER_NAME=chrome - SE_NODE_BROWSER_VERSION=$(short_version $(chromium --version | awk '{print $2}')) -elif [[ "${SE_NODE_BROWSER_NAME}" == "firefox" ]]; then - SE_NODE_BROWSER_VERSION=$(short_version $(firefox --version | awk '{print $3}')) -elif [[ "${SE_NODE_BROWSER_NAME}" == "MicrosoftEdge" ]]; then - SE_NODE_BROWSER_VERSION=$(short_version $(microsoft-edge --version | awk '{print $3}')) +SE_NODE_BROWSER_VERSION=$(short_version $(cat /opt/selenium/browser_version)) +SE__BROWSER_BINARY_LOCATION=$(cat /opt/selenium/browser_binary_location) + +if [[ -z "$SE_NODE_STEREOTYPE" ]]; then +SE_NODE_STEREOTYPE="{\"browserName\": \"${SE_NODE_BROWSER_NAME}\", \"browserVersion\": \"${SE_NODE_BROWSER_VERSION}\", \"platformName\": \"Linux\", ${SE__BROWSER_BINARY_LOCATION}}" +else +SE_NODE_STEREOTYPE="$SE_NODE_STEREOTYPE" fi -SE_NODE_STEREOTYPE="{\"browserName\": \"${SE_NODE_BROWSER_NAME}\", \"browserVersion\": \"${SE_NODE_BROWSER_VERSION}\", \"platformName\": \"Linux\"}" echo "[[node.driver-configuration]]" >> "$FILENAME" echo "display-name = \"${SE_NODE_BROWSER_NAME}\"" >> "$FILENAME" echo "stereotype = '${SE_NODE_STEREOTYPE}'" >> "$FILENAME" diff --git a/Standalone/start-selenium-standalone.sh b/Standalone/start-selenium-standalone.sh index 7af58b6e8..cfde62d8e 100755 --- a/Standalone/start-selenium-standalone.sh +++ b/Standalone/start-selenium-standalone.sh @@ -17,21 +17,29 @@ echo "Selenium Grid Standalone configuration: " cat /opt/selenium/config.toml echo "Starting Selenium Grid Standalone..." -EXTRA_LIBS="/opt/selenium/selenium-http-jdk-client.jar" +EXTRA_LIBS="" if [ ! -z "$SE_ENABLE_TRACING" ]; then EXTERNAL_JARS=$( /etc/apt/sources.list \ - && echo "deb http://archive.ubuntu.com/ubuntu focal-updates main universe\n" >> /etc/apt/sources.list \ - && echo "deb http://security.ubuntu.com/ubuntu focal-security main universe\n" >> /etc/apt/sources.list +RUN echo "deb http://archive.ubuntu.com/ubuntu jammy main universe\n" > /etc/apt/sources.list \ + && echo "deb http://archive.ubuntu.com/ubuntu jammy-updates main universe\n" >> /etc/apt/sources.list \ + && echo "deb http://security.ubuntu.com/ubuntu jammy-security main universe\n" >> /etc/apt/sources.list # No interactive frontend during docker build ENV DEBIAN_FRONTEND=noninteractive \ @@ -18,7 +20,10 @@ ENV DEBIAN_FRONTEND=noninteractive \ RUN apt-get -qqy update \ && apt-get upgrade -yq \ && apt-get -qqy --no-install-recommends install \ - supervisor x11-xserver-utils python3-pip \ + supervisor x11-xserver-utils x11-utils curl jq python3-pip \ + && python3 -m pip install --upgrade pip \ + && python3 -m pip install --upgrade setuptools \ + && python3 -m pip install --upgrade wheel \ && rm -rf /var/lib/apt/lists/* /var/cache/apt/* #====================================== @@ -28,7 +33,8 @@ COPY supervisord.conf /etc COPY entry_point.sh video.sh video_ready.py /opt/bin/ RUN cd /opt/bin && pip install psutil -RUN mkdir -p /var/run/supervisor /var/log/supervisor /videos +ENV SE_VIDEO_FOLDER /videos +RUN mkdir -p /var/run/supervisor /var/log/supervisor $SE_VIDEO_FOLDER ENTRYPOINT ["/opt/bin/entry_point.sh"] CMD ["/opt/bin/entry_point.sh"] @@ -41,6 +47,5 @@ ENV SE_FRAME_RATE 15 ENV SE_CODEC libx264 ENV SE_PRESET "-preset ultrafast" ENV FILE_NAME video.mp4 -ENV SE_VIDEO_FOLDER /videos EXPOSE 9000 diff --git a/Video/supervisord.conf b/Video/supervisord.conf index 68d42b8a2..84b7d6ae2 100644 --- a/Video/supervisord.conf +++ b/Video/supervisord.conf @@ -10,6 +10,7 @@ pidfile=/var/run/supervisor/supervisord.pid ; (supervisord pidfile;default sup nodaemon=true ; (start in foreground if true;default false) minfds=1024 ; (min. avail startup file descriptors;default 1024) minprocs=200 ; (min. avail process descriptors;default 200) +user=root ; (default is current user, required if root) [program:video-recording] priority=0 diff --git a/charts/selenium-grid/.helmignore b/charts/selenium-grid/.helmignore index 0e8a0eb36..5bdaa3eb0 100644 --- a/charts/selenium-grid/.helmignore +++ b/charts/selenium-grid/.helmignore @@ -21,3 +21,4 @@ .idea/ *.tmproj .vscode/ +ci/ diff --git a/charts/selenium-grid/CHANGELOG.md b/charts/selenium-grid/CHANGELOG.md index 32a81cbfb..cb77401b7 100644 --- a/charts/selenium-grid/CHANGELOG.md +++ b/charts/selenium-grid/CHANGELOG.md @@ -1,6 +1,107 @@ -# Change Log +## :heavy_check_mark: selenium-grid-0.26.2 -All notable changes to this helm chart will be documented in this file. +- Chart is using image tag 4.16.1-20231212 + +### Changed +- Update tag in docs and files :: Selenium CI Bot +- Update chart CHANGELOG [skip ci] :: Selenium CI Bot + +## :heavy_check_mark: selenium-grid-0.26.1 + +- Chart is using image tag 4.16.1-20231208 + +### Added +- Add script to generate chart CHANGELOG after released (#2054) :: Viet Nguyen Duc +- feat(chart): Append subPath to ENV variable SE_NODE_GRID_URL (#2053) :: Viet Nguyen Duc + +### Changed +- Update tag in docs and files :: Selenium CI Bot + +## :heavy_check_mark: selenium-grid-0.26.0 + +- Chart is using image tag 4.16.0-20231206 + +### Added +- feat(chart): Add default annotations for ingress nginx controller (#2047) :: Viet Nguyen Duc +- feat: Video image with based FFmpeg-6.1 on Ubuntu-22.04 (#2042) :: Viet Nguyen Duc + +### Fixed +- bug(#1824): Container ENV SE_NODE_SESSION_TIMEOUT not take effect (#2044) :: Viet Nguyen Duc +- bug(#2038): Rollback io.opentelemetry 1.31.0 - add test tracing enabled (#2040) :: Viet Nguyen Duc + +### Changed +- Update tag in docs and files :: Selenium CI Bot +- Update chart CHANGELOG [skip ci] :: Viet Nguyen Duc +- test(chart): Parallel tests execution against autoscaling in Kubernetes (#2046) :: Viet Nguyen Duc +- test(chart): Chart template render and assert output (#2043) :: Viet Nguyen Duc +- test(chart): Add test for setting registry to pull images (#2036) :: Viet Nguyen Duc + +## :heavy_check_mark: 0.25.3 + +### Changed +- Update image tag to 4.16.0-20231206 +- Update tag in docs and files :: Selenium CI Bot +- test: Add sanity test for download file (#2034) [deploy] :: Viet Nguyen Duc +- feat(chart): distribution registry can be set global and individual component (#2030) :: Viet Nguyen Duc +- Update tag in docs and files [skip ci] :: Selenium CI Bot +- test: Sanity tests Selenium Grid chart via Makefile commands (#2029) :: Viet Nguyen Duc +- Feature run selenium tests grid on kubernetes via helm chart (#2027) :: Amar Deep Singh +- feat: CI Bot bump chart version along with new deploy image version (#2028) :: Viet Nguyen Duc +- Update NodeChrome support latest version from GoogleChromeLabs (#2018) :: Viet Nguyen Duc +- Update tag in docs and files [skip ci] :: Selenium CI Bot +- corrected typo in selenium grid charts (#2010) :: Thabelo Ramabulana + +## :heavy_check_mark: 0.25.1 + +### Changed +- Update image tag to 4.15.0-20231110 +- Bug: Error setting name in helm release #2006 #2007 (#2009) :: Viet Nguyen Duc + +## :heavy_check_mark: 0.25.0 + +### Changed +- Update image tag to 4.15.0-20231110 +- feat(helm-test): Added helm test and linting (#2003) :: Amar Deep Singh +- Update tag in docs and files [skip ci] :: Selenium CI Bot +- Update tag in docs and files [skip ci] :: Selenium CI Bot +- feat: Adding port to nodes service (#1996) :: Viet Nguyen Duc + +## :heavy_check_mark: 0.24.0 + +### Changed +- Update image tag to 4.15.0-20231102 +- Bumping chart version :: Viet Nguyen Duc +- Add chart parameter ingress.paths to configure custom paths (#1994) :: Viet Nguyen Duc +- feat(autoscaling): Unified parameters to set scaled options for browser nodes (#1989) :: Viet Nguyen Duc +- Update tag in docs and files [skip ci] :: Selenium CI Bot +- Improve chart templates in the section videoRecorder (#1987) :: Viet Nguyen Duc +- Improve default value for videoRecorder in chart (#1984) :: Viet Nguyen Duc +- Fix minor issues after PR #1881 and #1981 (#1983) :: Viet Nguyen Duc + +## :heavy_check_mark: 0.23.0 + +### Added + +- Update tag in docs and files [skip ci] :: Selenium CI Bot +- feat: video recording with pluggable upload container (#1881) :: MÃ¥rten Svantesson +- Update Video/Dockerfile with based image ffmpeg:6.0-alpine (#1981) :: Viet Nguyen Duc + +### Changed +- Update image tag to 4.14.1-20231025 + +## :heavy_check_mark: 0.22.0 + +### Added +- feat(keda): bumped up keda 2.12.0 (#1960) :: Amar Deep Singh +- Add missing Ingress namespace field (#1966) :: Cody Lent + +### Changed +- Update image tag to 4.13.0-20231004 + +## :heavy_check_mark: 0.21.3 + +### Changed +- Update image tag to 4.13.0-20230926 ## :heavy_check_mark: 0.21.2 diff --git a/charts/selenium-grid/Chart.yaml b/charts/selenium-grid/Chart.yaml index 3835c2445..5f8594060 100644 --- a/charts/selenium-grid/Chart.yaml +++ b/charts/selenium-grid/Chart.yaml @@ -2,11 +2,16 @@ apiVersion: v2 name: selenium-grid description: A Helm chart for creating a Selenium Grid Server in Kubernetes type: application -version: 0.21.2 -appVersion: 4.12.1-20230920 +version: 0.26.2 +appVersion: 4.16.1-20231212 icon: https://github.com/SeleniumHQ/docker-selenium/raw/trunk/logo.png dependencies: - repository: https://kedacore.github.io/charts - version: 2.10.2 + version: 2.12.0 name: keda condition: autoscaling.enabled +maintainers: + - name: SeleniumHQ + email: selenium-developers@googlegroups.com +sources: + - https://github.com/SeleniumHQ/docker-selenium diff --git a/charts/selenium-grid/README.md b/charts/selenium-grid/README.md index 2d77ce9fa..524b497b5 100644 --- a/charts/selenium-grid/README.md +++ b/charts/selenium-grid/README.md @@ -27,20 +27,22 @@ helm install selenium-grid docker-selenium/selenium-grid --version # In both cases grid exposed by default using ingress. You may want to set hostname for the grid. Default hostname is selenium-grid.local. helm install selenium-grid --set ingress.hostname=selenium-grid.k8s.local docker-selenium/chart/selenium-grid/. +# Verify ingress configuration via kubectl get ingress +# Notes: In case you want to set hostname is selenium-grid.local. You need to add the IP and hostname to the local host file in `/etc/hosts` ``` ## Enable Selenium Grid Autoscaling -Selenium Grid has the ability to autoscale browser nodes up/down based on the pending requests in the +Selenium Grid has the ability to autoscaling browser nodes up/down based on the pending requests in the session queue. -To do this [KEDA](https://keda.sh/docs/2.10/scalers/selenium-grid-scaler/) is used. When enabling +To do this [KEDA](https://keda.sh/docs/latest/scalers/selenium-grid-scaler/) is used. When enabling autoscaling using `autoscaling.enabling` KEDA is installed automatically. To instead use an existing installation of KEDA you can enable autoscaling with `autoscaling.enableWithExistingKEDA` instead. KEDA can scale either with -[deployments](https://keda.sh/docs/2.10/concepts/scaling-deployments/#scaling-of-deployments-and-statefulsets) -or [jobs](https://keda.sh/docs/2.10/concepts/scaling-jobs/) and the charts support both types. This +[deployments](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaling-of-deployments-and-statefulsets) +or [jobs](https://keda.sh/docs/latest/concepts/scaling-jobs/) and the charts support both types. This chart support both modes. It is controlled with `autoscaling.scalingType` that can be set to either job (default) or deployment. @@ -77,151 +79,248 @@ To uninstall: helm uninstall selenium-grid ``` +## Ingress Configuration + +By default, ingress is enabled without annotations set. If NGINX ingress controller is used, you need to set few annotations to override the default timeout values to avoid 504 errors (see [#1808](https://github.com/SeleniumHQ/docker-selenium/issues/1808)). Since in Selenium Grid the default of `SE_NODE_SESSION_TIMEOUT` and `SE_SESSION_REQUEST_TIMEOUT` is `300` seconds. + +In order to make user experience better, there are few annotations will be set by default if NGINX ingress controller is used. Mostly relates to timeouts and buffer sizes. + +If you are not using NGINX ingress controller, you can disable these default annotations by setting `ingress.nginx` to `nil` (aka null) via Helm CLI `--set ingress.nginx=null`) or via an override-values.yaml as below: + +```yaml +ingress: + nginx: + # nginx: null (alternative way) +``` + +Similarly, if you want to disable a sub-config of `ingress.nginx`. For example: `--set ingress.nginx.proxyBuffer=null`) + +You are also able to combine using both default annotations and your own annotations in `ingress.annotations`. Duplicated keys will be merged strategy overwrite with your own annotations in `ingress.annotations` take precedence. + +```yaml +ingress: + nginx: + proxyTimeout: 3600 + annotations: + nginx.ingress.kubernetes.io/proxy-connect-timeout: "7200" # This key will take 7200 instead of 3600 +``` + +List mapping of chart values and default annotation(s) + +```markdown +# `ingress.nginx.proxyTimeout` pass value to annotation(s) +nginx.ingress.kubernetes.io/proxy-connect-timeout +nginx.ingress.kubernetes.io/proxy-send-timeout +nginx.ingress.kubernetes.io/proxy-read-timeout +nginx.ingress.kubernetes.io/proxy-next-upstream-timeout +nginx.ingress.kubernetes.io/auth-keepalive-timeout + +# `ingress.nginx.proxyBuffer` pass value to to annotation(s) +nginx.ingress.kubernetes.io/proxy-request-buffering: "on" +nginx.ingress.kubernetes.io/proxy-buffering: "on" + +# `ingress.nginx.proxyBuffer.size` pass value to to annotation(s) +nginx.ingress.kubernetes.io/proxy-buffer-size +nginx.ingress.kubernetes.io/client-body-buffer-size + +# `ingress.nginx.proxyBuffer.number` pass value to annotation(s) +nginx.ingress.kubernetes.io/proxy-buffers-number +``` + ## Configuration For now, global configuration supported is: -| Parameter | Default | Description | -|---------------------------------------|-------------------|---------------------------------------| -| `global.seleniumGrid.imageTag` | `4.12.1-20230920` | Image tag for all selenium components | -| `global.seleniumGrid.nodesImageTag` | `4.12.1-20230920` | Image tag for browser's nodes | -| `global.seleniumGrid.imagePullSecret` | `""` | Pull secret to be used for all images | -| `global.seleniumGrid.imagePullSecret` | `""` | Pull secret to be used for all images | -| `global.seleniumGrid.affinity` | `{}` | Affinity assigned globally | +| Parameter | Default | Description | +|---------------------------------------|-----------------------|---------------------------------------| +| `global.seleniumGrid.imageRegistry` | `selenium` | Distribution registry to pull images | +| `global.seleniumGrid.imageTag` | `4.16.1-20231212` | Image tag for all selenium components | +| `global.seleniumGrid.nodesImageTag` | `4.16.1-20231212` | Image tag for browser's nodes | +| `global.seleniumGrid.videoImageTag` | `ffmpeg-6.1-20231212` | Image tag for browser's video recoder | +| `global.seleniumGrid.imagePullSecret` | `""` | Pull secret to be used for all images | +| `global.seleniumGrid.imagePullSecret` | `""` | Pull secret to be used for all images | +| `global.seleniumGrid.affinity` | `{}` | Affinity assigned globally | This table contains the configuration parameters of the chart and their default values: -| Parameter | Default | Description | -|---------------------------------------------|---------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| -| `isolateComponents` | `false` | Deploy Router, Distributor, EventBus, SessionMap and Nodes separately | -| `serviceAccount.create` | `true` | Enable or disable creation of service account (if `false`, `serviceAccount.name` MUST be specified | -| `serviceAccount.name` | `""` | Name of the service account to be made or existing service account to use for all deployments and jobs | -| `serviceAccount.annotations` | `{}` | Custom annotations for service account | -| `busConfigMap.name` | `selenium-event-bus-config` | Name of the configmap that contains SE_EVENT_BUS_HOST, SE_EVENT_BUS_PUBLISH_PORT and SE_EVENT_BUS_SUBSCRIBE_PORT variables | -| `busConfigMap.annotations` | `{}` | Custom annotations for configmap | -| `nodeConfigMap.name` | `selenium-node-config` | Name of the configmap that contains common environment variables for browser nodes | -| `nodeConfigMap.annotations` | `{}` | Custom annotations for configmap | -| `ingress.enabled` | `true` | Enable or disable ingress resource | -| `ingress.className` | `""` | Name of ingress class to select which controller will implement ingress resource | -| `ingress.annotations` | `{}` | Custom annotations for ingress resource | -| `ingress.hostname` | `selenium-grid.local` | Default host for the ingress resource | -| `ingress.path` | `/` | Default host path for the ingress resource | -| `ingress.tls` | `[]` | TLS backend configuration for ingress resource | -| `autoscaling.enableWithExistingKEDA` | `false` | Enable autoscaling of browser nodes. | -| `autoscaling.enabled` | `false` | Same as above plus installation of KEDA | -| `autoscaling.scalingType` | `job` | Which typ of KEDA scaling to use: job or deployment | -| `autoscaling.scaledJobOptions` | See `values.yaml` | Options for KEDA ScaledJobs | -| `autoscaling.deregisterLifecycle` | See `values.yaml` | Lifecycle applied to pods of deployments controlled by KEDA. Makes the node deregister from selenium hub | -| `chromeNode.enabled` | `true` | Enable chrome nodes | -| `chromeNode.deploymentEnabled` | `true` | Enable creation of Deployment for chrome nodes | -| `chromeNode.replicas` | `1` | Number of chrome nodes. Disabled if autoscaling is enabled. | -| `chromeNode.imageName` | `selenium/node-chrome` | Image of chrome nodes | -| `chromeNode.imageTag` | `4.12.1-20230920` | Image of chrome nodes | -| `chromeNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `chromeNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `chromeNode.ports` | `[5555]` | Port list to enable on container | -| `chromeNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | -| `chromeNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | -| `chromeNode.annotations` | `{}` | Annotations for chrome-node pods | -| `chromeNode.labels` | `{}` | Labels for chrome-node pods | -| `chromeNode.resources` | `See values.yaml` | Resources for chrome-node pods | -| `chromeNode.securityContext` | `See values.yaml` | Security context for chrome-node pods | -| `chromeNode.tolerations` | `[]` | Tolerations for chrome-node pods | -| `chromeNode.nodeSelector` | `{}` | Node Selector for chrome-node pods | -| `chromeNode.affinity` | `{}` | Affinity for chrome-node pods | -| `chromeNode.hostAliases` | `nil` | Custom host aliases for chrome nodes | -| `chromeNode.priorityClassName` | `""` | Priority class name for chrome-node pods | -| `chromeNode.extraEnvironmentVariables` | `nil` | Custom environment variables for chrome nodes | -| `chromeNode.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for chrome nodes | -| `chromeNode.service.enabled` | `true` | Create a service for node | -| `chromeNode.service.type` | `ClusterIP` | Service type | -| `chromeNode.service.annotations` | `{}` | Custom annotations for service | -| `chromeNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | -| `chromeNode.startupProbe` | `{}` | Probe to check pod is started successfully | -| `chromeNode.livenessProbe` | `{}` | Liveness probe settings | -| `chromeNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | -| `chromeNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | -| `chromeNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | -| `chromeNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | -| `chromeNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | -| `chromeNode.hpa.browserName` | `chrome` | BrowserName from the capability | -| `chromeNode.hpa.browserVersion` | `` | BrowserVersion from the capability | -| `chromeNode.maxReplicaCount` | `8` | Max number of replicas that this browsernode can auto scale up to | -| `chromeNode.minReplicaCount` | `1` | Min number of replicas that this browsernode has when jobs are running | -| `firefoxNode.enabled` | `true` | Enable firefox nodes | -| `firefoxNode.deploymentEnabled` | `true` | Enable creation of Deployment for firefox nodes | -| `firefoxNode.replicas` | `1` | Number of firefox nodes. Disabled if autoscaling is enabled. | -| `firefoxNode.imageName` | `selenium/node-firefox` | Image of firefox nodes | -| `firefoxNode.imageTag` | `4.12.1-20230920` | Image of firefox nodes | -| `firefoxNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `firefoxNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `firefoxNode.ports` | `[5555]` | Port list to enable on container | -| `firefoxNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | -| `firefoxNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | -| `firefoxNode.annotations` | `{}` | Annotations for firefox-node pods | -| `firefoxNode.labels` | `{}` | Labels for firefox-node pods | -| `firefoxNode.resources` | `See values.yaml` | Resources for firefox-node pods | -| `firefoxNode.securityContext` | `See values.yaml` | Security context for firefox-node pods | -| `firefoxNode.tolerations` | `[]` | Tolerations for firefox-node pods | -| `firefoxNode.nodeSelector` | `{}` | Node Selector for firefox-node pods | -| `firefoxNode.affinity` | `{}` | Affinity for firefox-node pods | -| `firefoxNode.hostAliases` | `nil` | Custom host aliases for firefox nodes | -| `firefoxNode.priorityClassName` | `""` | Priority class name for firefox-node pods | -| `firefoxNode.extraEnvironmentVariables` | `nil` | Custom environment variables for firefox nodes | -| `firefoxNode.extraEnvFrom` | `nil` | Custom environment variables taken from `configMap` or `secret` for firefox nodes | -| `firefoxNode.service.enabled` | `true` | Create a service for node | -| `firefoxNode.service.type` | `ClusterIP` | Service type | -| `firefoxNode.service.annotations` | `{}` | Custom annotations for service | -| `firefoxNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | -| `firefoxNode.startupProbe` | `{}` | Probe to check pod is started successfully | -| `firefoxNode.livenessProbe` | `{}` | Liveness probe settings | -| `firefoxNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | -| `firefoxNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | -| `firefoxNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | -| `firefoxNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | -| `firefoxNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | -| `firefoxNode.hpa.browserName` | `firefox` | BrowserName from the capability | -| `firefoxNode.hpa.browserVersion` | `` | BrowserVersion from the capability | -| `firefoxNode.maxReplicaCount` | `8` | Max number of replicas that this browsernode can auto scale up to | -| `firefoxNode.minReplicaCount` | `1` | Min number of replicas that this browsernode has when jobs are running | -| `edgeNode.enabled` | `true` | Enable edge nodes | -| `edgeNode.deploymentEnabled` | `true` | Enable creation of Deployment for edge nodes | -| `edgeNode.replicas` | `1` | Number of edge nodes. Disabled if autoscaling is enabled. | -| `edgeNode.imageName` | `selenium/node-edge` | Image of edge nodes | -| `edgeNode.imageTag` | `4.12.1-20230920` | Image of edge nodes | -| `edgeNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `edgeNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `edgeNode.ports` | `[5555]` | Port list to enable on container | -| `edgeNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | -| `edgeNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | -| `edgeNode.annotations` | `{}` | Annotations for edge-node pods | -| `edgeNode.labels` | `{}` | Labels for edge-node pods | -| `edgeNode.resources` | `See values.yaml` | Resources for edge-node pods | -| `edgeNode.securityContext` | `See values.yaml` | Security context for edge-node pods | -| `edgeNode.tolerations` | `[]` | Tolerations for edge-node pods | -| `edgeNode.nodeSelector` | `{}` | Node Selector for edge-node pods | -| `edgeNode.affinity` | `{}` | Affinity for edge-node pods | -| `edgeNode.hostAliases` | `nil` | Custom host aliases for edge nodes | -| `edgeNode.priorityClassName` | `""` | Priority class name for edge-node pods | -| `edgeNode.extraEnvironmentVariables` | `nil` | Custom environment variables for firefox nodes | -| `edgeNode.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for firefox nodes | -| `edgeNode.service.enabled` | `true` | Create a service for node | -| `edgeNode.service.type` | `ClusterIP` | Service type | -| `edgeNode.service.annotations` | `{}` | Custom annotations for service | -| `edgeNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | -| `edgeNode.startupProbe` | `{}` | Probe to check pod is started successfully | -| `edgeNode.livenessProbe` | `{}` | Liveness probe settings | -| `edgeNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | -| `edgeNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | -| `edgeNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | -| `edgeNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | -| `edgeNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | -| `edgeNode.hpa.browserName` | `edge` | BrowserName from the capability | -| `edgeNode.hpa.browserVersion` | `` | BrowserVersion from the capability | -| `edgeNode.maxReplicaCount` | `8` | Max number of replicas that this browsernode can auto scale up to | -| `edgeNode.minReplicaCount` | `1` | Min number of replicas that this browsernode has when jobs are running | -| `customLabels` | `{}` | Custom labels for k8s resources | -| `customLabels` | `{}` | Custom labels for k8s resources | +| Parameter | Default | Description | +|-----------------------------------------------|---------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| `basicAuth.enabled` | `true` | Enable or disable basic auth for Selenium Grid | +| `basicAuth.username` | `admin` | Username of basic auth for Selenium Grid | +| `basicAuth.password` | `admin` | Password of basic auth for Selenium Grid | +| `isolateComponents` | `false` | Deploy Router, Distributor, EventBus, SessionMap and Nodes separately | +| `serviceAccount.create` | `true` | Enable or disable creation of service account (if `false`, `serviceAccount.name` MUST be specified | +| `serviceAccount.name` | `""` | Name of the service account to be made or existing service account to use for all deployments and jobs | +| `serviceAccount.annotations` | `{}` | Custom annotations for service account | +| `busConfigMap.name` | `selenium-event-bus-config` | Name of the configmap that contains SE_EVENT_BUS_HOST, SE_EVENT_BUS_PUBLISH_PORT and SE_EVENT_BUS_SUBSCRIBE_PORT variables | +| `busConfigMap.annotations` | `{}` | Custom annotations for configmap | +| `nodeConfigMap.name` | `selenium-node-config` | Name of the configmap that contains common environment variables for browser nodes | +| `nodeConfigMap.annotations` | `{}` | Custom annotations for configmap | +| `ingress.enabled` | `true` | Enable or disable ingress resource | +| `ingress.className` | `""` | Name of ingress class to select which controller will implement ingress resource | +| `ingress.annotations` | `{}` | Custom annotations for ingress resource | +| `ingress.nginx.proxyTimeout` | `3600` | Value is used to set for NGINX ingress annotations related to proxy timeout | +| `ingress.nginx.proxyBuffer.size` | `512M` | Value is used to set for NGINX ingress annotations on size of the buffer proxy_buffer_size used for reading | +| `ingress.nginx.proxyBuffer.number` | `4` | Value is used to set for NGINX ingress annotations on number of the buffers in proxy_buffers used for reading | +| `ingress.hostname` | `` | Default host for the ingress resource | +| `ingress.path` | `/` | Default host path for the ingress resource | +| `ingress.pathType` | `Prefix` | Default path type for the ingress resource | +| `ingress.paths` | `[]` | List of paths config for the ingress resource. This will override the default path | +| `ingress.tls` | `[]` | TLS backend configuration for ingress resource | +| `autoscaling.enableWithExistingKEDA` | `false` | Enable autoscaling of browser nodes. | +| `autoscaling.enabled` | `false` | Same as above plus installation of KEDA | +| `autoscaling.scalingType` | `job` | Which typ of KEDA scaling to use: `job` or `deployment` | +| `autoscaling.scaledOptions` | See `values.yaml` | Common options for KEDA scaled resources (both ScaledJobs and ScaledObjects) | +| `autoscaling.scaledOptions.minReplicaCount` | `0` | Min number of replicas that each browser nodes has when autoscaling | +| `autoscaling.scaledOptions.maxReplicaCount` | `8` | Max number of replicas that each browser nodes can auto scale up to | +| `autoscaling.scaledOptions.pollingInterval` | `10` | The interval to check each trigger on | +| `autoscaling.scaledJobOptions` | See `values.yaml` | Options for KEDA ScaledJobs (when `scalingType` is set to `job`) | +| `autoscaling.scaledObjectOptions` | See `values.yaml` | Options for KEDA ScaledObjects (when `scalingType` is set to `deployment`) | +| `autoscaling.deregisterLifecycle` | See `values.yaml` | Lifecycle applied to pods of deployments controlled by KEDA. Makes the node deregister from selenium hub | +| `chromeNode.enabled` | `true` | Enable chrome nodes | +| `chromeNode.deploymentEnabled` | `true` | Enable creation of Deployment for chrome nodes | +| `chromeNode.replicas` | `1` | Number of chrome nodes. Disabled if autoscaling is enabled. | +| `chromeNode.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `chromeNode.imageName` | `node-chrome` | Image of chrome nodes | +| `chromeNode.imageTag` | `4.16.1-20231212` | Image of chrome nodes | +| `chromeNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `chromeNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `chromeNode.ports` | `[5555]` | Port list to enable on container | +| `chromeNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | +| `chromeNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | +| `chromeNode.annotations` | `{}` | Annotations for chrome-node pods | +| `chromeNode.labels` | `{}` | Labels for chrome-node pods | +| `chromeNode.resources` | `See values.yaml` | Resources for chrome-node pods | +| `chromeNode.securityContext` | `See values.yaml` | Security context for chrome-node pods | +| `chromeNode.tolerations` | `[]` | Tolerations for chrome-node pods | +| `chromeNode.nodeSelector` | `{}` | Node Selector for chrome-node pods | +| `chromeNode.affinity` | `{}` | Affinity for chrome-node pods | +| `chromeNode.hostAliases` | `nil` | Custom host aliases for chrome nodes | +| `chromeNode.priorityClassName` | `""` | Priority class name for chrome-node pods | +| `chromeNode.extraEnvironmentVariables` | `nil` | Custom environment variables for chrome nodes | +| `chromeNode.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for chrome nodes | +| `chromeNode.service.enabled` | `true` | Create a service for node | +| `chromeNode.service.type` | `ClusterIP` | Service type | +| `chromeNode.service.loadBalancerIP` | `` | Set specific loadBalancerIP when serviceType is LoadBalancer | +| `chromeNode.service.ports` | `[]` | Extra ports exposed in node service | +| `chromeNode.service.annotations` | `{}` | Custom annotations for service | +| `chromeNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | +| `chromeNode.startupProbe` | `{}` | Probe to check pod is started successfully | +| `chromeNode.livenessProbe` | `{}` | Liveness probe settings | +| `chromeNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | +| `chromeNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | +| `chromeNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | +| `chromeNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | +| `chromeNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | +| `chromeNode.hpa.browserName` | `chrome` | BrowserName from the capability | +| `chromeNode.hpa.browserVersion` | `` | BrowserVersion from the capability | +| `chromeNode.scaledOptions` | See `values.yaml` | Override the global `autoscaling.scaledOptions` with specific scaled options for chrome nodes | +| `chromeNode.scaledJobOptions` | See `values.yaml` | Override the global `autoscaling.scaledJobOptions` with specific scaled options for chrome nodes | +| `chromeNode.scaledObjectOptions` | See `values.yaml` | Override the global `autoscaling.scaledObjectOptions` with specific scaled options for chrome nodes | +| `firefoxNode.enabled` | `true` | Enable firefox nodes | +| `firefoxNode.deploymentEnabled` | `true` | Enable creation of Deployment for firefox nodes | +| `firefoxNode.replicas` | `1` | Number of firefox nodes. Disabled if autoscaling is enabled. | +| `firefoxNode.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `firefoxNode.imageName` | `node-firefox` | Image of firefox nodes | +| `firefoxNode.imageTag` | `4.16.1-20231212` | Image of firefox nodes | +| `firefoxNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `firefoxNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `firefoxNode.ports` | `[5555]` | Port list to enable on container | +| `firefoxNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | +| `firefoxNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | +| `firefoxNode.annotations` | `{}` | Annotations for firefox-node pods | +| `firefoxNode.labels` | `{}` | Labels for firefox-node pods | +| `firefoxNode.resources` | `See values.yaml` | Resources for firefox-node pods | +| `firefoxNode.securityContext` | `See values.yaml` | Security context for firefox-node pods | +| `firefoxNode.tolerations` | `[]` | Tolerations for firefox-node pods | +| `firefoxNode.nodeSelector` | `{}` | Node Selector for firefox-node pods | +| `firefoxNode.affinity` | `{}` | Affinity for firefox-node pods | +| `firefoxNode.hostAliases` | `nil` | Custom host aliases for firefox nodes | +| `firefoxNode.priorityClassName` | `""` | Priority class name for firefox-node pods | +| `firefoxNode.extraEnvironmentVariables` | `nil` | Custom environment variables for firefox nodes | +| `firefoxNode.extraEnvFrom` | `nil` | Custom environment variables taken from `configMap` or `secret` for firefox nodes | +| `firefoxNode.service.enabled` | `true` | Create a service for node | +| `firefoxNode.service.type` | `ClusterIP` | Service type | +| `firefoxNode.service.loadBalancerIP` | `` | Set specific loadBalancerIP when serviceType is LoadBalancer | +| `firefoxNode.service.ports` | `[]` | Extra ports exposed in node service | +| `firefoxNode.service.annotations` | `{}` | Custom annotations for service | +| `firefoxNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | +| `firefoxNode.startupProbe` | `{}` | Probe to check pod is started successfully | +| `firefoxNode.livenessProbe` | `{}` | Liveness probe settings | +| `firefoxNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | +| `firefoxNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | +| `firefoxNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | +| `firefoxNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | +| `firefoxNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | +| `firefoxNode.hpa.browserName` | `firefox` | BrowserName from the capability | +| `firefoxNode.hpa.browserVersion` | `` | BrowserVersion from the capability | +| `firefoxNode.scaledOptions` | See `values.yaml` | Override the global `autoscaling.scaledOptions` with specific scaled options for firefox nodes | +| `firefoxNode.scaledJobOptions` | See `values.yaml` | Override the global `autoscaling.scaledJobOptions` with specific scaled options for firefox nodes | +| `firefoxNode.scaledObjectOptions` | See `values.yaml` | Override the global `autoscaling.scaledObjectOptions` with specific scaled options for firefox nodes | +| `edgeNode.enabled` | `true` | Enable edge nodes | +| `edgeNode.deploymentEnabled` | `true` | Enable creation of Deployment for edge nodes | +| `edgeNode.replicas` | `1` | Number of edge nodes. Disabled if autoscaling is enabled. | +| `edgeNode.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `edgeNode.imageName` | `node-edge` | Image of edge nodes | +| `edgeNode.imageTag` | `4.16.1-20231212` | Image of edge nodes | +| `edgeNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `edgeNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `edgeNode.ports` | `[5555]` | Port list to enable on container | +| `edgeNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | +| `edgeNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | +| `edgeNode.annotations` | `{}` | Annotations for edge-node pods | +| `edgeNode.labels` | `{}` | Labels for edge-node pods | +| `edgeNode.resources` | `See values.yaml` | Resources for edge-node pods | +| `edgeNode.securityContext` | `See values.yaml` | Security context for edge-node pods | +| `edgeNode.tolerations` | `[]` | Tolerations for edge-node pods | +| `edgeNode.nodeSelector` | `{}` | Node Selector for edge-node pods | +| `edgeNode.affinity` | `{}` | Affinity for edge-node pods | +| `edgeNode.hostAliases` | `nil` | Custom host aliases for edge nodes | +| `edgeNode.priorityClassName` | `""` | Priority class name for edge-node pods | +| `edgeNode.extraEnvironmentVariables` | `nil` | Custom environment variables for firefox nodes | +| `edgeNode.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for firefox nodes | +| `edgeNode.service.enabled` | `true` | Create a service for node | +| `edgeNode.service.type` | `ClusterIP` | Service type | +| `edgeNode.service.loadBalancerIP` | `` | Set specific loadBalancerIP when serviceType is LoadBalancer | +| `edgeNode.service.ports` | `[]` | Extra ports exposed in node service | +| `edgeNode.service.annotations` | `{}` | Custom annotations for service | +| `edgeNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | +| `edgeNode.startupProbe` | `{}` | Probe to check pod is started successfully | +| `edgeNode.livenessProbe` | `{}` | Liveness probe settings | +| `edgeNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | +| `edgeNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | +| `edgeNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | +| `edgeNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | +| `edgeNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | +| `edgeNode.hpa.browserName` | `edge` | BrowserName from the capability | +| `edgeNode.hpa.browserVersion` | `` | BrowserVersion from the capability | +| `edgeNode.scaledOptions` | See `values.yaml` | Override the global `autoscaling.scaledOptions` with specific scaled options for edge nodes | +| `edgeNode.scaledJobOptions` | See `values.yaml` | Override the global `autoscaling.scaledJobOptions` with specific scaled options for edge nodes | +| `edgeNode.scaledObjectOptions` | See `values.yaml` | Override the global `autoscaling.scaledObjectOptions` with specific scaled options for edge nodes | +| `videoRecorder.enabled` | `false` | Enable video recorder for node | +| `videoRecorder.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `videoRecorder.imageName` | `video` | Selenium video recoder image name | +| `videoRecorder.imageTag` | `ffmpeg-6.1-20231212` | Image tag of video recorder | +| `videoRecorder.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `videoRecorder.uploader` | `false` | Name of the uploader to use. The value `false` is used to disable uploader. Supported default `s3` | +| `videoRecorder.uploadDestinationPrefix` | `false` | Destination URL for uploading video file. The value `false` is used to disable the uploading | +| `videoRecorder.ports` | `[9000]` | Port list to enable on video recorder container | +| `videoRecorder.resources` | `See values.yaml` | Resources for video recorder | +| `videoRecorder.extraEnvironmentVariables` | `nil` | Custom environment variables for video recorder | +| `videoRecorder.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for video recorder | +| `videoRecorder.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | +| `videoRecorder.startupProbe` | `{}` | Probe to check pod is started successfully | +| `videoRecorder.livenessProbe` | `{}` | Liveness probe settings | +| `videoRecorder.volume.name.folder` | `video` | Name is used to set for the volume to persist and share output video folder in container | +| `videoRecorder.volume.name.scripts` | `video-scripts` | Name is used to set for the volume to persist and share video recorder scripts in container | +| `videoRecorder.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | +| `videoRecorder.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | +| `videoRecorder.s3` | `See values.yaml` | Container spec for the uploader if `videoRecorder.uploader` is `s3`. Similarly, create for your new uploader | +| `videoRecorder.s3.resources` | `See values.yaml` | Resources for video uploader | +| `videoRecorder.s3.extraEnvironmentVariables` | `` | Custom environment variables for video uploader container | +| `videoRecorder.s3.extraEnvFrom` | `` | Custom environment taken from `configMap` or `secret` variables for video uploader | +| `videoRecorder.s3.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod of video uploader | +| `customLabels` | `{}` | Custom labels for k8s resources | ### Configuration of KEDA @@ -237,7 +336,8 @@ You can configure the Selenium Hub with these values: | Parameter | Default | Description | |---------------------------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| -| `hub.imageName` | `selenium/hub` | Selenium Hub image name | +| `hub.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `hub.imageName` | `hub` | Selenium Hub image name | | `hub.imageTag` | `nil` | Selenium Hub image tag (this overwrites `.global.seleniumGrid.imageTag` value) | | `hub.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | | `hub.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | @@ -268,84 +368,89 @@ You can configure the Selenium Hub with these values: If you implement selenium-grid with separate components (`isolateComponents: true`), you can configure all components via the following values: -| Parameter | Default | Description | -|----------------------------------------------|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| -| `components.router.imageName` | `selenium/router` | Router image name | -| `components.router.imageTag` | `nil` | Router image tag (this overwrites `.global.seleniumGrid.imageTag` value) | -| `components.router.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `components.router.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `components.router.annotations` | `{}` | Custom annotations for router pod | -| `components.router.port` | `4444` | Router port | -| `components.router.livenessProbe` | `See values.yaml` | Liveness probe settings | -| `components.router.readinessProbe` | `See values.yaml` | Readiness probe settings | -| `components.router.resources` | `{}` | Resources for router pod | -| `components.router.securityContext` | `See values.yaml` | Security context for router pod | -| `components.router.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | -| `components.router.loadBalancerIP` | `nil` | Set specific loadBalancerIP when serviceType is LoadBalancer (see https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) | -| `components.router.serviceAnnotations` | `{}` | Custom annotations for router service | -| `components.router.tolerations` | `[]` | Tolerations for router pods | -| `components.router.nodeSelector` | `{}` | Node Selector for router pods | -| `components.router.affinity` | `{}` | Affinity for router pods | -| `components.router.priorityClassName` | `""` | Priority class name for router pods | -| `components.distributor.imageName` | `selenium/distributor` | Distributor image name | -| `components.distributor.imageTag` | `nil` | Distributor image tag (this overwrites `.global.seleniumGrid.imageTag` value) | -| `components.distributor.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `components.distributor.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `components.distributor.annotations` | `{}` | Custom annotations for Distributor pod | -| `components.distributor.port` | `5553` | Distributor port | -| `components.distributor.resources` | `{}` | Resources for Distributor pod | -| `components.distributor.securityContext` | `See values.yaml` | Security context for Distributor pod | -| `components.distributor.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | -| `components.distributor.serviceAnnotations` | `{}` | Custom annotations for Distributor service | -| `components.distributor.tolerations` | `[]` | Tolerations for Distributor pods | -| `components.distributor.nodeSelector` | `{}` | Node Selector for Distributor pods | -| `components.distributor.affinity` | `{}` | Affinity for Distributor pods | -| `components.distributor.priorityClassName` | `""` | Priority class name for Distributor pods | -| `components.eventBus.imageName` | `selenium/event-bus` | Event Bus image name | -| `components.eventBus.imageTag` | `nil` | Event Bus image tag (this overwrites `.global.seleniumGrid.imageTag` value) | -| `components.eventBus.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `components.eventBus.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `components.eventBus.annotations` | `{}` | Custom annotations for Event Bus pod | -| `components.eventBus.port` | `5557` | Event Bus port | -| `components.eventBus.publishPort` | `4442` | Port where events are published | -| `components.eventBus.subscribePort` | `4443` | Port where to subscribe for events | -| `components.eventBus.resources` | `{}` | Resources for event-bus pod | -| `components.eventBus.securityContext` | `See values.yaml` | Security context for event-bus pod | -| `components.eventBus.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | -| `components.eventBus.serviceAnnotations` | `{}` | Custom annotations for Event Bus service | -| `components.eventBus.tolerations` | `[]` | Tolerations for Event Bus pods | -| `components.eventBus.nodeSelector` | `{}` | Node Selector for Event Bus pods | -| `components.eventBus.affinity` | `{}` | Affinity for Event Bus pods | -| `components.eventBus.priorityClassName` | `""` | Priority class name for Event Bus pods | -| `components.sessionMap.imageName` | `selenium/sessions` | Session Map image name | -| `components.sessionMap.imageTag` | `nil` | Session Map image tag (this overwrites `.global.seleniumGrid.imageTag` value) | -| `components.sessionMap.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `components.sessionMap.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `components.sessionMap.annotations` | `{}` | Custom annotations for Session Map pod | -| `components.sessionMap.resources` | `{}` | Resources for Session Map pod | -| `components.sessionMap.securityContext` | `See values.yaml` | Security context for Session Map pod | -| `components.sessionMap.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | -| `components.sessionMap.serviceAnnotations` | `{}` | Custom annotations for Session Map service | -| `components.sessionMap.tolerations` | `[]` | Tolerations for Session Map pods | -| `components.sessionMap.nodeSelector` | `{}` | Node Selector for Session Map pods | -| `components.sessionMap.affinity` | `{}` | Affinity for Session Map pods | -| `components.sessionMap.priorityClassName` | `""` | Priority class name for Session Map pods | -| `components.sessionQueue.imageName` | `selenium/session-queue` | Session Queue image name | -| `components.sessionQueue.imageTag` | `nil` | Session Queue image tag (this overwrites `.global.seleniumGrid.imageTag` value) | -| `components.sessionQueue.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `components.sessionQueue.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `components.sessionQueue.annotations` | `{}` | Custom annotations for Session Queue pod | -| `components.sessionQueue.port` | `5559` | Session Queue Port | -| `components.sessionQueue.resources` | `{}` | Resources for Session Queue pod | -| `components.sessionQueue.securityContext` | `See values.yaml` | Security context for Session Queue pod | -| `components.sessionQueue.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | -| `components.sessionQueue.serviceAnnotations` | `{}` | Custom annotations for Session Queue service | -| `components.sessionQueue.tolerations` | `[]` | Tolerations for Session Queue pods | -| `components.sessionQueue.nodeSelector` | `{}` | Node Selector for Session Queue pods | -| `components.sessionQueue.affinity` | `{}` | Affinity for Session Queue pods | -| `components.sessionQueue.priorityClassName` | `""` | Priority class name for Session Queue pods | -| `components.subPath` | `/` | Custom sub path for all components | -| `components.extraEnvironmentVariables` | `nil` | Custom environment variables for all components | -| `components.extraEnvFrom` | `nil` | Custom environment variables taken from `configMap` or `secret` for all components | +| Parameter | Default | Description | +|----------------------------------------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| `components.router.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `components.router.imageName` | `router` | Router image name | +| `components.router.imageTag` | `nil` | Router image tag (this overwrites `.global.seleniumGrid.imageTag` value) | +| `components.router.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `components.router.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `components.router.annotations` | `{}` | Custom annotations for router pod | +| `components.router.port` | `4444` | Router port | +| `components.router.livenessProbe` | `See values.yaml` | Liveness probe settings | +| `components.router.readinessProbe` | `See values.yaml` | Readiness probe settings | +| `components.router.resources` | `{}` | Resources for router pod | +| `components.router.securityContext` | `See values.yaml` | Security context for router pod | +| `components.router.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | +| `components.router.loadBalancerIP` | `nil` | Set specific loadBalancerIP when serviceType is LoadBalancer (see https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) | +| `components.router.serviceAnnotations` | `{}` | Custom annotations for router service | +| `components.router.tolerations` | `[]` | Tolerations for router pods | +| `components.router.nodeSelector` | `{}` | Node Selector for router pods | +| `components.router.affinity` | `{}` | Affinity for router pods | +| `components.router.priorityClassName` | `""` | Priority class name for router pods | +| `components.distributor.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `components.distributor.imageName` | `distributor` | Distributor image name | +| `components.distributor.imageTag` | `nil` | Distributor image tag (this overwrites `.global.seleniumGrid.imageTag` value) | +| `components.distributor.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `components.distributor.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `components.distributor.annotations` | `{}` | Custom annotations for Distributor pod | +| `components.distributor.port` | `5553` | Distributor port | +| `components.distributor.resources` | `{}` | Resources for Distributor pod | +| `components.distributor.securityContext` | `See values.yaml` | Security context for Distributor pod | +| `components.distributor.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | +| `components.distributor.serviceAnnotations` | `{}` | Custom annotations for Distributor service | +| `components.distributor.tolerations` | `[]` | Tolerations for Distributor pods | +| `components.distributor.nodeSelector` | `{}` | Node Selector for Distributor pods | +| `components.distributor.affinity` | `{}` | Affinity for Distributor pods | +| `components.distributor.priorityClassName` | `""` | Priority class name for Distributor pods | +| `components.eventBus.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `components.eventBus.imageName` | `event-bus` | Event Bus image name | +| `components.eventBus.imageTag` | `nil` | Event Bus image tag (this overwrites `.global.seleniumGrid.imageTag` value) | +| `components.eventBus.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `components.eventBus.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `components.eventBus.annotations` | `{}` | Custom annotations for Event Bus pod | +| `components.eventBus.port` | `5557` | Event Bus port | +| `components.eventBus.publishPort` | `4442` | Port where events are published | +| `components.eventBus.subscribePort` | `4443` | Port where to subscribe for events | +| `components.eventBus.resources` | `{}` | Resources for event-bus pod | +| `components.eventBus.securityContext` | `See values.yaml` | Security context for event-bus pod | +| `components.eventBus.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | +| `components.eventBus.serviceAnnotations` | `{}` | Custom annotations for Event Bus service | +| `components.eventBus.tolerations` | `[]` | Tolerations for Event Bus pods | +| `components.eventBus.nodeSelector` | `{}` | Node Selector for Event Bus pods | +| `components.eventBus.affinity` | `{}` | Affinity for Event Bus pods | +| `components.eventBus.priorityClassName` | `""` | Priority class name for Event Bus pods | +| `components.sessionMap.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `components.sessionMap.imageName` | `sessions` | Session Map image name | +| `components.sessionMap.imageTag` | `nil` | Session Map image tag (this overwrites `.global.seleniumGrid.imageTag` value) | +| `components.sessionMap.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `components.sessionMap.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `components.sessionMap.annotations` | `{}` | Custom annotations for Session Map pod | +| `components.sessionMap.resources` | `{}` | Resources for Session Map pod | +| `components.sessionMap.securityContext` | `See values.yaml` | Security context for Session Map pod | +| `components.sessionMap.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | +| `components.sessionMap.serviceAnnotations` | `{}` | Custom annotations for Session Map service | +| `components.sessionMap.tolerations` | `[]` | Tolerations for Session Map pods | +| `components.sessionMap.nodeSelector` | `{}` | Node Selector for Session Map pods | +| `components.sessionMap.affinity` | `{}` | Affinity for Session Map pods | +| `components.sessionMap.priorityClassName` | `""` | Priority class name for Session Map pods | +| `components.sessionQueue.imageRegistry` | `nil` | Distribution registry to pull the image (this overwrites `.global.seleniumGrid.imageRegistry` value) | +| `components.sessionQueue.imageName` | `session-queue` | Session Queue image name | +| `components.sessionQueue.imageTag` | `nil` | Session Queue image tag (this overwrites `.global.seleniumGrid.imageTag` value) | +| `components.sessionQueue.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `components.sessionQueue.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `components.sessionQueue.annotations` | `{}` | Custom annotations for Session Queue pod | +| `components.sessionQueue.port` | `5559` | Session Queue Port | +| `components.sessionQueue.resources` | `{}` | Resources for Session Queue pod | +| `components.sessionQueue.securityContext` | `See values.yaml` | Security context for Session Queue pod | +| `components.sessionQueue.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | +| `components.sessionQueue.serviceAnnotations` | `{}` | Custom annotations for Session Queue service | +| `components.sessionQueue.tolerations` | `[]` | Tolerations for Session Queue pods | +| `components.sessionQueue.nodeSelector` | `{}` | Node Selector for Session Queue pods | +| `components.sessionQueue.affinity` | `{}` | Affinity for Session Queue pods | +| `components.sessionQueue.priorityClassName` | `""` | Priority class name for Session Queue pods | +| `components.subPath` | `/` | Custom sub path for all components | +| `components.extraEnvironmentVariables` | `nil` | Custom environment variables for all components | +| `components.extraEnvFrom` | `nil` | Custom environment variables taken from `configMap` or `secret` for all components | See how to customize a helm chart installation in the [Helm Docs](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing) for more information. diff --git a/charts/selenium-grid/TESTING.md b/charts/selenium-grid/TESTING.md new file mode 100644 index 000000000..7e94f85e4 --- /dev/null +++ b/charts/selenium-grid/TESTING.md @@ -0,0 +1,77 @@ +# 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 | Test via | +|------------------------|----------------------------------------------------------------------|----------|----------| +| Basic Auth | Basic Auth is disabled | ✓ | Cluster | +| | Basic Auth is enabled | ✗ | | +| Auto scaling | Auto scaling with `enableWithExistingKEDA` is `true` | ✓ | Cluster | +| | Auto scaling with `scalingType` is `job` | ✓ | Cluster | +| | Auto scaling with `scalingType` is `deployment` | ✗ | | +| | Auto scaling with `autoscaling.scaledOptions.minReplicaCount` is `0` | ✓ | Cluster | +| | Parallel tests execution against node autoscaling | ✓ | Cluster | +| Ingress | Ingress is enabled without `hostname` | ✓ | Cluster | +| | Ingress is enabled with `hostname` is set | ✗ | | +| | Hub `sub-path` is set with Ingress `ImplementationSpecific` paths | ✓ | Cluster | +| | `ingress.nginx` configs for NGINX ingress controller annotations | ✓ | Template | +| Distributed components | `isolateComponents` is enabled | ✓ | Cluster | +| | `isolateComponents` is disabled | ✓ | Cluster | +| Browser Nodes | Node `nameOverride` is set | ✓ | Cluster | +| | Sanity tests in node | ✓ | Cluster | +| | Video recorder is enabled in node | ✗ | | +| | Node `extraEnvironmentVariables` is set value | ✓ | Cluster | +| General | Set new image registry via `global.seleniumGrid.imageRegistry` | ✓ | Cluster | +| | Components are able to set `.affinity` | ✓ | Template | +| Tracing | Enable tracing via `SE_ENABLE_TRACING` | ✓ | Cluster | +| | Disable tracing via `SE_ENABLE_TRACING` | ✓ | Cluster | + +## Test Chart Template +- By using `helm template` command, the chart template is tested without installing it to Kubernetes cluster. +- Templates are rendered and the output as a YAML manifest file. The manifest file is then asserted with [pyyaml](https://pyyaml.org/wiki/PyYAMLDocumentation). +- Set of values are used to render the templates located in [tests/charts/templates/render](../../tests/charts/templates/render). + +```bash +# Back to root directory +cd ../.. + +# Build chart dependencies and lint +make chart_build + +# Test chart template +make chart_test_template +``` +- Build chart dependencies and lint requires [Chart Testing `ct`](https://github.com/helm/chart-testing). There is a config file [ct.yaml](../../tests/charts/config/ct.yaml) to configure the chart testing. + +## Build & test Docker images with deploy to Kubernetes cluster +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 ../.. + +# Setup Kubernetes environment +make chart_setup_env + +# Build Docker images +make build + +# Build and lint charts +make chart_build + +# Setup Kubernetes cluster +make chart_cluster_setup + +# Test Selenium Grid on Kubernetes +make chart_test + +# make chart_test_parallel_autoscaling + +# Cleanup Kubernetes cluster +make chart_cluster_cleanup +``` +- Setup Kubernetes environment requires [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/) and [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/). +- Set of values are used to deploy the chart to Kubernetes cluster located in [tests/charts/ci](../../tests/charts/ci). diff --git a/charts/selenium-grid/templates/NOTES.txt b/charts/selenium-grid/templates/NOTES.txt index 80920a44f..ce79f74b5 100644 --- a/charts/selenium-grid/templates/NOTES.txt +++ b/charts/selenium-grid/templates/NOTES.txt @@ -1,7 +1,7 @@ Selenium Grid Server deployed successfully. {{- $svcName := ternary (include "seleniumGrid.router.fullname" . ) (include "seleniumGrid.hub.fullname" . ) .Values.isolateComponents }} -{{- $appName := ternary "selenium-router" "selenium-hub" .Values.isolateComponents }} +{{- $appName := ternary (include "seleniumGrid.router.fullname" . ) (include "seleniumGrid.hub.fullname" . ) .Values.isolateComponents }} {{- $serviceType := ternary .Values.components.router.serviceType .Values.hub.serviceType .Values.isolateComponents }} {{- $port := ternary .Values.components.router.port .Values.hub.port .Values.isolateComponents }} {{- $localUrl := ternary "http://localhost:PORT" "http://localhost:PORT/wd/hub" .Values.isolateComponents }} diff --git a/charts/selenium-grid/templates/_helpers.tpl b/charts/selenium-grid/templates/_helpers.tpl index 694b7a5c9..7fbd6e094 100644 --- a/charts/selenium-grid/templates/_helpers.tpl +++ b/charts/selenium-grid/templates/_helpers.tpl @@ -58,6 +58,13 @@ Chrome node fullname {{- default "selenium-chrome-node" .Values.chromeNode.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} +{{/* +Chromium node fullname +*/}} +{{- define "seleniumGrid.chromiumNode.fullname" -}} +{{- default "selenium-chromium-node" .Values.chromiumNode.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + {{/* Firefox node fullname */}} @@ -80,6 +87,29 @@ Ingress fullname {{- default "selenium-ingress" .Values.ingress.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} +{{- define "seleniumGrid.ingress.nginx.annotations.default" -}} +{{- with .Values.ingress.nginx }} + {{- with .proxyTimeout }} +nginx.ingress.kubernetes.io/proxy-connect-timeout: {{ . | quote }} +nginx.ingress.kubernetes.io/proxy-send-timeout: {{ . | quote }} +nginx.ingress.kubernetes.io/proxy-read-timeout: {{ . | quote }} +nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: {{ . | quote }} +nginx.ingress.kubernetes.io/auth-keepalive-timeout: {{ . | quote }} + {{- end }} + {{- with .proxyBuffer }} +nginx.ingress.kubernetes.io/proxy-request-buffering: "on" +nginx.ingress.kubernetes.io/proxy-buffering: "on" + {{- with .size }} +nginx.ingress.kubernetes.io/proxy-buffer-size: {{ . | quote }} +nginx.ingress.kubernetes.io/client-body-buffer-size: {{ . | quote }} + {{- end }} + {{- with .number }} +nginx.ingress.kubernetes.io/proxy-buffers-number: {{ . | quote }} + {{- end }} + {{- end }} +{{- end }} +{{- end -}} + {{/* Service Account fullname */}} @@ -87,6 +117,13 @@ Service Account fullname {{- .Values.serviceAccount.name | default "selenium-serviceaccount" | trunc 63 | trimSuffix "-" -}} {{- end -}} +{{/* +Video ConfigMap fullname +*/}} +{{- define "seleniumGrid.video.fullname" -}} +{{- default "selenium-video" .Values.videoRecorder.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + {{/* Is autoscaling using KEDA enabled */}} @@ -94,6 +131,46 @@ Is autoscaling using KEDA enabled {{- or .Values.autoscaling.enabled .Values.autoscaling.enableWithExistingKEDA | ternary "true" "" -}} {{- end -}} +{{/* +Common autoscaling spec template +*/}} +{{- define "seleniumGrid.autoscalingTemplate" -}} +{{- $spec := toYaml (dict) -}} +{{/* Merge with precedence from right to left */}} +{{- with .Values.autoscaling.scaledOptions -}} + {{- $spec = mergeOverwrite ($spec | fromYaml) . | toYaml -}} +{{- end -}} +{{- with .node.scaledOptions -}} + {{- $spec = mergeOverwrite ($spec | fromYaml) . | toYaml -}} +{{- end -}} +{{- if eq .Values.autoscaling.scalingType "deployment" -}} + {{- with .Values.autoscaling.scaledObjectOptions -}} + {{- $spec = mergeOverwrite ($spec | fromYaml) . | toYaml -}} + {{- end -}} + {{- with .node.scaledObjectOptions -}} + {{- $spec = mergeOverwrite ($spec | fromYaml) . | toYaml -}} + {{- end -}} + {{- $spec = mergeOverwrite ($spec | fromYaml) (dict "scaleTargetRef" (dict "name" .name)) | toYaml -}} +{{- else if eq .Values.autoscaling.scalingType "job" -}} + {{- with .Values.autoscaling.scaledJobOptions -}} + {{- $spec = mergeOverwrite ($spec | fromYaml) . | toYaml -}} + {{- end -}} + {{- with .node.scaledJobOptions -}} + {{- $spec = mergeOverwrite ($spec | fromYaml) . | toYaml -}} + {{- end -}} + {{- $spec = mergeOverwrite ($spec | fromYaml) (dict "jobTargetRef" .podTemplate) | toYaml -}} +{{- end -}} +{{- if and $spec (ne $spec "{}") -}} + {{ tpl $spec $ }} +{{- end -}} +{{- if not .Values.autoscaling.scaledOptions.triggers }} +triggers: + - type: selenium-grid + {{- with .node.hpa }} + metadata: {{- tpl (toYaml .) $ | nindent 6 }} + {{- end }} +{{- end }} +{{- end -}} {{/* Common pod template @@ -126,7 +203,8 @@ template: containers: - name: {{.name}} {{- $imageTag := default .Values.global.seleniumGrid.nodesImageTag .node.imageTag }} - image: {{ printf "%s:%s" .node.imageName $imageTag }} + {{- $imageRegistry := default .Values.global.seleniumGrid.imageRegistry .node.imageRegistry }} + image: {{ printf "%s/%s:%s" $imageRegistry .node.imageName $imageTag }} imagePullPolicy: {{ .node.imagePullPolicy }} {{- with .node.extraEnvironmentVariables }} env: {{- tpl (toYaml .) $ | nindent 10 }} @@ -137,7 +215,7 @@ template: - configMapRef: name: {{ .Values.nodeConfigMap.name }} {{- with .node.extraEnvFrom }} - {{- toYaml . | nindent 10 }} + {{- tpl (toYaml .) $ | nindent 10 }} {{- end }} {{- if gt (len .node.ports) 0 }} ports: @@ -150,7 +228,7 @@ template: - name: dshm mountPath: /dev/shm {{- if .node.extraVolumeMounts }} - {{- toYaml .node.extraVolumeMounts | nindent 10 }} + {{- tpl (toYaml .node.extraVolumeMounts) $ | nindent 10 }} {{- end }} {{- with .node.resources }} resources: {{- toYaml . | nindent 10 }} @@ -168,6 +246,71 @@ template: {{- if .node.sidecars }} {{- toYaml .node.sidecars | nindent 6 }} {{- end }} + {{- if .Values.videoRecorder.enabled }} + - name: video + {{- $imageTag := default .Values.global.seleniumGrid.videoImageTag .Values.videoRecorder.imageTag }} + {{- $imageRegistry := default .Values.global.seleniumGrid.imageRegistry .Values.videoRecorder.imageRegistry }} + image: {{ printf "%s/%s:%s" $imageRegistry .Values.videoRecorder.imageName $imageTag }} + imagePullPolicy: {{ .Values.videoRecorder.imagePullPolicy }} + env: + - name: UPLOAD_DESTINATION_PREFIX + value: {{ .Values.videoRecorder.uploadDestinationPrefix | quote }} + {{- with .Values.videoRecorder.extraEnvironmentVariables }} + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + envFrom: + - configMapRef: + name: {{ .Values.busConfigMap.name }} + {{- with .Values.videoRecorder.extraEnvFrom }} + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- if gt (len .Values.videoRecorder.ports) 0 }} + ports: + {{- range .Values.videoRecorder.ports }} + - containerPort: {{ . }} + protocol: TCP + {{- end }} + {{- end }} + volumeMounts: + - name: dshm + mountPath: /dev/shm + {{- tpl (include "seleniumGrid.video.volumeMounts" .) $ | nindent 8 }} + {{- with .Values.videoRecorder.resources }} + resources: {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.videoRecorder.startupProbe }} + startupProbe: {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.videoRecorder.livenessProbe }} + livenessProbe: {{- toYaml . | nindent 10 }} + {{- end }} + {{- if .uploader }} + - name: uploader + image: {{ printf "%s:%s" .uploader.imageName .uploader.imageTag }} + imagePullPolicy: {{ .uploader.imagePullPolicy }} + {{- with .uploader.command }} + command: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- with .uploader.args }} + args: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- with .uploader.extraEnvironmentVariables }} + env: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- with .uploader.extraEnvFrom }} + envFrom: + {{- tpl (toYaml .) $ | nindent 10 }} + {{- end }} + volumeMounts: + {{- tpl (include "seleniumGrid.video.uploader.volumeMounts" .) $ | nindent 8 }} + {{- with .uploader.resources }} + resources: {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .uploader.securityContext }} + securityContext: {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} {{- if or .Values.global.seleniumGrid.imagePullSecret .node.imagePullSecret }} imagePullSecrets: - name: {{ default .Values.global.seleniumGrid.imagePullSecret .node.imagePullSecret }} @@ -175,6 +318,9 @@ template: {{- with .node.nodeSelector }} nodeSelector: {{- toYaml . | nindent 6 }} {{- end }} + {{- with .node.affinity }} + affinity: {{- toYaml . | nindent 6 }} + {{- end }} {{- with .node.tolerations }} tolerations: {{ toYaml . | nindent 4 }} @@ -189,7 +335,10 @@ template: medium: Memory sizeLimit: {{ default "1Gi" .node.dshmVolumeSizeLimit }} {{- if .node.extraVolumes }} - {{ toYaml .node.extraVolumes | nindent 6 }} + {{ tpl (toYaml .node.extraVolumes) $ | nindent 6 }} + {{- end }} + {{- if .Values.videoRecorder.enabled }} + {{- tpl (include "seleniumGrid.video.volumes" .) $ | nindent 6 }} {{- end }} {{- end -}} @@ -204,6 +353,16 @@ http://{{- if eq .Values.basicAuth.enabled true}}{{ .Values.basicAuth.username}} {{- end }} {{- end -}} +{{- define "seleniumGrid.url.subPath" -}} +{{- $subPath := "/" -}} +{{ if $.Values.isolateComponents }} + {{- $subPath = default $subPath $.Values.components.subPath -}} +{{- else -}} + {{- $subPath = default $subPath $.Values.hub.subPath -}} +{{- end -}} +{{ $subPath }} +{{- end -}} + {{/* Graphql Url of the hub or the router */}} @@ -224,3 +383,102 @@ deployment preStop hook to deregister from the selenium hub. lifecycle: {{ $lifecycle | nindent 2 }} {{- end -}} {{- end -}} + +{{/* +Default specs of VolumeMounts and Volumes for video recorder +*/}} +{{- define "seleniumGrid.video.volume.name.folder" -}} +{{- $name := default "video" (((.Values.videoRecorder).volume).name).folder -}} +{{- $name -}} +{{- end -}} + +{{- define "seleniumGrid.video.volume.name.scripts" -}} +{{- $name := default "video-scripts" (((.Values.videoRecorder).volume).name).scripts -}} +{{- $name -}} +{{- end -}} + +{{- define "seleniumGrid.video.volumeMounts.default" -}} +- name: {{ include "seleniumGrid.video.volume.name.scripts" . }} + mountPath: /opt/bin/video.sh + subPath: video.sh +- name: {{ include "seleniumGrid.video.volume.name.folder" . }} + mountPath: /videos +{{- end -}} + +{{- define "seleniumGrid.video.volumes.default" -}} +- name: {{ include "seleniumGrid.video.volume.name.scripts" . }} + configMap: + name: {{ template "seleniumGrid.video.fullname" . }} + defaultMode: 0500 +- name: {{ include "seleniumGrid.video.volume.name.folder" . }} + emptyDir: {} +{{- end -}} + +{{- define "seleniumGrid.video.uploader.volumeMounts.default" -}} +- name: {{ include "seleniumGrid.video.volume.name.folder" . }} + mountPath: /videos +{{- end -}} + +{{/* Combine videoRecorder.extraVolumeMounts with the default ones for container video recorder */}} +{{- define "seleniumGrid.video.volumeMounts" -}} +{{- $videoVolumeMounts := list -}} +{{- if .Values.videoRecorder.extraVolumeMounts -}} + {{- range .Values.videoRecorder.extraVolumeMounts -}} + {{- $videoVolumeMounts = append $videoVolumeMounts . -}} + {{- end -}} +{{- end -}} +{{- $defaultVolumeMounts := (include "seleniumGrid.video.volumeMounts.default" . | toString | fromYamlArray ) -}} +{{- $videoVolumeMounts = include "utils.appendDefaultIfNotExist" (dict "currentArray" $videoVolumeMounts "defaultArray" $defaultVolumeMounts "uniqueKey" "mountPath") -}} +{{- not (empty $videoVolumeMounts) | ternary $videoVolumeMounts "" -}} +{{- end -}} + +{{/* Combine videoRecorder.uploader.extraVolumeMounts with the default ones for container video uploader */}} +{{- define "seleniumGrid.video.uploader.volumeMounts" -}} +{{- $videoUploaderVolumeMounts := list -}} +{{- if .uploader.extraVolumeMounts -}} + {{- range .uploader.extraVolumeMounts -}} + {{- $videoUploaderVolumeMounts = append $videoUploaderVolumeMounts . -}} + {{- end -}} +{{- end }} +{{- $defaultVolumeMounts := (include "seleniumGrid.video.uploader.volumeMounts.default" . | toString | fromYamlArray ) -}} +{{- $videoUploaderVolumeMounts = include "utils.appendDefaultIfNotExist" (dict "currentArray" $videoUploaderVolumeMounts "defaultArray" $defaultVolumeMounts "uniqueKey" "mountPath") -}} +{{- not (empty $videoUploaderVolumeMounts) | ternary $videoUploaderVolumeMounts "" -}} +{{- end -}} + +{{/* Combine videoRecorder.extraVolumes with the default ones for the node pod */}} +{{- define "seleniumGrid.video.volumes" -}} +{{- $videoVolumes := list -}} +{{- if .Values.videoRecorder.extraVolumes -}} + {{- range .Values.videoRecorder.extraVolumes -}} + {{- $videoVolumes = append $videoVolumes . -}} + {{- end -}} +{{- end -}} +{{- $defaultVolumes := (include "seleniumGrid.video.volumes.default" . | toString | fromYamlArray ) -}} +{{- $videoVolumes = include "utils.appendDefaultIfNotExist" (dict "currentArray" $videoVolumes "defaultArray" $defaultVolumes "uniqueKey" "name") -}} +{{- not (empty $videoVolumes) | ternary $videoVolumes "" -}} +{{- end -}} + +{{/* +Is used to append default items needed to an array if they are not already present. Args: currentArray, defaultArray, uniqueKey +Usage: {{- $thisArray = include "utils.appendDefaultIfNotExist" (dict "currentArray" $thisArray "defaultArray" $defaultArray "uniqueKey" $uniqueKey }} +*/}} +{{- define "utils.appendDefaultIfNotExist" -}} + {{- $currentArray := index . "currentArray" -}} + {{- $defaultArray := index . "defaultArray" -}} + {{- $uniqueKey := index . "uniqueKey" -}} + {{- range $default := $defaultArray -}} + {{- if eq (len $currentArray) 0 -}} + {{- $currentArray = append $currentArray $default -}} + {{- end -}} + {{- $isExisting := false -}} + {{- range $current := $currentArray -}} + {{- if eq (index $default $uniqueKey | toString) (index $current $uniqueKey | toString) -}} + {{- $isExisting = true -}} + {{- end -}} + {{- end -}} + {{- if not $isExisting -}} + {{- $currentArray = append $currentArray $default -}} + {{- end -}} + {{- end -}} + {{- $currentArray | toYaml -}} +{{- end -}} diff --git a/charts/selenium-grid/templates/chrome-node-deployment.yaml b/charts/selenium-grid/templates/chrome-node-deployment.yaml index 0b3d2a9b5..e3bf3f1d2 100644 --- a/charts/selenium-grid/templates/chrome-node-deployment.yaml +++ b/charts/selenium-grid/templates/chrome-node-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.chromeNode.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-chrome-node - app.kubernetes.io/name: selenium-chrome-node + app: {{ template "seleniumGrid.chromeNode.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.chromeNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.chromeNode.labels }} {{- toYaml . | nindent 4 }} @@ -20,10 +20,11 @@ spec: {{end}} selector: matchLabels: - app: selenium-chrome-node + app: {{ template "seleniumGrid.chromeNode.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- $podScope := deepCopy . -}} -{{- $_ := set $podScope "name" "selenium-chrome-node" -}} -{{- $_ = set $podScope "node" .Values.chromeNode -}} +{{- $_ := set $podScope "name" (include "seleniumGrid.chromeNode.fullname" .) -}} +{{- $_ = set $podScope "node" .Values.chromeNode -}} +{{- $_ = set $podScope "uploader" (get .Values.videoRecorder (.Values.videoRecorder.uploader | toString)) -}} {{- include "seleniumGrid.podTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/chrome-node-hpa.yaml b/charts/selenium-grid/templates/chrome-node-hpa.yaml index 7cb78043c..74c588198 100644 --- a/charts/selenium-grid/templates/chrome-node-hpa.yaml +++ b/charts/selenium-grid/templates/chrome-node-hpa.yaml @@ -11,13 +11,8 @@ metadata: labels: deploymentName: {{ template "seleniumGrid.chromeNode.fullname" . }} spec: - maxReplicaCount: {{ .Values.chromeNode.maxReplicaCount }} - minReplicaCount: {{ .Values.chromeNode.minReplicaCount }} - scaleTargetRef: - name: {{ template "seleniumGrid.chromeNode.fullname" . }} - triggers: - - type: selenium-grid - {{- with .Values.chromeNode.hpa }} - metadata: {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} + {{- $podScope := deepCopy . -}} + {{- $_ := set $podScope "name" (include "seleniumGrid.chromeNode.fullname" .) -}} + {{- $_ = set $podScope "node" .Values.chromeNode -}} + {{- include "seleniumGrid.autoscalingTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/chrome-node-scaledjobs.yaml b/charts/selenium-grid/templates/chrome-node-scaledjobs.yaml index 4971aec74..0b0d34443 100644 --- a/charts/selenium-grid/templates/chrome-node-scaledjobs.yaml +++ b/charts/selenium-grid/templates/chrome-node-scaledjobs.yaml @@ -9,8 +9,8 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} labels: - app: selenium-chrome-node - app.kubernetes.io/name: selenium-chrome-node + app: {{ template "seleniumGrid.chromeNode.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.chromeNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.chromeNode.labels }} {{- toYaml . | nindent 4 }} @@ -19,21 +19,10 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - maxReplicaCount: {{ .Values.chromeNode.maxReplicaCount }} - {{- with .Values.autoscaling.scaledJobOptions -}} - {{ toYaml . | nindent 2 }} - {{- end }} - triggers: - - type: selenium-grid - {{- with .Values.chromeNode.hpa }} - metadata: {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} - jobTargetRef: - parallelism: 1 - completions: 1 - backoffLimit: 0 -{{- $podScope := deepCopy . -}} -{{- $_ := set $podScope "name" "selenium-chrome-node" -}} -{{- $_ = set $podScope "node" .Values.chromeNode -}} -{{- include "seleniumGrid.podTemplate" $podScope | nindent 4 }} + {{- $podScope := deepCopy . -}} + {{- $_ := set $podScope "name" (include "seleniumGrid.chromeNode.fullname" .) -}} + {{- $_ = set $podScope "node" .Values.chromeNode -}} + {{- $_ = set $podScope "uploader" (get .Values.videoRecorder (.Values.videoRecorder.uploader | toString)) -}} + {{- $_ = set $podScope "podTemplate" (include "seleniumGrid.podTemplate" $podScope | fromYaml) }} + {{- include "seleniumGrid.autoscalingTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/chrome-node-service.yaml b/charts/selenium-grid/templates/chrome-node-service.yaml index 41aebd570..efe8f1873 100644 --- a/charts/selenium-grid/templates/chrome-node-service.yaml +++ b/charts/selenium-grid/templates/chrome-node-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.chromeNode.fullname" . }} namespace: {{ .Release.Namespace }} labels: - name: selenium-chrome-node + name: {{ template "seleniumGrid.chromeNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.chromeNode.service.annotations }} annotations: @@ -14,11 +14,27 @@ metadata: spec: type: {{ .Values.chromeNode.service.type }} selector: - app: selenium-chrome-node + app: {{ template "seleniumGrid.chromeNode.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} + {{- if and (eq .Values.chromeNode.service.type "LoadBalancer") (.Values.chromeNode.service.loadBalancerIP) }} + loadBalancerIP: {{ .Values.chromeNode.service.loadBalancerIP }} + {{- end }} ports: - name: tcp-chrome protocol: TCP port: {{ .Values.chromeNode.seleniumServicePort }} targetPort: {{ .Values.chromeNode.seleniumPort }} + {{- with .Values.chromeNode.service.ports }} + {{- range . }} + - name: {{ .name }} + port: {{ .port }} + targetPort: {{ .targetPort }} + {{- if .protocol }} + protocol: {{ .protocol }} + {{- end }} + {{- if and (eq $.Values.chromeNode.service.type "NodePort") .nodePort }} + nodePort: {{ .nodePort }} + {{- end }} + {{- end }} + {{- end }} {{- end }} diff --git a/charts/selenium-grid/templates/chromium-node-deployment.yaml b/charts/selenium-grid/templates/chromium-node-deployment.yaml new file mode 100644 index 000000000..5bdd0fb4a --- /dev/null +++ b/charts/selenium-grid/templates/chromium-node-deployment.yaml @@ -0,0 +1,30 @@ +{{- if and .Values.chromiumNode.enabled ((eq (include "seleniumGrid.useKEDA" .) "true") | ternary (eq .Values.autoscaling.scalingType "deployment") .Values.chromiumNode.deploymentEnabled) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "seleniumGrid.chromiumNode.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "seleniumGrid.chromiumNode.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.chromiumNode.fullname" . }} + {{- include "seleniumGrid.commonLabels" . | nindent 4 }} + {{- with .Values.chromiumNode.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and (not .Values.autoscaling.enabled) (not .Values.autoscaling.enableWithExistingKEDA) }} + replicas: {{ .Values.chromiumNode.replicas }} + {{end}} + selector: + matchLabels: + app: {{ template "seleniumGrid.chromiumNode.fullname" . }} + app.kubernetes.io/instance: {{ .Release.Name }} +{{- $podScope := deepCopy . -}} +{{- $_ := set $podScope "name" (include "seleniumGrid.chromiumNode.fullname" .) -}} +{{- $_ = set $podScope "node" .Values.chromiumNode -}} +{{- $_ = set $podScope "uploader" (get .Values.videoRecorder (.Values.videoRecorder.uploader | toString)) -}} +{{- include "seleniumGrid.podTemplate" $podScope | nindent 2 }} +{{- end }} diff --git a/charts/selenium-grid/templates/chromium-node-hpa.yaml b/charts/selenium-grid/templates/chromium-node-hpa.yaml new file mode 100644 index 000000000..8777c003f --- /dev/null +++ b/charts/selenium-grid/templates/chromium-node-hpa.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.chromiumNode.enabled (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "deployment") }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: selenium-grid-chromium-scaledobject + namespace: {{ .Release.Namespace }} + annotations: + {{- with .Values.autoscaling.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + deploymentName: {{ template "seleniumGrid.chromiumNode.fullname" . }} +spec: + {{- $podScope := deepCopy . -}} + {{- $_ := set $podScope "name" (include "seleniumGrid.chromiumNode.fullname" .) -}} + {{- $_ = set $podScope "node" .Values.chromiumNode -}} + {{- include "seleniumGrid.autoscalingTemplate" $podScope | nindent 2 }} +{{- end }} diff --git a/charts/selenium-grid/templates/chromium-node-scaledjobs.yaml b/charts/selenium-grid/templates/chromium-node-scaledjobs.yaml new file mode 100644 index 000000000..d7bf26fea --- /dev/null +++ b/charts/selenium-grid/templates/chromium-node-scaledjobs.yaml @@ -0,0 +1,28 @@ +{{- if and .Values.chromiumNode.enabled (include "seleniumGrid.useKEDA" .) (eq .Values.autoscaling.scalingType "job") }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledJob +metadata: + name: {{ template "seleniumGrid.chromiumNode.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + {{- with .Values.autoscaling.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + app: {{ template "seleniumGrid.chromiumNode.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.chromiumNode.fullname" . }} + {{- include "seleniumGrid.commonLabels" . | nindent 4 }} + {{- with .Values.chromiumNode.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- $podScope := deepCopy . -}} + {{- $_ := set $podScope "name" (include "seleniumGrid.chromiumNode.fullname" .) -}} + {{- $_ = set $podScope "node" .Values.chromiumNode -}} + {{- $_ = set $podScope "uploader" (get .Values.videoRecorder (.Values.videoRecorder.uploader | toString)) -}} + {{- $_ = set $podScope "podTemplate" (include "seleniumGrid.podTemplate" $podScope | fromYaml) }} + {{- include "seleniumGrid.autoscalingTemplate" $podScope | nindent 2 }} +{{- end }} diff --git a/charts/selenium-grid/templates/chromium-node-service.yaml b/charts/selenium-grid/templates/chromium-node-service.yaml new file mode 100644 index 000000000..b9982ee21 --- /dev/null +++ b/charts/selenium-grid/templates/chromium-node-service.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.chromiumNode.enabled .Values.chromiumNode.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "seleniumGrid.chromiumNode.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + name: {{ template "seleniumGrid.chromiumNode.fullname" . }} + {{- include "seleniumGrid.commonLabels" . | nindent 4 }} + {{- with .Values.chromiumNode.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.chromiumNode.service.type }} + selector: + app: {{ template "seleniumGrid.chromiumNode.fullname" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- if and (eq .Values.chromiumNode.service.type "LoadBalancer") (.Values.chromiumNode.service.loadBalancerIP) }} + loadBalancerIP: {{ .Values.chromiumNode.service.loadBalancerIP }} + {{- end }} + ports: + - name: tcp-chromium + protocol: TCP + port: {{ .Values.chromiumNode.seleniumServicePort }} + targetPort: {{ .Values.chromiumNode.seleniumPort }} + {{- with .Values.chromiumNode.service.ports }} + {{- range . }} + - name: {{ .name }} + port: {{ .port }} + targetPort: {{ .targetPort }} + {{- if .protocol }} + protocol: {{ .protocol }} + {{- end }} + {{- if and (eq $.Values.chromiumNode.service.type "NodePort") .nodePort }} + nodePort: {{ .nodePort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/selenium-grid/templates/distributor-deployment.yaml b/charts/selenium-grid/templates/distributor-deployment.yaml index bf35d43ed..07462f15e 100644 --- a/charts/selenium-grid/templates/distributor-deployment.yaml +++ b/charts/selenium-grid/templates/distributor-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.distributor.fullname" . }} namespace: {{ .Release.Namespace }} labels: &distributor_labels - app: selenium-distributor - app.kubernetes.io/name: selenium-distributor + app: {{ template "seleniumGrid.distributor.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.distributor.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ spec: replicas: 1 selector: matchLabels: - app: selenium-distributor + app: {{ template "seleniumGrid.distributor.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: @@ -27,9 +27,10 @@ spec: serviceAccountName: {{ template "seleniumGrid.serviceAccount.fullname" . }} serviceAccount: {{ template "seleniumGrid.serviceAccount.fullname" . }} containers: - - name: selenium-distributor + - name: {{ template "seleniumGrid.distributor.fullname" . }} {{- $imageTag := default .Values.global.seleniumGrid.imageTag .Values.components.distributor.imageTag }} - image: {{ printf "%s:%s" .Values.components.distributor.imageName $imageTag }} + {{- $imageRegistry := default .Values.global.seleniumGrid.imageRegistry .Values.components.distributor.imageRegistry }} + image: {{ printf "%s/%s:%s" $imageRegistry .Values.components.distributor.imageName $imageTag }} imagePullPolicy: {{ .Values.components.distributor.imagePullPolicy }} env: - name: SE_SESSIONS_MAP_HOST diff --git a/charts/selenium-grid/templates/distributor-service.yaml b/charts/selenium-grid/templates/distributor-service.yaml index 067be8952..295407fac 100644 --- a/charts/selenium-grid/templates/distributor-service.yaml +++ b/charts/selenium-grid/templates/distributor-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.distributor.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-distributor + app: {{ template "seleniumGrid.distributor.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ metadata: {{- end }} spec: selector: - app: selenium-distributor + app: {{ template "seleniumGrid.distributor.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} type: {{ .Values.components.distributor.serviceType }} ports: diff --git a/charts/selenium-grid/templates/edge-node-deployment.yaml b/charts/selenium-grid/templates/edge-node-deployment.yaml index 883a45a76..9802239e8 100644 --- a/charts/selenium-grid/templates/edge-node-deployment.yaml +++ b/charts/selenium-grid/templates/edge-node-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.edgeNode.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-edge-node - app.kubernetes.io/name: selenium-edge-node + app: {{ template "seleniumGrid.edgeNode.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.edgeNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.edgeNode.labels }} {{- toYaml . | nindent 4 }} @@ -20,10 +20,11 @@ spec: {{end}} selector: matchLabels: - app: selenium-edge-node + app: {{ template "seleniumGrid.edgeNode.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- $podScope := deepCopy . -}} -{{- $_ := set $podScope "name" "selenium-edge-node" -}} -{{- $_ = set $podScope "node" .Values.edgeNode -}} +{{- $_ := set $podScope "name" (include "seleniumGrid.edgeNode.fullname" .) -}} +{{- $_ = set $podScope "node" .Values.edgeNode -}} +{{- $_ = set $podScope "uploader" (get .Values.videoRecorder (.Values.videoRecorder.uploader | toString)) -}} {{- include "seleniumGrid.podTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/edge-node-hpa.yaml b/charts/selenium-grid/templates/edge-node-hpa.yaml index b115de353..0839a3bdd 100644 --- a/charts/selenium-grid/templates/edge-node-hpa.yaml +++ b/charts/selenium-grid/templates/edge-node-hpa.yaml @@ -11,13 +11,8 @@ metadata: labels: deploymentName: {{ template "seleniumGrid.edgeNode.fullname" . }} spec: - maxReplicaCount: {{ .Values.edgeNode.maxReplicaCount }} - minReplicaCount: {{ .Values.edgeNode.minReplicaCount }} - scaleTargetRef: - name: {{ template "seleniumGrid.edgeNode.fullname" . }} - triggers: - - type: selenium-grid - {{- with .Values.edgeNode.hpa }} - metadata: {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} + {{- $podScope := deepCopy . -}} + {{- $_ := set $podScope "name" (include "seleniumGrid.edgeNode.fullname" .) -}} + {{- $_ = set $podScope "node" .Values.edgeNode -}} + {{- include "seleniumGrid.autoscalingTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/edge-node-scaledjob.yaml b/charts/selenium-grid/templates/edge-node-scaledjob.yaml index 6d113d186..132792c79 100644 --- a/charts/selenium-grid/templates/edge-node-scaledjob.yaml +++ b/charts/selenium-grid/templates/edge-node-scaledjob.yaml @@ -9,8 +9,8 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} labels: - app: selenium-edge-node - app.kubernetes.io/name: selenium-edge-node + app: {{ template "seleniumGrid.edgeNode.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.edgeNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.edgeNode.labels }} {{- toYaml . | nindent 4 }} @@ -19,21 +19,10 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - maxReplicaCount: {{ .Values.edgeNode.maxReplicaCount }} - {{- with .Values.autoscaling.scaledJobOptions -}} - {{ toYaml . | nindent 2 }} - {{- end }} - triggers: - - type: selenium-grid - {{- with .Values.edgeNode.hpa }} - metadata: {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} - jobTargetRef: - parallelism: 1 - completions: 1 - backoffLimit: 0 -{{- $podScope := deepCopy . -}} -{{- $_ := set $podScope "name" "selenium-edge-node" -}} -{{- $_ = set $podScope "node" .Values.edgeNode -}} -{{- include "seleniumGrid.podTemplate" $podScope | nindent 4 }} + {{- $podScope := deepCopy . -}} + {{- $_ := set $podScope "name" (include "seleniumGrid.edgeNode.fullname" .) -}} + {{- $_ = set $podScope "node" .Values.edgeNode -}} + {{- $_ = set $podScope "uploader" (get .Values.videoRecorder (.Values.videoRecorder.uploader | toString)) -}} + {{- $_ = set $podScope "podTemplate" (include "seleniumGrid.podTemplate" $podScope | fromYaml) }} + {{- include "seleniumGrid.autoscalingTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/edge-node-service.yaml b/charts/selenium-grid/templates/edge-node-service.yaml index ae59a4a63..d54dd70a3 100644 --- a/charts/selenium-grid/templates/edge-node-service.yaml +++ b/charts/selenium-grid/templates/edge-node-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.edgeNode.fullname" . }} namespace: {{ .Release.Namespace }} labels: - name: selenium-edge-node + name: {{ template "seleniumGrid.edgeNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.edgeNode.service.annotations }} annotations: @@ -14,11 +14,27 @@ metadata: spec: type: {{ .Values.edgeNode.service.type }} selector: - app: selenium-edge-node + app: {{ template "seleniumGrid.edgeNode.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} + {{- if and (eq .Values.edgeNode.service.type "LoadBalancer") (.Values.edgeNode.service.loadBalancerIP) }} + loadBalancerIP: {{ .Values.edgeNode.service.loadBalancerIP }} + {{- end }} ports: - name: tcp-edge protocol: TCP port: {{ .Values.edgeNode.seleniumServicePort }} targetPort: {{ .Values.edgeNode.seleniumPort }} + {{- with .Values.edgeNode.service.ports }} + {{- range . }} + - name: {{ .name }} + port: {{ .port }} + targetPort: {{ .targetPort }} + {{- if .protocol }} + protocol: {{ .protocol }} + {{- end }} + {{- if and (eq $.Values.edgeNode.service.type "NodePort") .nodePort }} + nodePort: {{ .nodePort }} + {{- end }} + {{- end }} + {{- end }} {{- end }} diff --git a/charts/selenium-grid/templates/event-bus-configmap.yaml b/charts/selenium-grid/templates/event-bus-configmap.yaml index efe33fa7d..05f279bba 100644 --- a/charts/selenium-grid/templates/event-bus-configmap.yaml +++ b/charts/selenium-grid/templates/event-bus-configmap.yaml @@ -15,6 +15,6 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} data: - SE_EVENT_BUS_HOST: {{ $eventBusHost }} + SE_EVENT_BUS_HOST: {{ $eventBusHost | quote }} SE_EVENT_BUS_PUBLISH_PORT: {{ $eventBusPublishPort | quote }} SE_EVENT_BUS_SUBSCRIBE_PORT: {{ $eventBusSubscribePort | quote }} diff --git a/charts/selenium-grid/templates/event-bus-deployment.yaml b/charts/selenium-grid/templates/event-bus-deployment.yaml index 498921e8c..8d894790b 100644 --- a/charts/selenium-grid/templates/event-bus-deployment.yaml +++ b/charts/selenium-grid/templates/event-bus-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.eventBus.fullname" . }} namespace: {{ .Release.Namespace }} labels: &event_bus_labels - app: selenium-event-bus - app.kubernetes.io/name: selenium-event-bus + app: {{ template "seleniumGrid.eventBus.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.eventBus.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ spec: replicas: 1 selector: matchLabels: - app: selenium-event-bus + app: {{ template "seleniumGrid.eventBus.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: @@ -27,9 +27,10 @@ spec: serviceAccountName: {{ template "seleniumGrid.serviceAccount.fullname" . }} serviceAccount: {{ template "seleniumGrid.serviceAccount.fullname" . }} containers: - - name: selenium-event-bus + - name: {{ template "seleniumGrid.eventBus.fullname" . }} {{- $imageTag := default .Values.global.seleniumGrid.imageTag .Values.components.eventBus.imageTag }} - image: {{ printf "%s:%s" .Values.components.eventBus.imageName $imageTag }} + {{- $imageRegistry := default .Values.global.seleniumGrid.imageRegistry .Values.components.eventBus.imageRegistry }} + image: {{ printf "%s/%s:%s" $imageRegistry .Values.components.eventBus.imageName $imageTag }} imagePullPolicy: {{ .Values.components.eventBus.imagePullPolicy }} ports: - containerPort: {{ .Values.components.eventBus.port }} diff --git a/charts/selenium-grid/templates/event-bus-service.yaml b/charts/selenium-grid/templates/event-bus-service.yaml index 38ff6cd1e..c3a30bcc0 100644 --- a/charts/selenium-grid/templates/event-bus-service.yaml +++ b/charts/selenium-grid/templates/event-bus-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.eventBus.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-event-bus + app: {{ template "seleniumGrid.eventBus.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ metadata: {{- end }} spec: selector: - app: selenium-event-bus + app: {{ template "seleniumGrid.eventBus.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} type: {{ .Values.components.eventBus.serviceType }} ports: diff --git a/charts/selenium-grid/templates/firefox-node-deployment.yaml b/charts/selenium-grid/templates/firefox-node-deployment.yaml index d0b250470..5a1e96b55 100644 --- a/charts/selenium-grid/templates/firefox-node-deployment.yaml +++ b/charts/selenium-grid/templates/firefox-node-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.firefoxNode.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-firefox-node - app.kubernetes.io/name: selenium-firefox-node + app: {{ template "seleniumGrid.firefoxNode.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.firefoxNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.firefoxNode.labels }} {{- toYaml . | nindent 4 }} @@ -20,10 +20,11 @@ spec: {{end}} selector: matchLabels: - app: selenium-firefox-node + app: {{ template "seleniumGrid.firefoxNode.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- $podScope := deepCopy . -}} -{{- $_ := set $podScope "name" "selenium-firefox-node" -}} -{{- $_ = set $podScope "node" .Values.firefoxNode -}} +{{- $_ := set $podScope "name" (include "seleniumGrid.firefoxNode.fullname" .) -}} +{{- $_ = set $podScope "node" .Values.firefoxNode -}} +{{- $_ = set $podScope "uploader" (get .Values.videoRecorder (.Values.videoRecorder.uploader | toString)) -}} {{- include "seleniumGrid.podTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/firefox-node-hpa.yaml b/charts/selenium-grid/templates/firefox-node-hpa.yaml index 70a463e79..ad18269a5 100644 --- a/charts/selenium-grid/templates/firefox-node-hpa.yaml +++ b/charts/selenium-grid/templates/firefox-node-hpa.yaml @@ -11,13 +11,8 @@ metadata: labels: deploymentName: {{ template "seleniumGrid.firefoxNode.fullname" . }} spec: - maxReplicaCount: {{ .Values.firefoxNode.maxReplicaCount }} - minReplicaCount: {{ .Values.firefoxNode.minReplicaCount }} - scaleTargetRef: - name: {{ template "seleniumGrid.firefoxNode.fullname" . }} - triggers: - - type: selenium-grid - {{- with .Values.firefoxNode.hpa }} - metadata: {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} + {{- $podScope := deepCopy . -}} + {{- $_ := set $podScope "name" (include "seleniumGrid.firefoxNode.fullname" .) -}} + {{- $_ = set $podScope "node" .Values.firefoxNode -}} + {{- include "seleniumGrid.autoscalingTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/firefox-node-scaledjob.yaml b/charts/selenium-grid/templates/firefox-node-scaledjob.yaml index de90024c7..e7992c1fc 100644 --- a/charts/selenium-grid/templates/firefox-node-scaledjob.yaml +++ b/charts/selenium-grid/templates/firefox-node-scaledjob.yaml @@ -9,8 +9,8 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} labels: - app: selenium-firefox-node - app.kubernetes.io/name: selenium-firefox-node + app: {{ template "seleniumGrid.firefoxNode.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.firefoxNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.firefoxNode.labels }} {{- toYaml . | nindent 4 }} @@ -19,21 +19,10 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - maxReplicaCount: {{ .Values.firefoxNode.maxReplicaCount }} - {{- with .Values.autoscaling.scaledJobOptions -}} - {{ toYaml . | nindent 2 }} - {{- end }} - triggers: - - type: selenium-grid - {{- with .Values.firefoxNode.hpa }} - metadata: {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} - jobTargetRef: - parallelism: 1 - completions: 1 - backoffLimit: 0 -{{- $podScope := deepCopy . -}} -{{- $_ := set $podScope "name" "selenium-firefox-node" -}} -{{- $_ = set $podScope "node" .Values.firefoxNode -}} -{{- include "seleniumGrid.podTemplate" $podScope | nindent 4 }} + {{- $podScope := deepCopy . -}} + {{- $_ := set $podScope "name" (include "seleniumGrid.firefoxNode.fullname" .) -}} + {{- $_ = set $podScope "node" .Values.firefoxNode -}} + {{- $_ = set $podScope "uploader" (get .Values.videoRecorder (.Values.videoRecorder.uploader | toString)) -}} + {{- $_ = set $podScope "podTemplate" (include "seleniumGrid.podTemplate" $podScope | fromYaml) }} + {{- include "seleniumGrid.autoscalingTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/firefox-node-service.yaml b/charts/selenium-grid/templates/firefox-node-service.yaml index 473225f93..43ba40380 100644 --- a/charts/selenium-grid/templates/firefox-node-service.yaml +++ b/charts/selenium-grid/templates/firefox-node-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.firefoxNode.fullname" . }} namespace: {{ .Release.Namespace }} labels: - name: selenium-firefox-node + name: {{ template "seleniumGrid.firefoxNode.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.firefoxNode.service.annotations }} annotations: @@ -14,11 +14,27 @@ metadata: spec: type: {{ .Values.firefoxNode.service.type }} selector: - app: selenium-firefox-node + app: {{ template "seleniumGrid.firefoxNode.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} + {{- if and (eq .Values.firefoxNode.service.type "LoadBalancer") (.Values.firefoxNode.service.loadBalancerIP) }} + loadBalancerIP: {{ .Values.firefoxNode.service.loadBalancerIP }} + {{- end }} ports: - name: tcp-firefox protocol: TCP port: {{ .Values.firefoxNode.seleniumServicePort }} targetPort: {{ .Values.firefoxNode.seleniumPort }} + {{- with .Values.firefoxNode.service.ports }} + {{- range . }} + - name: {{ .name }} + port: {{ .port }} + targetPort: {{ .targetPort }} + {{- if .protocol }} + protocol: {{ .protocol }} + {{- end }} + {{- if and (eq $.Values.firefoxNode.service.type "NodePort") .nodePort }} + nodePort: {{ .nodePort }} + {{- end }} + {{- end }} + {{- end }} {{- end }} diff --git a/charts/selenium-grid/templates/hub-deployment.yaml b/charts/selenium-grid/templates/hub-deployment.yaml index 6f9488d12..8b322ecd5 100644 --- a/charts/selenium-grid/templates/hub-deployment.yaml +++ b/charts/selenium-grid/templates/hub-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.hub.fullname" . }} namespace: {{ .Release.Namespace }} labels: &hub_labels - app: selenium-hub - app.kubernetes.io/name: selenium-hub + app: {{ template "seleniumGrid.hub.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.hub.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.hub.labels }} {{- toYaml . | nindent 4 }} @@ -18,7 +18,7 @@ spec: replicas: 1 selector: matchLabels: - app: selenium-hub + app: {{ template "seleniumGrid.hub.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: @@ -30,9 +30,10 @@ spec: serviceAccountName: {{ template "seleniumGrid.serviceAccount.fullname" . }} serviceAccount: {{ template "seleniumGrid.serviceAccount.fullname" . }} containers: - - name: selenium-hub + - name: {{ template "seleniumGrid.hub.fullname" . }} {{- $imageTag := default .Values.global.seleniumGrid.imageTag .Values.hub.imageTag }} - image: {{ printf "%s:%s" .Values.hub.imageName $imageTag }} + {{- $imageRegistry := default .Values.global.seleniumGrid.imageRegistry .Values.hub.imageRegistry }} + image: {{ printf "%s/%s:%s" $imageRegistry .Values.hub.imageName $imageTag }} imagePullPolicy: {{ .Values.hub.imagePullPolicy }} ports: - containerPort: {{ .Values.hub.port }} diff --git a/charts/selenium-grid/templates/hub-service.yaml b/charts/selenium-grid/templates/hub-service.yaml index 417e9849f..aa4daa34c 100644 --- a/charts/selenium-grid/templates/hub-service.yaml +++ b/charts/selenium-grid/templates/hub-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.hub.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-hub + app: {{ template "seleniumGrid.hub.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ metadata: {{- end }} spec: selector: - app: selenium-hub + app: {{ template "seleniumGrid.hub.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} type: {{ .Values.hub.serviceType }} {{- if and (eq .Values.hub.serviceType "LoadBalancer") ( .Values.hub.loadBalancerIP ) }} diff --git a/charts/selenium-grid/templates/ingress.yaml b/charts/selenium-grid/templates/ingress.yaml index 6df2c9553..215cfbace 100644 --- a/charts/selenium-grid/templates/ingress.yaml +++ b/charts/selenium-grid/templates/ingress.yaml @@ -14,14 +14,19 @@ apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ template "seleniumGrid.ingress.fullname" . }} + namespace: {{ .Release.Namespace }} labels: {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} {{- end }} - {{- with .Values.ingress.annotations }} + {{- $ingressAnnotations := (include "seleniumGrid.ingress.nginx.annotations.default" . | toString | fromYaml ) }} + {{- with .Values.ingress.annotations -}} + {{- $ingressAnnotations = mergeOverwrite $ingressAnnotations . }} + {{- end }} + {{- if not (empty $ingressAnnotations) }} annotations: - {{- toYaml . | nindent 4 }} + {{- $ingressAnnotations | toYaml | nindent 4 }} {{- end }} spec: {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} @@ -46,8 +51,9 @@ spec: {{- end }} {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.Version }} paths: + {{- if empty (.Values.ingress.paths) }} - path: {{ $.Values.ingress.path | default "/" }} - pathType: Prefix + pathType: {{ $.Values.ingress.pathType | default "Prefix" }} backend: service: {{- if $.Values.isolateComponents }} @@ -59,6 +65,9 @@ spec: port: number: {{ $.Values.hub.port }} {{- end }} + {{- else }} + {{- tpl (toYaml .Values.ingress.paths | nindent 10) . }} + {{- end }} {{- else }} paths: - path: / diff --git a/charts/selenium-grid/templates/node-configmap.yaml b/charts/selenium-grid/templates/node-configmap.yaml index 08d6d4981..52e447d63 100644 --- a/charts/selenium-grid/templates/node-configmap.yaml +++ b/charts/selenium-grid/templates/node-configmap.yaml @@ -12,5 +12,5 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} data: - DRAIN_AFTER_SESSION_COUNT: '{{- and (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "job") | ternary "1" "0" -}}' - SE_NODE_GRID_URL: '{{ include "seleniumGrid.url" .}}' + SE_DRAIN_AFTER_SESSION_COUNT: '{{- and (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "job") | ternary "1" "0" -}}' + SE_NODE_GRID_URL: '{{ include "seleniumGrid.url" .}}{{ include "seleniumGrid.url.subPath" .}}' diff --git a/charts/selenium-grid/templates/router-deployment.yaml b/charts/selenium-grid/templates/router-deployment.yaml index 037bcbf39..33d98c65a 100644 --- a/charts/selenium-grid/templates/router-deployment.yaml +++ b/charts/selenium-grid/templates/router-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.router.fullname" . }} namespace: {{ .Release.Namespace }} labels: &router_labels - app: selenium-router - app.kubernetes.io/name: selenium-router + app: {{ template "seleniumGrid.router.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.router.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ spec: replicas: 1 selector: matchLabels: - app: selenium-router + app: {{ template "seleniumGrid.router.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: @@ -27,9 +27,10 @@ spec: serviceAccountName: {{ template "seleniumGrid.serviceAccount.fullname" . }} serviceAccount: {{ template "seleniumGrid.serviceAccount.fullname" . }} containers: - - name: selenium-router + - name: {{ template "seleniumGrid.router.fullname" . }} {{- $imageTag := default .Values.global.seleniumGrid.imageTag .Values.components.router.imageTag }} - image: {{ printf "%s:%s" .Values.components.router.imageName $imageTag }} + {{- $imageRegistry := default .Values.global.seleniumGrid.imageRegistry .Values.components.router.imageRegistry }} + image: {{ printf "%s/%s:%s" $imageRegistry .Values.components.router.imageName $imageTag }} imagePullPolicy: {{ .Values.components.router.imagePullPolicy }} env: - name: SE_DISTRIBUTOR_HOST diff --git a/charts/selenium-grid/templates/router-service.yaml b/charts/selenium-grid/templates/router-service.yaml index d586c31fe..9eaff7955 100644 --- a/charts/selenium-grid/templates/router-service.yaml +++ b/charts/selenium-grid/templates/router-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.router.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-router + app: {{ template "seleniumGrid.router.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ metadata: {{- end }} spec: selector: - app: selenium-router + app: {{ template "seleniumGrid.router.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} type: {{ .Values.components.router.serviceType }} {{- if and (eq .Values.components.router.serviceType "LoadBalancer") (.Values.components.router.loadBalancerIP) }} diff --git a/charts/selenium-grid/templates/session-map-deployment.yaml b/charts/selenium-grid/templates/session-map-deployment.yaml index 437c664e2..8aebdefb2 100644 --- a/charts/selenium-grid/templates/session-map-deployment.yaml +++ b/charts/selenium-grid/templates/session-map-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.sessionMap.fullname" . }} namespace: {{ .Release.Namespace }} labels: &session_map_labels - app: selenium-session-map - app.kubernetes.io/name: selenium-session-map + app: {{ template "seleniumGrid.sessionMap.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.sessionMap.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ spec: replicas: 1 selector: matchLabels: - app: selenium-session-map + app: {{ template "seleniumGrid.sessionMap.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: @@ -27,9 +27,10 @@ spec: serviceAccountName: {{ template "seleniumGrid.serviceAccount.fullname" . }} serviceAccount: {{ template "seleniumGrid.serviceAccount.fullname" . }} containers: - - name: selenium-session-map + - name: {{ template "seleniumGrid.sessionMap.fullname" . }} {{- $imageTag := default .Values.global.seleniumGrid.imageTag .Values.components.sessionMap.imageTag }} - image: {{ printf "%s:%s" .Values.components.sessionMap.imageName $imageTag }} + {{- $imageRegistry := default .Values.global.seleniumGrid.imageRegistry .Values.components.sessionMap.imageRegistry }} + image: {{ printf "%s/%s:%s" $imageRegistry .Values.components.sessionMap.imageName $imageTag }} imagePullPolicy: {{ .Values.components.sessionMap.imagePullPolicy }} {{- with .Values.components.extraEnvironmentVariables }} env: {{- tpl (toYaml .) $ | nindent 12 }} diff --git a/charts/selenium-grid/templates/session-map-service.yaml b/charts/selenium-grid/templates/session-map-service.yaml index 6df2d9bbf..13dd963b6 100644 --- a/charts/selenium-grid/templates/session-map-service.yaml +++ b/charts/selenium-grid/templates/session-map-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.sessionMap.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-session-map + app: {{ template "seleniumGrid.sessionMap.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ metadata: {{- end }} spec: selector: - app: selenium-session-map + app: {{ template "seleniumGrid.sessionMap.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} type: {{ .Values.components.sessionMap.serviceType }} ports: diff --git a/charts/selenium-grid/templates/session-queuer-deployment.yaml b/charts/selenium-grid/templates/session-queuer-deployment.yaml index 0f26f06e5..557d6774e 100644 --- a/charts/selenium-grid/templates/session-queuer-deployment.yaml +++ b/charts/selenium-grid/templates/session-queuer-deployment.yaml @@ -5,8 +5,8 @@ metadata: name: {{ template "seleniumGrid.sessionQueue.fullname" . }} namespace: {{ .Release.Namespace }} labels: &session_queue_labels - app: selenium-session-queue - app.kubernetes.io/name: selenium-session-queue + app: {{ template "seleniumGrid.sessionQueue.fullname" . }} + app.kubernetes.io/name: {{ template "seleniumGrid.sessionQueue.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ spec: replicas: 1 selector: matchLabels: - app: selenium-session-queue + app: {{ template "seleniumGrid.sessionQueue.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: @@ -27,9 +27,10 @@ spec: serviceAccountName: {{ template "seleniumGrid.serviceAccount.fullname" . }} serviceAccount: {{ template "seleniumGrid.serviceAccount.fullname" . }} containers: - - name: selenium-session-queue + - name: {{ template "seleniumGrid.sessionQueue.fullname" . }} {{- $imageTag := default .Values.global.seleniumGrid.imageTag .Values.components.sessionQueue.imageTag }} - image: {{ printf "%s:%s" .Values.components.sessionQueue.imageName $imageTag }} + {{- $imageRegistry := default .Values.global.seleniumGrid.imageRegistry .Values.components.sessionQueue.imageRegistry }} + image: {{ printf "%s/%s:%s" $imageRegistry .Values.components.sessionQueue.imageName $imageTag }} imagePullPolicy: {{ .Values.components.sessionQueue.imagePullPolicy }} {{- with .Values.components.extraEnvironmentVariables }} env: {{- tpl (toYaml .) $ | nindent 12 }} diff --git a/charts/selenium-grid/templates/session-queuer-service.yaml b/charts/selenium-grid/templates/session-queuer-service.yaml index be3739ad0..54e535f7b 100644 --- a/charts/selenium-grid/templates/session-queuer-service.yaml +++ b/charts/selenium-grid/templates/session-queuer-service.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "seleniumGrid.sessionQueue.fullname" . }} namespace: {{ .Release.Namespace }} labels: - app: selenium-session-queue + app: {{ template "seleniumGrid.sessionQueue.fullname" . }} {{- include "seleniumGrid.commonLabels" . | nindent 4 }} {{- with .Values.customLabels }} {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ metadata: {{- end }} spec: selector: - app: selenium-session-queue + app: {{ template "seleniumGrid.sessionQueue.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} type: {{ .Values.components.sessionQueue.serviceType }} ports: diff --git a/charts/selenium-grid/templates/video-cm.yaml b/charts/selenium-grid/templates/video-cm.yaml new file mode 100644 index 000000000..1b49e026e --- /dev/null +++ b/charts/selenium-grid/templates/video-cm.yaml @@ -0,0 +1,98 @@ +{{- if .Values.videoRecorder.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "seleniumGrid.video.fullname" . }} +data: + video.sh: | + #!/usr/bin/env bash + set -em + function finish { + echo exit > /videos/uploadpipe + kill -s SIGINT `cat /var/run/supervisor/supervisord.pid` + } + trap finish EXIT + FRAME_RATE=${FRAME_RATE:-$SE_FRAME_RATE} + CODEC=${CODEC:-$SE_CODEC} + PRESET=${PRESET:-$SE_PRESET} + export DISPLAY=localhost:${DISPLAY_NUM}.0 + + return_code=1 + max_attempts=600 + attempts=0 + if [ ! -p /videos/uploadpipe ]; + then + mkfifo /videos/uploadpipe + fi + if [[ "$UPLOAD_DESTINATION_PREFIX" = "" ]] + then + echo Upload destination not known since UPLOAD_DESTINATION_PREFIX is not set. Exiting video recorder. + exit + fi + echo Checking if the display is open + until xset b off || [[ $attempts = $max_attempts ]] + do + echo Waiting before next display check + sleep 0.5 + attempts=$((attempts+1)) + done + if [[ $attempts = $max_attempts ]] + then + echo Can not open display, exiting. + exit + fi + VIDEO_SIZE=$(xdpyinfo | grep 'dimensions:' | awk '{print $2}') + + recording_started="false" + video_file_name="" + video_file="" + prev_session_id="" + attempts=0 + echo Checking if node API responds + until curl -s --request GET http://localhost:5555/status || [[ $attempts = $max_attempts ]] + do + echo Waiting before next API check + sleep 0.5 + attempts=$((attempts+1)) + done + if [[ $attempts = $max_attempts ]] + then + echo Can not reach node API, exiting. + exit + fi + while curl -s --request GET http://localhost:5555/status > /tmp/status.json + do + session_id=$(jq -r '.[]?.node?.slots | .[0]?.session?.sessionId' /tmp/status.json) + echo $session_id + if [[ "$session_id" != "null" && "$session_id" != "" && "$recording_started" = "false" ]] + then + video_file_name="$session_id.mp4" + video_file="${SE_VIDEO_FOLDER:-/videos}/$video_file_name" + echo "Starting to record video" + ffmpeg -nostdin -y -f x11grab -video_size ${VIDEO_SIZE} -r ${FRAME_RATE} -i ${DISPLAY} -codec:v ${CODEC} ${PRESET} -pix_fmt yuv420p $video_file & + recording_started="true" + echo "Video recording started" + elif [[ "$session_id" != "$prev_session_id" && "$recording_started" = "true" ]] + then + echo "Stopping to record video" + kill -INT %1 + fg || echo ffmpeg exited with code $? + if [[ "$UPLOAD_DESTINATION_PREFIX" != "false" ]] + then + upload_destination=${UPLOAD_DESTINATION_PREFIX}${video_file_name} + echo "Uploading video to $upload_destination" + echo $video_file $upload_destination > /videos/uploadpipe + fi + recording_started="false" + elif [[ $recording_started = "true" ]] + then + echo "Video recording in progress" + sleep 1 + else + echo "No session in progress" + sleep 1 + fi + prev_session_id=$session_id + done + echo +{{- end }} diff --git a/charts/selenium-grid/values.yaml b/charts/selenium-grid/values.yaml index a29c27865..3489fb588 100644 --- a/charts/selenium-grid/values.yaml +++ b/charts/selenium-grid/values.yaml @@ -1,20 +1,24 @@ global: seleniumGrid: + # Image registry for all selenium components + imageRegistry: selenium # Image tag for all selenium components - imageTag: 4.12.1-20230920 + imageTag: 4.16.1-20231212 # Image tag for browser's nodes - nodesImageTag: 4.12.1-20230920 + nodesImageTag: 4.16.1-20231212 + # Image tag for browser's video recorder + videoImageTag: ffmpeg-6.1-20231212 # Pull secret for all components, can be overridden individually - imagePullSecret: "" + imagePullSecret: "" # Basic auth settings for Selenium Grid basicAuth: - # Enable or disable basic auth - enabled: true - # Username for basic auth - username: admin - # Password for basic auth - password: admin + # Enable or disable basic auth + enabled: true + # Username for basic auth + username: admin + # Password for basic auth + password: admin # Deploy Router, Distributor, EventBus, SessionMap and Nodes separately isolateComponents: false @@ -24,6 +28,7 @@ serviceAccount: create: true name: "" annotations: {} + # eks.amazonaws.com/role-arn: "arn:aws:iam::12345678:role/video-bucket-permissions" # Configure the ingress resource to access the Grid installation. ingress: @@ -31,12 +36,22 @@ ingress: enabled: true # Name of ingress class to select which controller will implement ingress resource className: "" + # Refer to list nginx annotations: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#annotations + nginx: + proxyTimeout: 3600 + proxyBuffer: + size: 512M + number: 4 # Custom annotations for ingress resource annotations: {} # Default host for the ingress resource - hostname: selenium-grid.local + hostname: "" # Default host path for the ingress resource path: / + # Default path type for the ingress resource + pathType: Prefix + # List of paths for the ingress resource. This will override the default path + paths: [] # TLS backend configuration for ingress resource tls: [] @@ -58,10 +73,11 @@ components: # Configuration for router component router: + # imageRegistry: selenium # Router image name - imageName: selenium/router + imageName: router # Router image tag (this overwrites global.seleniumGrid.imageTag parameter) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent @@ -109,10 +125,11 @@ components: # Configuration for distributor component distributor: + # imageRegistry: selenium # Distributor image name - imageName: selenium/distributor + imageName: distributor # Distributor image tag (this overwrites global.seleniumGrid.imageTag parameter) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent @@ -140,10 +157,11 @@ components: # Configuration for Event Bus component eventBus: + # imageRegistry: selenium # Event Bus image name - imageName: selenium/event-bus + imageName: event-bus # Event Bus image tag (this overwrites global.seleniumGrid.imageTag parameter) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent @@ -175,10 +193,11 @@ components: # Configuration for Session Map component sessionMap: + # imageRegistry: selenium # Session Map image name - imageName: selenium/sessions + imageName: sessions # Session Map image tag (this overwrites global.seleniumGrid.imageTag parameter) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent @@ -205,10 +224,11 @@ components: # Configuration for Session Queue component sessionQueue: + # imageRegistry: selenium # Session Queue image name - imageName: selenium/session-queue + imageName: session-queue # Session Queue image tag (this overwrites global.seleniumGrid.imageTag parameter) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent @@ -255,10 +275,11 @@ components: # Configuration for selenium hub deployment (applied only if `isolateComponents: false`) hub: + # imageRegistry: selenium # Selenium Hub image name - imageName: selenium/hub + imageName: hub # Selenium Hub image tag (this overwrites global.seleniumGrid.imageTag parameter) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent # Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/) @@ -342,16 +363,36 @@ autoscaling: enabled: false # Enable autoscaling without automatically installing KEDA enableWithExistingKEDA: false - # Which typ of KEDA scaling to use: job or deployment + # Which type of KEDA scaling to use: job or deployment scalingType: job # Annotations for KEDA resources: ScaledObject and ScaledJob annotations: helm.sh/hook: post-install,post-upgrade - # Options for KEDA ScaledJobs - scaledJobOptions: + # Options for KEDA scaled resources (keep only common options used for both ScaledJob and ScaledObject) + scaledOptions: + minReplicaCount: 0 + maxReplicaCount: 8 pollingInterval: 10 + # List of triggers. Be careful, the default trigger of selenium-grid will be overwritten if you specify this + # triggers: + # Options for KEDA ScaledJobs (only used when scalingType is set to "job") + # see https://keda.sh/docs/latest/concepts/scaling-jobs/#scaledjob-spec + scaledJobOptions: scalingStrategy: strategy: accurate + # Number of Completed jobs should be kept + successfulJobsHistoryLimit: 0 + # Number of Failed jobs should be kept (for troubleshooting purposes) + failedJobsHistoryLimit: 100 + jobTargetRef: + parallelism: 1 + completions: 1 + backoffLimit: 0 + # Options for KEDA ScaledObjects (only used when scalingType is set to "deployment") + # see https://keda.sh/docs/latest/concepts/scaling-deployments/#scaledobject-spec + scaledObjectOptions: + scaleTargetRef: + kind: Deployment deregisterLifecycle: preStop: exec: @@ -375,10 +416,11 @@ chromeNode: # Number of chrome nodes replicas: 1 + # imageRegistry: selenium # Image of chrome nodes - imageName: selenium/node-chrome + imageName: node-chrome # Image of chrome nodes (this overwrites global.seleniumGrid.nodesImageTag) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent # Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/) @@ -439,6 +481,13 @@ chromeNode: enabled: true # Service type type: ClusterIP + # Set specific loadBalancerIP when serviceType is LoadBalancer (see https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) + loadBalancerIP: "" + # Extra ports exposed in node service + ports: + # - name: node-port + # port: 5555 + # targetPort: 5555 # Custom annotations for service annotations: {} # Size limit for DSH volume mounted in container (if not set, default is "1Gi") @@ -471,13 +520,15 @@ chromeNode: # persistentVolumeClaim: # claimName: my-pv-claim - maxReplicaCount: 8 - minReplicaCount: 1 + # Override the scaled options for chrome nodes + # scaledOptions: + # scaledJobOptions: + # scaledObjectOptions: hpa: url: '{{ include "seleniumGrid.graphqlURL" . }}' browserName: chrome # browserVersion: '91.0' # Optional. Only required when supporting multiple versions of browser in your Selenium Grid. - unsafeSsl : 'true' # Optional + unsafeSsl: 'true' # Optional # It is used to add a sidecars proxy in the same pod of the browser node. # It means it will add a new container to the deployment itself. @@ -498,10 +549,11 @@ firefoxNode: # Number of firefox nodes replicas: 1 + # imageRegistry: selenium # Image of firefox nodes - imageName: selenium/node-firefox + imageName: node-firefox # Image of firefox nodes (this overwrites global.seleniumGrid.nodesImageTag) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent # Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/) @@ -562,6 +614,13 @@ firefoxNode: enabled: true # Service type type: ClusterIP + # Set specific loadBalancerIP when serviceType is LoadBalancer (see https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) + loadBalancerIP: "" + # Extra ports exposed in node service + ports: + # - name: node-port + # port: 5555 + # targetPort: 5555 # Custom annotations for service annotations: {} # Size limit for DSH volume mounted in container (if not set, default is "1Gi") @@ -593,8 +652,11 @@ firefoxNode: # - name: my-extra-volume-from-pvc # persistentVolumeClaim: # claimName: my-pv-claim - maxReplicaCount: 8 - minReplicaCount: 1 + + # Override the scaled options for firefox nodes + # scaledOptions: + # scaledJobOptions: + # scaledObjectOptions: hpa: url: '{{ include "seleniumGrid.graphqlURL" . }}' browserName: firefox @@ -617,10 +679,11 @@ edgeNode: # Number of edge nodes replicas: 1 + # imageRegistry: selenium # Image of edge nodes - imageName: selenium/node-edge + imageName: node-edge # Image of edge nodes (this overwrites global.seleniumGrid.nodesImageTag) - # imageTag: 4.12.1-20230920 + # imageTag: 4.16.1-20231212 # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) imagePullPolicy: IfNotPresent # Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/) @@ -680,9 +743,15 @@ edgeNode: enabled: true # Service type type: ClusterIP + # Set specific loadBalancerIP when serviceType is LoadBalancer (see https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) + loadBalancerIP: "" + # Extra ports exposed in node service + ports: + # - name: node-port + # port: 5555 + # targetPort: 5555 # Custom annotations for service - annotations: - hello: world + annotations: {} # Size limit for DSH volume mounted in container (if not set, default is "1Gi") dshmVolumeSizeLimit: 1Gi # Priority class name for edge-node pods @@ -712,8 +781,11 @@ edgeNode: # - name: my-extra-volume-from-pvc # persistentVolumeClaim: # claimName: my-pv-claim - maxReplicaCount: 8 - minReplicaCount: 1 + + # Override the scaled options for edge nodes + # scaledOptions: + # scaledJobOptions: + # scaledObjectOptions: hpa: url: '{{ include "seleniumGrid.graphqlURL" . }}' browserName: MicrosoftEdge @@ -724,5 +796,249 @@ edgeNode: # It should be set using the --set-json option sidecars: [] +# Configuration for chromium nodes +chromiumNode: + # Enable chromium nodes + enabled: true + + # NOTE: Only used when autoscaling.enabled is false + # Enable creation of Deployment + # true (default) - if you want long living pods + # false - for provisioning your own custom type such as Jobs + deploymentEnabled: true + + # Number of chromium nodes + replicas: 1 + # imageRegistry: selenium + # Image of chromium nodes + imageName: node-chromium + # Image of chromium nodes (this overwrites global.seleniumGrid.nodesImageTag) + # imageTag: 4.16.1-20231212 + # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) + imagePullPolicy: IfNotPresent + # Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/) + imagePullSecret: "" + + # Port list to enable on container + ports: + - 5555 + # Selenium port (spec.ports[0].targetPort in kubernetes service) + seleniumPort: 5900 + # Selenium port exposed in service (spec.ports[0].port in kubernetes service) + seleniumServicePort: 6900 + # Annotations for chromium-node pods + annotations: {} + # Labels for chromium-node pods + labels: {} + # Resources for chromium-node container + resources: + requests: + memory: "1Gi" + cpu: "1" + limits: + memory: "1Gi" + cpu: "1" + # SecurityContext for chromium-node container + securityContext: {} + # Tolerations for chromium-node pods + tolerations: [] + # Node selector for chromium-node pods + nodeSelector: {} + # Custom host aliases for chromium nodes + hostAliases: + # - ip: "198.51.100.0" + # hostnames: + # - "example.com" + # - "example.net" + # - ip: "203.0.113.0" + # hostnames: + # - "example.org" + # Custom environment variables for chromium nodes + extraEnvironmentVariables: + # - name: SE_JAVA_OPTS + # value: "-Xmx512m" + # - name: + # valueFrom: + # secretKeyRef: + # name: secret-name + # key: secret-key + # Custom environment variables by sourcing entire configMap, Secret, etc. for chromium nodes + extraEnvFrom: + # - configMapRef: + # name: proxy-settings + # - secretRef: + # name: mysecret + # Service configuration + service: + # Create a service for node + enabled: true + # Service type + type: ClusterIP + # Set specific loadBalancerIP when serviceType is LoadBalancer (see https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) + loadBalancerIP: "" + # Extra ports exposed in node service + ports: + # - name: node-port + # port: 5555 + # targetPort: 5555 + # Custom annotations for service + annotations: {} + # Size limit for DSH volume mounted in container (if not set, default is "1Gi") + dshmVolumeSizeLimit: 1Gi + # Priority class name for chromium-node pods + priorityClassName: "" + + # Wait for pod startup + startupProbe: {} + # httpGet: + # path: /status + # port: 5555 + # failureThreshold: 120 + # periodSeconds: 5 + + # Liveness probe settings + livenessProbe: {} + + # Time to wait for pod termination + terminationGracePeriodSeconds: 30 + lifecycle: {} + extraVolumeMounts: [] + # - name: my-extra-volume + # mountPath: /home/seluser/Downloads + + extraVolumes: [] + # - name: my-extra-volume + # emptyDir: {} + # - name: my-extra-volume-from-pvc + # persistentVolumeClaim: + # claimName: my-pv-claim + + # Override the scaled options for chromium nodes + # scaledOptions: + # scaledJobOptions: + # scaledObjectOptions: + hpa: + url: '{{ include "seleniumGrid.graphqlURL" . }}' + browserName: chrome + # browserVersion: '91.0' # Optional. Only required when supporting multiple versions of browser in your Selenium Grid. + unsafeSsl: 'true' # Optional + + # It is used to add a sidecars proxy in the same pod of the browser node. + # It means it will add a new container to the deployment itself. + # It should be set using the --set-json option + sidecars: [] + +videoRecorder: + enabled: false + # imageRegistry: selenium + # Image of video recorder + imageName: video + # Image of video recorder + # imageTag: ffmpeg-6.1-20231212 + # Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) + imagePullPolicy: IfNotPresent + # Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/) + + # What uploader to use. See .videRecorder.s3 for how to create a new one. + # uploader: s3 + uploader: false + # Where to upload the video file. Should be set to something like 's3://myvideobucket/' + uploadDestinationPrefix: false + + ports: + - 9000 + resources: + requests: + memory: "1Gi" + cpu: "1" + limits: + memory: "1Gi" + cpu: "1" + extraEnvironmentVariables: + # - name: SE_VIDEO_FOLDER + # value: /videos + # Custom environment variables by sourcing entire configMap, Secret, etc. for video recorder. + extraEnvFrom: + # - configMapRef: + # name: proxy-settings + # - secretRef: + # name: mysecret + # Wait for pod startup + terminationGracePeriodSeconds: 30 + + # Wait for pod startup + startupProbe: {} + # httpGet: + # path: / + # port: 9000 + # failureThreshold: 120 + # periodSeconds: 5 + + # Liveness probe settings + livenessProbe: {} + + volume: + # name: + # folder: video + # scripts: video-scripts + # Custom video recorder back-end scripts (video.sh, video_ready.py, etc.) further by ConfigMap. + # NOTE: For the mount point with the name "video", or "video-scripts", it will override the default. For other names, it will be appended. + extraVolumeMounts: [] + # - name: video-scripts + # mountPath: /opt/bin/video.sh + # subPath: custom_video.sh + # - name: video-scripts + # mountPath: /opt/bin/video_ready.py + # subPath: video_ready.py + + extraVolumes: [] + # - name: video-scripts + # configMap: + # name: my-video-scripts-cm + # defaultMode: 0500 + # - name: video + # persistentVolumeClaim: + # claimName: video-pv-claim + + # Container spec for the uploader if above it is defined as "uploader: s3" + s3: + imageName: public.ecr.aws/bitnami/aws-cli + imageTag: "2" + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 0 + command: + - /bin/sh + args: + - -c + - | + while ! [ -p /videos/uploadpipe ] + do + echo Waiting for /videos/uploadpipe to be created + sleep 1 + done + echo Waiting for files to upload + while read FILE DESTINATION < /videos/uploadpipe + do + if [ "$FILE" = "exit" ] + then + echo "$FILE" + break + else + echo "Uploading $FILE to $DESTINATION" + aws s3 cp --no-progress $FILE $DESTINATION + fi + done + extraEnvironmentVariables: + # - name: AWS_ACCESS_KEY_ID + # value: aws_access_key_id + # - name: AWS_SECRET_ACCESS_KEY + # value: aws_secret_access_key + # - name: + # valueFrom: + # secretKeyRef: + # name: secret-name + # key: secret-key + # Custom labels for k8s resources customLabels: {} diff --git a/docker-compose-v2-tracing.yml b/docker-compose-v2-tracing.yml index 9e64e2204..888e06a86 100644 --- a/docker-compose-v2-tracing.yml +++ b/docker-compose-v2-tracing.yml @@ -9,7 +9,7 @@ services: - "16686:16686" - "14250:14250" chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -23,7 +23,7 @@ services: - "6900:5900" edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -37,7 +37,7 @@ services: - "6901:5900" firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -50,8 +50,23 @@ services: ports: - "6902:5900" + + chromium: + image: selenium/node-chromium:4.16.1-20231212 + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + - SE_ENABLE_TRACING=true + - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-chromium + ports: + - "6903:5900" + selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 ports: - "4442:4442" - "4443:4443" diff --git a/docker-compose-v2.yml b/docker-compose-v2.yml index ef92434e4..b405cccc9 100644 --- a/docker-compose-v2.yml +++ b/docker-compose-v2.yml @@ -4,7 +4,7 @@ version: '2' services: chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -16,7 +16,7 @@ services: - "6900:5900" edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -28,7 +28,7 @@ services: - "6901:5900" firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -39,8 +39,20 @@ services: ports: - "6902:5900" + chromium: + image: selenium/node-chromium:4.16.1-20231212 + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + ports: + - "6903:5900" + selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 ports: - "4442:4442" - "4443:4443" diff --git a/docker-compose-v3-basicauth.yml b/docker-compose-v3-basicauth.yml index 64937694c..9edadac76 100644 --- a/docker-compose-v3-basicauth.yml +++ b/docker-compose-v3-basicauth.yml @@ -4,7 +4,7 @@ version: "3" services: chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -14,7 +14,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -24,7 +24,17 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + + chromium: + image: selenium/node-chromium:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -34,7 +44,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 container_name: selenium-hub ports: - "4442:4442" diff --git a/docker-compose-v3-dev.yml b/docker-compose-v3-dev.yml index 6cbae1478..3503977c8 100644 --- a/docker-compose-v3-dev.yml +++ b/docker-compose-v3-dev.yml @@ -4,7 +4,7 @@ version: "3" services: chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar @@ -16,7 +16,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar @@ -28,7 +28,19 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 + shm_size: 2gb + volumes: + - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + + chromium: + image: selenium/node-chromium:4.16.1-20231212 shm_size: 2gb volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar @@ -40,7 +52,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 container_name: selenium-hub volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar diff --git a/docker-compose-v3-dynamic-grid.yml b/docker-compose-v3-dynamic-grid.yml index a091c6a5d..ae3d587dc 100644 --- a/docker-compose-v3-dynamic-grid.yml +++ b/docker-compose-v3-dynamic-grid.yml @@ -4,7 +4,7 @@ version: "3" services: node-docker: - image: selenium/node-docker:4.12.1-20230920 + image: selenium/node-docker:4.16.1-20231212 volumes: - ./assets:/opt/selenium/assets - ./NodeDocker/config.toml:/opt/bin/config.toml @@ -17,7 +17,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 container_name: selenium-hub ports: - "4442:4442" diff --git a/docker-compose-v3-full-grid-dev.yml b/docker-compose-v3-full-grid-dev.yml index d4345a711..1022a6a92 100644 --- a/docker-compose-v3-full-grid-dev.yml +++ b/docker-compose-v3-full-grid-dev.yml @@ -4,7 +4,7 @@ version: "3" services: selenium-event-bus: - image: selenium/event-bus:4.12.1-20230920 + image: selenium/event-bus:4.16.1-20231212 volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar container_name: selenium-event-bus @@ -14,7 +14,7 @@ services: - "5557:5557" selenium-sessions: - image: selenium/sessions:4.12.1-20230920 + image: selenium/sessions:4.16.1-20231212 volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar container_name: selenium-sessions @@ -28,7 +28,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 selenium-session-queue: - image: selenium/session-queue:4.12.1-20230920 + image: selenium/session-queue:4.16.1-20231212 volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar container_name: selenium-session-queue @@ -36,7 +36,7 @@ services: - "5559:5559" selenium-distributor: - image: selenium/distributor:4.12.1-20230920 + image: selenium/distributor:4.16.1-20231212 volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar container_name: selenium-distributor @@ -56,7 +56,7 @@ services: - SE_SESSION_QUEUE_PORT=5559 selenium-router: - image: selenium/router:4.12.1-20230920 + image: selenium/router:4.16.1-20231212 volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar container_name: selenium-router @@ -75,7 +75,7 @@ services: - SE_SESSION_QUEUE_PORT=5559 chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar @@ -87,7 +87,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar @@ -99,7 +99,19 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 + shm_size: 2gb + volumes: + - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar + depends_on: + - selenium-event-bus + environment: + - SE_EVENT_BUS_HOST=selenium-event-bus + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + + chromium: + image: selenium/node-chromium:4.16.1-20231212 shm_size: 2gb volumes: - ./selenium_server_deploy.jar:/opt/selenium/selenium-server.jar diff --git a/docker-compose-v3-full-grid-swarm.yml b/docker-compose-v3-full-grid-swarm.yml index 2f0ca79a3..116e15876 100644 --- a/docker-compose-v3-full-grid-swarm.yml +++ b/docker-compose-v3-full-grid-swarm.yml @@ -7,7 +7,7 @@ version: '3.7' services: chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb environment: - SE_EVENT_BUS_HOST=selenium-hub @@ -18,7 +18,7 @@ services: entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh' edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb environment: - SE_EVENT_BUS_HOST=selenium-hub @@ -29,7 +29,18 @@ services: entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh' firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 + shm_size: 2gb + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + deploy: + replicas: 1 + entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh' + + chromium: + image: selenium/node-chromium:4.16.1-20231212 shm_size: 2gb environment: - SE_EVENT_BUS_HOST=selenium-hub @@ -40,7 +51,7 @@ services: entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh' selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 ports: - "4442:4442" - "4443:4443" diff --git a/docker-compose-v3-full-grid-tracing.yml b/docker-compose-v3-full-grid-tracing.yml index d650ad0fc..a303c5e58 100644 --- a/docker-compose-v3-full-grid-tracing.yml +++ b/docker-compose-v3-full-grid-tracing.yml @@ -9,7 +9,7 @@ services: - "16686:16686" - "14250:14250" selenium-event-bus: - image: selenium/event-bus:4.12.1-20230920 + image: selenium/event-bus:4.16.1-20231212 container_name: selenium-event-bus ports: - "4442:4442" @@ -21,7 +21,7 @@ services: - SE_ENABLE_TRACING=true - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-event-bus selenium-sessions: - image: selenium/sessions:4.12.1-20230920 + image: selenium/sessions:4.16.1-20231212 container_name: selenium-sessions ports: - "5556:5556" @@ -34,7 +34,7 @@ services: - SE_ENABLE_TRACING=true - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-sessions selenium-session-queue: - image: selenium/session-queue:4.12.1-20230920 + image: selenium/session-queue:4.16.1-20231212 container_name: selenium-session-queue ports: - "5559:5559" @@ -42,7 +42,7 @@ services: - SE_ENABLE_TRACING=true - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-session-queue selenium-distributor: - image: selenium/distributor:4.12.1-20230920 + image: selenium/distributor:4.16.1-20231212 container_name: selenium-distributor ports: - "5553:5553" @@ -61,7 +61,7 @@ services: - SE_ENABLE_TRACING=true - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-distributor selenium-router: - image: selenium/router:4.12.1-20230920 + image: selenium/router:4.16.1-20231212 container_name: selenium-router ports: - "4444:4444" @@ -76,10 +76,10 @@ services: - SE_SESSIONS_MAP_PORT=5556 - SE_SESSION_QUEUE_HOST=selenium-session-queue - SE_SESSION_QUEUE_PORT=5559 - - SE_ENABLE_TRACING=true + - SE_ENABLE_TRACING=true - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-router chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-event-bus @@ -88,9 +88,9 @@ services: - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - SE_ENABLE_TRACING=true - - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-chrome + - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-chrome edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-event-bus @@ -98,10 +98,21 @@ services: - SE_EVENT_BUS_HOST=selenium-event-bus - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - - SE_ENABLE_TRACING=true + - SE_ENABLE_TRACING=true - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-edge firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 + shm_size: 2gb + depends_on: + - selenium-event-bus + environment: + - SE_EVENT_BUS_HOST=selenium-event-bus + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + - SE_ENABLE_TRACING=true + - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-firefox + chromium: + image: selenium/node-chromium:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-event-bus @@ -110,4 +121,4 @@ services: - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - SE_ENABLE_TRACING=true - - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-firefox \ No newline at end of file + - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-chromium diff --git a/docker-compose-v3-full-grid.yml b/docker-compose-v3-full-grid.yml index d97cacc4d..76a21c938 100644 --- a/docker-compose-v3-full-grid.yml +++ b/docker-compose-v3-full-grid.yml @@ -4,7 +4,7 @@ version: "3" services: selenium-event-bus: - image: selenium/event-bus:4.12.1-20230920 + image: selenium/event-bus:4.16.1-20231212 container_name: selenium-event-bus ports: - "4442:4442" @@ -12,7 +12,7 @@ services: - "5557:5557" selenium-sessions: - image: selenium/sessions:4.12.1-20230920 + image: selenium/sessions:4.16.1-20231212 container_name: selenium-sessions ports: - "5556:5556" @@ -24,13 +24,13 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 selenium-session-queue: - image: selenium/session-queue:4.12.1-20230920 + image: selenium/session-queue:4.16.1-20231212 container_name: selenium-session-queue ports: - "5559:5559" selenium-distributor: - image: selenium/distributor:4.12.1-20230920 + image: selenium/distributor:4.16.1-20231212 container_name: selenium-distributor ports: - "5553:5553" @@ -48,7 +48,7 @@ services: - SE_SESSION_QUEUE_PORT=5559 selenium-router: - image: selenium/router:4.12.1-20230920 + image: selenium/router:4.16.1-20231212 container_name: selenium-router ports: - "4444:4444" @@ -65,7 +65,7 @@ services: - SE_SESSION_QUEUE_PORT=5559 chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-event-bus @@ -75,7 +75,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-event-bus @@ -85,11 +85,21 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-event-bus environment: - SE_EVENT_BUS_HOST=selenium-event-bus - SE_EVENT_BUS_PUBLISH_PORT=4442 - - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ No newline at end of file + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + + chromium: + image: selenium/node-chromium:4.16.1-20231212 + shm_size: 2gb + depends_on: + - selenium-event-bus + environment: + - SE_EVENT_BUS_HOST=selenium-event-bus + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 diff --git a/docker-compose-v3-swarm.yml b/docker-compose-v3-swarm.yml index 2f0ca79a3..116e15876 100644 --- a/docker-compose-v3-swarm.yml +++ b/docker-compose-v3-swarm.yml @@ -7,7 +7,7 @@ version: '3.7' services: chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb environment: - SE_EVENT_BUS_HOST=selenium-hub @@ -18,7 +18,7 @@ services: entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh' edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb environment: - SE_EVENT_BUS_HOST=selenium-hub @@ -29,7 +29,18 @@ services: entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh' firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 + shm_size: 2gb + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + deploy: + replicas: 1 + entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh' + + chromium: + image: selenium/node-chromium:4.16.1-20231212 shm_size: 2gb environment: - SE_EVENT_BUS_HOST=selenium-hub @@ -40,7 +51,7 @@ services: entrypoint: bash -c 'SE_OPTS="--host $$HOSTNAME" /opt/bin/entry_point.sh' selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 ports: - "4442:4442" - "4443:4443" diff --git a/docker-compose-v3-tracing.yml b/docker-compose-v3-tracing.yml index f9611c3ef..938f4da86 100644 --- a/docker-compose-v3-tracing.yml +++ b/docker-compose-v3-tracing.yml @@ -9,7 +9,7 @@ services: - "16686:16686" - "14250:14250" chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -21,7 +21,7 @@ services: - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-chrome edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -33,7 +33,7 @@ services: - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-edge firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -44,8 +44,20 @@ services: - SE_ENABLE_TRACING=true - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-firefox + chromium: + image: selenium/node-chromium:4.16.1-20231212 + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + - SE_ENABLE_TRACING=true + - JAVA_OPTS=-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://jaegar:14250 -Dotel.resource.attributes=service.name=selenium-node-chromium + selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 container_name: selenium-hub ports: - "4442:4442" diff --git a/docker-compose-v3-video.yml b/docker-compose-v3-video.yml index 3fbfab199..3f33c8c18 100644 --- a/docker-compose-v3-video.yml +++ b/docker-compose-v3-video.yml @@ -4,7 +4,7 @@ version: "3" services: chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -14,7 +14,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -24,7 +24,17 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + + chromium: + image: selenium/node-chromium:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -34,7 +44,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 chrome_video: - image: selenium/video:ffmpeg-4.3.1-20230920 + image: selenium/video:ffmpeg-6.1-20231212 volumes: - /tmp/videos:/videos depends_on: @@ -44,7 +54,7 @@ services: - FILE_NAME=chrome_video.mp4 edge_video: - image: selenium/video:ffmpeg-4.3.1-20230920 + image: selenium/video:ffmpeg-6.1-20231212 volumes: - /tmp/videos:/videos depends_on: @@ -54,7 +64,7 @@ services: - FILE_NAME=edge_video.mp4 firefox_video: - image: selenium/video:ffmpeg-4.3.1-20230920 + image: selenium/video:ffmpeg-6.1-20231212 volumes: - /tmp/videos:/videos depends_on: @@ -63,8 +73,18 @@ services: - DISPLAY_CONTAINER_NAME=firefox - FILE_NAME=firefox_video.mp4 + chromium_video: + image: selenium/video:ffmpeg-6.1-20231212 + volumes: + - /tmp/videos:/videos + depends_on: + - chrome + environment: + - DISPLAY_CONTAINER_NAME=chromium + - FILE_NAME=chromium_video.mp4 + selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 container_name: selenium-hub ports: - "4442:4442" diff --git a/docker-compose-v3.yml b/docker-compose-v3.yml index 8636018bc..81790ec73 100644 --- a/docker-compose-v3.yml +++ b/docker-compose-v3.yml @@ -4,7 +4,7 @@ version: "3" services: chrome: - image: selenium/node-chrome:4.12.1-20230920 + image: selenium/node-chrome:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -14,7 +14,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 edge: - image: selenium/node-edge:4.12.1-20230920 + image: selenium/node-edge:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -24,7 +24,17 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 firefox: - image: selenium/node-firefox:4.12.1-20230920 + image: selenium/node-firefox:4.16.1-20231212 + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + + chromium: + image: selenium/node-chromium:4.16.1-20231212 shm_size: 2gb depends_on: - selenium-hub @@ -34,7 +44,7 @@ services: - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 selenium-hub: - image: selenium/hub:4.12.1-20230920 + image: selenium/hub:4.16.1-20231212 container_name: selenium-hub ports: - "4442:4442" diff --git a/generate_chart_changelog.sh b/generate_chart_changelog.sh new file mode 100755 index 000000000..4afaedfb6 --- /dev/null +++ b/generate_chart_changelog.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Specify the output file for the CHANGELOG +CHART_DIR="./charts/selenium-grid" +CHANGELOG_FILE="./charts/selenium-grid/CHANGELOG.md" +TAG_PATTERN="selenium-grid" + +# Get current chart app version +CHART_APP_VERSION=$(find . \( -type d -name .git -prune \) -o -type f -name 'Chart.yaml' -print0 | xargs -0 cat | grep ^appVersion | cut -d ':' -f 2 | tr -d '[:space:]') + +# Generate the changelog +generate_changelog() { + # Get a list of tags sorted by commit date + tags=($(git tag --sort=committerdate | grep "^$TAG_PATTERN")) + tags_size=${#tags[@]} + + # Check if there are tags + if [ ${#tags[@]} -eq 0 ]; then + commit_range="HEAD" + elif [ ${#tags[@]} -eq 1 ]; then + previous_tag="${tags[$tags_size-1]}" + current_tag="HEAD" + commit_range="${previous_tag}..${current_tag}" + else + previous_tag="${tags[$tags_size-2]}" + current_tag="${tags[$tags_size-1]}" + commit_range="${previous_tag}..${current_tag}" + fi + + # Get the changes for each section (Added, Removed, Fixed, Changed) + image_tag_changes=$(echo "Chart is using image tag $CHART_APP_VERSION" | sed -e 's/^/- /') + added_changes=$(git log --pretty=format:"%s :: %an" "$commit_range" -- "$CHART_DIR" | grep -iE "^feat|^add" | sed -e 's/^/- /') + removed_changes=$(git log --pretty=format:"%s :: %an" "$commit_range" -- "$CHART_DIR" | grep -iE "^remove|^deprecate|^delete" | sed -e 's/^/- /') + fixed_changes=$(git log --pretty=format:"%s :: %an" "$commit_range" -- "$CHART_DIR" | grep -iE "^fix|^bug" | sed -e 's/^/- /') + changed_changes=$(git log --pretty=format:"%s :: %an" "$commit_range" -- "$CHART_DIR" | grep -iEv "^feat|^add|^remove|^deprecate|^delete|^fix|^bug" | sed -e 's/^/- /') + + if [[ $(cat $CHANGELOG_FILE) == *"${current_tag}"* ]]; then + echo "Changelog already generated for ${current_tag}" + exit 0 + fi + + # Create a temporary file + temp_file=$(mktemp) + + # Write to the temporary file + echo "## :heavy_check_mark: ${current_tag}" >> "$temp_file" + echo "" >> "$temp_file" + echo "$image_tag_changes" >> "$temp_file" + echo "" >> "$temp_file" + + if [ -n "$added_changes" ]; then + echo "### Added" >> "$temp_file" + echo "$added_changes" >> "$temp_file" + echo "" >> "$temp_file" + fi + + if [ -n "$removed_changes" ]; then + echo "### Removed" >> "$temp_file" + echo "$removed_changes" >> "$temp_file" + echo "" >> "$temp_file" + fi + + if [ -n "$fixed_changes" ]; then + echo "### Fixed" >> "$temp_file" + echo "$fixed_changes" >> "$temp_file" + echo "" >> "$temp_file" + fi + + if [ -n "$changed_changes" ]; then + echo "### Changed" >> "$temp_file" + echo "$changed_changes" >> "$temp_file" + echo "" >> "$temp_file" + fi + + # Append the existing content of CHANGELOG to the temporary file + cat "$CHANGELOG_FILE" >> "$temp_file" + + # Overwrite CHANGELOG with the content of the temporary file + mv "$temp_file" "$CHANGELOG_FILE" +} + +# Run the function to generate the changelog +generate_changelog + +echo "Changelog generated successfully at $CHANGELOG_FILE" diff --git a/generate_release_notes.sh b/generate_release_notes.sh index 014f56156..c17fbe344 100755 --- a/generate_release_notes.sh +++ b/generate_release_notes.sh @@ -12,12 +12,14 @@ echo "### Changelog" > release_notes.md git --no-pager log "${LATEST_TAG}...${HEAD_BRANCH}" --pretty=format:"* [\`%h\`](http://github.com/seleniumhq/docker-selenium/commit/%H) - %s :: %an" --reverse >> release_notes.md CHROME_VERSION=$(docker run --rm selenium/node-chrome:${TAG_VERSION} google-chrome --version | awk '{print $3}') +CHROMIUM_VERSION=$(docker run --rm selenium/node-chromium:${TAG_VERSION} chromium --version | awk '{print $2}') EDGE_VERSION=$(docker run --rm selenium/node-edge:${TAG_VERSION} microsoft-edge --version | awk '{print $3}') CHROMEDRIVER_VERSION=$(docker run --rm selenium/node-chrome:${TAG_VERSION} chromedriver --version | awk '{print $2}') +CHROMIUMDRIVER_VERSION=$(docker run --rm selenium/node-chromium:${TAG_VERSION} chromedriver --version | awk '{print $2}') EDGEDRIVER_VERSION=$(docker run --rm selenium/node-edge:${TAG_VERSION} msedgedriver --version | awk '{print $4}') FIREFOX_VERSION=$(docker run --rm selenium/node-firefox:${TAG_VERSION} firefox --version | awk '{print $3}') GECKODRIVER_VERSION=$(docker run --rm selenium/node-firefox:${TAG_VERSION} geckodriver --version | awk 'NR==1{print $2}') -FFMPEG_VERSION=$(docker run --entrypoint="" --rm selenium/video:ffmpeg-4.3.1-${BUILD_DATE} ffmpeg -version | awk '{print $3}' | head -n 1) +FFMPEG_VERSION=$(docker run --entrypoint="" --rm selenium/video:ffmpeg-6.1-${BUILD_DATE} ffmpeg -version | awk '{print $3}' | head -n 1) echo "" >> release_notes.md @@ -29,6 +31,8 @@ echo "* Edge: ${EDGE_VERSION}" >> release_notes.md echo "* EdgeDriver: ${EDGEDRIVER_VERSION}" >> release_notes.md echo "* Firefox: ${FIREFOX_VERSION}" >> release_notes.md echo "* GeckoDriver: ${GECKODRIVER_VERSION}" >> release_notes.md +echo "* Chromium: ${CHROMIUM_VERSION}" >> release_notes.md +echo "* ChromiumDriver: ${CHROMIUMDRIVER_VERSION}" >> release_notes.md echo "* ffmpeg: ${FFMPEG_VERSION}" >> release_notes.md echo "" >> release_notes.md 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/SeleniumTests/__init__.py b/tests/SeleniumTests/__init__.py index 2590fdfa1..c74b0d98b 100644 --- a/tests/SeleniumTests/__init__.py +++ b/tests/SeleniumTests/__init__.py @@ -1,5 +1,7 @@ import unittest +import concurrent.futures import os +import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait @@ -9,7 +11,8 @@ from selenium.webdriver.chrome.options import Options as ChromeOptions SELENIUM_GRID_HOST = os.environ.get('SELENIUM_GRID_HOST', 'localhost') - +SELENIUM_GRID_PORT = os.environ.get('SELENIUM_GRID_PORT', '4444') +WEB_DRIVER_WAIT_TIMEOUT = int(os.environ.get('WEB_DRIVER_WAIT_TIMEOUT', 60)) class SeleniumGenericTests(unittest.TestCase): @@ -51,7 +54,7 @@ def test_visit_basic_auth_secured_page(self): def test_play_video(self): driver = self.driver driver.get('https://hls-js.netlify.com/demo/') - wait = WebDriverWait(driver, 30) + wait = WebDriverWait(driver, WEB_DRIVER_WAIT_TIMEOUT) video = wait.until( EC.element_to_be_clickable((By.TAG_NAME, 'video')) ) @@ -62,33 +65,80 @@ def test_play_video(self): paused = video.get_property('paused') self.assertFalse(paused) + def test_download_file(self): + driver = self.driver + driver.get('https://the-internet.herokuapp.com/download') + file_name = 'some-file.txt' + is_continue = True + try: + wait = WebDriverWait(driver, 30) + file_link = wait.until( + EC.element_to_be_clickable((By.LINK_TEXT, file_name)) + ) + except: + is_continue = False + if is_continue: + file_link.click() + wait.until( + lambda d: len(d.get_downloadable_files()) > 0 and str(d.get_downloadable_files()[0]).endswith(file_name) + ) + self.assertTrue(str(driver.get_downloadable_files()[0]).endswith(file_name)) + def tearDown(self): self.driver.quit() class ChromeTests(SeleniumGenericTests): def setUp(self): + options = ChromeOptions() + options.enable_downloads = True + options.add_argument('disable-features=DownloadBubble,DownloadBubbleV2') self.driver = webdriver.Remote( - options=ChromeOptions(), - command_executor="http://%s:4444" % SELENIUM_GRID_HOST + options=options, + command_executor="http://%s:%s" % (SELENIUM_GRID_HOST,SELENIUM_GRID_PORT) ) class EdgeTests(SeleniumGenericTests): def setUp(self): + options = EdgeOptions() + options.enable_downloads = True + options.add_argument('disable-features=DownloadBubble,DownloadBubbleV2') self.driver = webdriver.Remote( - options=EdgeOptions(), - command_executor="http://%s:4444" % SELENIUM_GRID_HOST + options=options, + command_executor="http://%s:%s" % (SELENIUM_GRID_HOST,SELENIUM_GRID_PORT) ) class FirefoxTests(SeleniumGenericTests): def setUp(self): + profile = webdriver.FirefoxProfile() + profile.set_preference("browser.download.manager.showWhenStarting", False) + profile.set_preference("browser.helperApps.neverAsk.saveToDisk", "*/*") + options = FirefoxOptions() + options.profile = profile + options.enable_downloads = True self.driver = webdriver.Remote( - options=FirefoxOptions(), - command_executor="http://%s:4444" % SELENIUM_GRID_HOST + options=options, + command_executor="http://%s:%s" % (SELENIUM_GRID_HOST,SELENIUM_GRID_PORT) ) def test_title_and_maximize_window(self): self.driver.get('https://the-internet.herokuapp.com') self.driver.maximize_window() self.assertTrue(self.driver.title == 'The Internet') + +class ParallelAutoscaling(): + def run(self, test_classes): + with concurrent.futures.ThreadPoolExecutor() as executor: + futures = [] + for test_class in test_classes: + suite = unittest.TestLoader().loadTestsFromTestCase(test_class) + for test in suite: + futures.append(executor.submit(test)) + for future in concurrent.futures.as_completed(futures): + future.result() + +class ParallelAutoscalingTests(unittest.TestCase): + def test_parallel_autoscaling(self): + runner = ParallelAutoscaling() + runner.run([ChromeTests, EdgeTests, FirefoxTests]) diff --git a/tests/SmokeTests/__init__.py b/tests/SmokeTests/__init__.py index 37917b16b..a2f74c7fa 100644 --- a/tests/SmokeTests/__init__.py +++ b/tests/SmokeTests/__init__.py @@ -9,30 +9,42 @@ from urllib.request import urlopen 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) +HUB_CHECKS_MAX_ATTEMPTS = os.environ.get('HUB_CHECKS_MAX_ATTEMPTS', 3) +HUB_CHECKS_INTERVAL = os.environ.get('HUB_CHECKS_INTERVAL', 10) class SmokeTests(unittest.TestCase): def smoke_test_container(self, port): current_attempts = 0 - max_attempts = int(os.environ.get('GRID_STATUS_MAX_ATTEMPTS', 3)) - sleep_interval = 3 + max_attempts = int(HUB_CHECKS_MAX_ATTEMPTS) + sleep_interval = int(HUB_CHECKS_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): def test_grid_is_up(self): - self.smoke_test_container(4444) + self.smoke_test_container('%s' % SELENIUM_GRID_PORT) diff --git a/tests/bootstrap.sh b/tests/bootstrap.sh index 504ca061a..7ce7b8cf4 100755 --- a/tests/bootstrap.sh +++ b/tests/bootstrap.sh @@ -7,7 +7,7 @@ if [ "${CI:-false}" = "false" ]; then source docker-selenium-tests/bin/activate fi -python -m pip install selenium==4.12.0 \ +python -m pip install selenium==4.16.0 \ docker===6.1.3 \ | grep -v 'Requirement already satisfied' diff --git a/tests/charts/bootstrap.sh b/tests/charts/bootstrap.sh new file mode 100755 index 000000000..d961b63ae --- /dev/null +++ b/tests/charts/bootstrap.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +mkdir -p tests/tests +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 pyyaml==6.0.1 \ + | grep -v 'Requirement already satisfied' + +cd .. +helm template dummy --values tests/charts/templates/render/dummy.yaml \ + charts/selenium-grid > ./tests/tests/output_deployment.yaml + +python tests/charts/templates/test.py "./tests/tests/output_deployment.yaml" +ret_code=$? + +if [ "${CI:-false}" = "false" ]; then + deactivate +fi + +exit $ret_code diff --git a/tests/charts/ci/NodeChrome-values.yaml b/tests/charts/ci/NodeChrome-values.yaml new file mode 100644 index 000000000..f92bfd952 --- /dev/null +++ b/tests/charts/ci/NodeChrome-values.yaml @@ -0,0 +1,16 @@ +# This is used in Helm chart testing +# Configuration for chrome nodes +chromeNode: + nameOverride: my-chrome-name + extraEnvironmentVariables: + - name: SE_OPTS + value: "--enable-managed-downloads true" +# Configuration for chromium nodes +chromiumNode: + enabled: false +# Configuration for edge nodes +edgeNode: + enabled: false +# Configuration for firefox nodes +firefoxNode: + enabled: false diff --git a/tests/charts/ci/NodeChromium-values.yaml b/tests/charts/ci/NodeChromium-values.yaml new file mode 100644 index 000000000..fc015db60 --- /dev/null +++ b/tests/charts/ci/NodeChromium-values.yaml @@ -0,0 +1,16 @@ +# This is used in Helm chart testing +# Configuration for chrome nodes +chromeNode: + enabled: false +# Configuration for chromium nodes +chromiumNode: + nameOverride: my-chromium-name + extraEnvironmentVariables: + - name: SE_OPTS + value: "--enable-managed-downloads true" +# Configuration for edge nodes +edgeNode: + enabled: false +# Configuration for firefox nodes +firefoxNode: + enabled: false diff --git a/tests/charts/ci/NodeEdge-values.yaml b/tests/charts/ci/NodeEdge-values.yaml new file mode 100644 index 000000000..d90b7707d --- /dev/null +++ b/tests/charts/ci/NodeEdge-values.yaml @@ -0,0 +1,16 @@ +# This is used in Helm chart testing +# Configuration for chrome nodes +chromeNode: + enabled: false +# Configuration for chromium nodes +chromiumNode: + enabled: false +# Configuration for edge nodes +edgeNode: + nameOverride: my-edge-name + extraEnvironmentVariables: + - name: SE_OPTS + value: "--enable-managed-downloads true" +# Configuration for firefox nodes +firefoxNode: + enabled: false diff --git a/tests/charts/ci/NodeFirefox-values.yaml b/tests/charts/ci/NodeFirefox-values.yaml new file mode 100644 index 000000000..04822a5c2 --- /dev/null +++ b/tests/charts/ci/NodeFirefox-values.yaml @@ -0,0 +1,16 @@ +# This is used in Helm chart testing +# Configuration for chrome nodes +chromeNode: + enabled: false +# Configuration for chromium nodes +chromiumNode: + enabled: false +# Configuration for edge nodes +edgeNode: + enabled: false +# Configuration for firefox nodes +firefoxNode: + nameOverride: my-firefox-name + extraEnvironmentVariables: + - name: SE_OPTS + value: "--enable-managed-downloads true" diff --git a/tests/charts/ci/ParallelAutoscaling-values.yaml b/tests/charts/ci/ParallelAutoscaling-values.yaml new file mode 100644 index 000000000..2e1d42819 --- /dev/null +++ b/tests/charts/ci/ParallelAutoscaling-values.yaml @@ -0,0 +1,45 @@ +isolateComponents: false +autoscaling: + strategy: default + scaledOptions: + minReplicaCount: 0 + maxReplicaCount: 5 +chromeNode: + nameOverride: my-chrome-name + extraEnvironmentVariables: + - name: SE_OPTS + value: "--enable-managed-downloads true" +chromiumNode: + nameOverride: my-chromium-name + extraEnvironmentVariables: + - name: SE_OPTS + value: "--enable-managed-downloads true" +# Configuration for edge nodes +edgeNode: + nameOverride: my-edge-name + extraEnvironmentVariables: + - name: SE_OPTS + value: "--enable-managed-downloads true" +# Configuration for firefox nodes +firefoxNode: + nameOverride: my-firefox-name + extraEnvironmentVariables: + - name: SE_OPTS + value: "--enable-managed-downloads true" + +ingress: + paths: + - path: /selenium(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: '{{ template "seleniumGrid.hub.fullname" $ }}' + port: + number: 4444 + - path: /(/?)(session/.*/se/vnc) + pathType: ImplementationSpecific + backend: + service: + name: '{{ template "seleniumGrid.hub.fullname" $ }}' + port: + number: 4444 diff --git a/tests/charts/ci/auth-ingress-values.yaml b/tests/charts/ci/auth-ingress-values.yaml new file mode 100644 index 000000000..e5d055ba6 --- /dev/null +++ b/tests/charts/ci/auth-ingress-values.yaml @@ -0,0 +1,34 @@ +ingress: + annotations: + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/app-root: &gridAppRoot "/selenium" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "360" + nginx.ingress.kubernetes.io/proxy-read-timeout: "360" + nginx.ingress.kubernetes.io/proxy-send-timeout: "360" + hostname: "" + paths: + - path: /selenium(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: '{{ template "seleniumGrid.router.fullname" $ }}' + port: + number: 4444 + - path: /(/?)(session/.*/se/vnc) + pathType: ImplementationSpecific + backend: + service: + name: '{{ template "seleniumGrid.router.fullname" $ }}' + port: + number: 4444 + +basicAuth: + enabled: false +isolateComponents: true + +hub: + subPath: *gridAppRoot + +components: + subPath: *gridAppRoot diff --git a/tests/charts/ci/autoscaling-values.yaml b/tests/charts/ci/autoscaling-values.yaml new file mode 100644 index 000000000..7fa80b2bb --- /dev/null +++ b/tests/charts/ci/autoscaling-values.yaml @@ -0,0 +1,5 @@ +autoscaling: + enableWithExistingKEDA: true + scalingType: job + scaledOptions: + minReplicaCount: 0 diff --git a/tests/charts/ci/tracing-values.yaml b/tests/charts/ci/tracing-values.yaml new file mode 100644 index 000000000..065b2c081 --- /dev/null +++ b/tests/charts/ci/tracing-values.yaml @@ -0,0 +1,13 @@ +hub: + extraEnvironmentVariables: + - name: SE_ENABLE_TRACING + value: "true" + - name: SE_JAVA_OPTS + value: "-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://{{ .Release.Name }}-jaeger-all-in-one-headless:14250 -Dotel.resource.attributes=service.name=selenium -Dotel.java.global-autoconfigure.enabled=true" + +components: + extraEnvironmentVariables: + - name: SE_ENABLE_TRACING + value: "true" + - name: SE_JAVA_OPTS + value: "-Dotel.traces.exporter=jaeger -Dotel.exporter.jaeger.endpoint=http://{{ .Release.Name }}-jaeger-all-in-one-headless:14250 -Dotel.resource.attributes=service.name=selenium -Dotel.java.global-autoconfigure.enabled=true" diff --git a/tests/charts/config/ct.yaml b/tests/charts/config/ct.yaml new file mode 100755 index 000000000..7c085d076 --- /dev/null +++ b/tests/charts/config/ct.yaml @@ -0,0 +1,10 @@ +# 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 +chart-repos: + - kedacore=https://kedacore.github.io/charts +upgrade: false +helm-extra-args: --timeout 600s +check-version-increment: false diff --git a/tests/charts/config/kind-cluster.yaml b/tests/charts/config/kind-cluster.yaml new file mode 100755 index 000000000..f0bcacd1b --- /dev/null +++ b/tests/charts/config/kind-cluster.yaml @@ -0,0 +1,18 @@ +# This Config is required for KIND cluster to enable ingress. Documented here https://kind.sigs.k8s.io/docs/user/ingress +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: +- role: control-plane + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/tests/charts/make/chart_build.sh b/tests/charts/make/chart_build.sh new file mode 100755 index 000000000..5d57e4277 --- /dev/null +++ b/tests/charts/make/chart_build.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +CHART_PATH=${CHART_PATH:-"charts/selenium-grid"} +# Function to be executed on command failure +on_failure() { + local exit_status=$? + echo "There is step failed with exit status $exit_status" + exit $exit_status +} + +# 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 .. +rm -rf ${CHART_PATH}/Chart.lock +ct lint --all --config tests/charts/config/ct.yaml +# Helm dependencies build and lint is done by `ct lint` command +rm -rf ${CHART_PATH}/../*.tgz +helm package ${CHART_PATH} --version ${VERSION} --destination ${CHART_PATH}/.. + +readlink -f ${CHART_PATH}/../*.tgz > /tmp/selenium_chart_version +cat /tmp/selenium_chart_version + +if [ "${CI:-false}" = "false" ]; then + deactivate +fi diff --git a/tests/charts/make/chart_cluster_cleanup.sh b/tests/charts/make/chart_cluster_cleanup.sh new file mode 100755 index 000000000..972a9e8b6 --- /dev/null +++ b/tests/charts/make/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/charts/make/chart_cluster_setup.sh b/tests/charts/make/chart_cluster_setup.sh new file mode 100755 index 000000000..76b27872d --- /dev/null +++ b/tests/charts/make/chart_cluster_setup.sh @@ -0,0 +1,52 @@ +#!/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:-"tests/charts/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/charts/make/chart_cluster_cleanup.sh + fi +} + +# Function to be executed on command failure +on_failure() { + local exit_status=$? + echo "There is step failed with exit status $exit_status" + cleanup + exit $exit_status +} + +# 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/charts/config/kind-cluster.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/charts/make/chart_setup_env.sh b/tests/charts/make/chart_setup_env.sh new file mode 100755 index 000000000..8c77101bf --- /dev/null +++ b/tests/charts/make/chart_setup_env.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Function to be executed on command failure +on_failure() { + local exit_status=$? + echo "There is step failed with exit status $exit_status" + exit $exit_status +} + +# 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/charts/make/chart_test.sh b/tests/charts/make/chart_test.sh new file mode 100755 index 000000000..2d87c799e --- /dev/null +++ b/tests/charts/make/chart_test.sh @@ -0,0 +1,76 @@ +#!/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:-"tests/charts/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"} +HUB_CHECKS_INTERVAL=${HUB_CHECKS_INTERVAL:-45} +WEB_DRIVER_WAIT_TIMEOUT=${WEB_DRIVER_WAIT_TIMEOUT:-120} +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() { + local exit_status=$? + echo "Describe all resources in the ${SELENIUM_NAMESPACE} namespace for debugging purposes" + kubectl describe all -n ${SELENIUM_NAMESPACE} + echo "There is step failed with exit status $exit_status" + cleanup + exit $exit_status +} + +# Trap ERR signal and call on_failure function +trap 'on_failure' ERR + +HELM_COMMAND_SET_AUTOSCALING="" +if [ "${SELENIUM_GRID_AUTOSCALING}" = "true" ]; then + HELM_COMMAND_SET_AUTOSCALING="--values ${TEST_VALUES_PATH}/autoscaling-values.yaml \ + --set autoscaling.enableWithExistingKEDA=${SELENIUM_GRID_AUTOSCALING} \ + --set autoscaling.scaledOptions.minReplicaCount=${SELENIUM_GRID_AUTOSCALING_MIN_REPLICA}" +fi + +echo "Deploy Selenium Grid Chart" +helm upgrade --install ${RELEASE_NAME} \ +--values ${TEST_VALUES_PATH}/auth-ingress-values.yaml \ +--values ${TEST_VALUES_PATH}/tracing-values.yaml \ +--values ${TEST_VALUES_PATH}/${MATRIX_BROWSER}-values.yaml \ +${HELM_COMMAND_SET_AUTOSCALING} \ +--set global.seleniumGrid.imageTag=${VERSION} --set global.seleniumGrid.imageRegistry=${NAMESPACE} \ +--set global.seleniumGrid.nodesImageTag=${VERSION} \ +${CHART_PATH} --namespace ${SELENIUM_NAMESPACE} --create-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 HUB_CHECKS_INTERVAL=${HUB_CHECKS_INTERVAL} +export WEB_DRIVER_WAIT_TIMEOUT=${WEB_DRIVER_WAIT_TIMEOUT} +./tests/bootstrap.sh ${MATRIX_BROWSER} + +echo "Get pods status" +kubectl get pods -n ${SELENIUM_NAMESPACE} + +echo "Get all resources in the ${SELENIUM_NAMESPACE} namespace" +kubectl get all -n ${SELENIUM_NAMESPACE} + +cleanup diff --git a/tests/charts/templates/render/dummy.yaml b/tests/charts/templates/render/dummy.yaml new file mode 100644 index 000000000..185cc4191 --- /dev/null +++ b/tests/charts/templates/render/dummy.yaml @@ -0,0 +1,58 @@ +# This is dummy values file for chart template testing +global: + seleniumGrid: + affinity: &affinity + podAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - selenium + topologyKey: "kubernetes.io/hostname" +ingress: + nginx: + proxyTimeout: 360 # Set different proxy timout + proxyBuffer: + # size: 512M # Keep using sub-config default + number: # Disable sub-config + annotations: # Add you own annotations + nginx.ingress.kubernetes.io/use-regex: "true" # Add new key + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/app-root: &gridAppRoot "/selenium" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600" # Override default key + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" # Override default key + hostname: "" + paths: + - path: /selenium(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: '{{ template "seleniumGrid.router.fullname" $ }}' + port: + number: 4444 + - path: /(/?)(session/.*/se/vnc) + pathType: ImplementationSpecific + backend: + service: + name: '{{ template "seleniumGrid.router.fullname" $ }}' + port: + number: 4444 + +isolateComponents: true + +components: + subPath: *gridAppRoot + +chromeNode: + affinity: *affinity + +chromiumNode: + affinity: *affinity + +firefoxNode: + affinity: *affinity + +edgeNode: + affinity: *affinity diff --git a/tests/charts/templates/test.py b/tests/charts/templates/test.py new file mode 100644 index 000000000..08ea8d8a9 --- /dev/null +++ b/tests/charts/templates/test.py @@ -0,0 +1,83 @@ +import yaml +import unittest +import sys +import logging + +logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") +logger = logging.getLogger(__name__) + +def load_template(yaml_file): + try: + with open(yaml_file, 'r') as file: + documents = yaml.safe_load_all(file) + list_of_documents = [doc for doc in documents] + return list_of_documents + except yaml.YAMLError as error: + print("Error in configuration file: ", error) + +class ChartTemplateTests(unittest.TestCase): + def test_set_affinity(self): + resources_name = ['selenium-chrome-node', 'selenium-distributor', 'selenium-edge-node', 'selenium-firefox-node', 'selenium-chromium-node', + 'selenium-event-bus', 'selenium-router', 'selenium-session-map', 'selenium-session-queue'] + count = 0 + logger.info(f"Assert affinity is set in global and nodes") + for doc in LIST_OF_DOCUMENTS: + if doc['metadata']['name'] in resources_name and doc['kind'] == 'Deployment': + logger.info(f"Assert affinity is set in resource {doc['metadata']['name']}") + self.assertTrue(doc['spec']['template']['spec']['affinity']['podAffinity']['requiredDuringSchedulingIgnoredDuringExecution'][0]['labelSelector']['matchExpressions'] is not None) + count += 1 + self.assertEqual(count, len(resources_name), "Not all resources have affinity set") + + def test_ingress_nginx_annotations(self): + resources_name = ['selenium-ingress'] + count = 0 + for doc in LIST_OF_DOCUMENTS: + if doc['metadata']['name'] in resources_name and doc['kind'] == 'Ingress': + logger.info(f"Assert ingress ingress annotations") + logger.info(f"Config `ingress.nginx.proxyTimeout` is able to be set a different value") + self.assertTrue(doc['metadata']['annotations']['nginx.ingress.kubernetes.io/proxy-read-timeout'] == '360') + logger.info(f"Duplicated in `ingress.annotations` take precedence to overwrite the default value") + self.assertTrue(doc['metadata']['annotations']['nginx.ingress.kubernetes.io/proxy-connect-timeout'] == '3600') + logger.info(f"Default annotation is able to be disabled by setting it to null") + self.assertTrue(doc['metadata']['annotations'].get('nginx.ingress.kubernetes.io/proxy-buffers-number') is None) + logger.info(f"Default annotation is added if no override value") + self.assertTrue(doc['metadata']['annotations']['nginx.ingress.kubernetes.io/client-body-buffer-size'] == '512M') + count += 1 + self.assertEqual(count, len(resources_name), "No ingress resources found") + + def test_sub_path_append_to_node_grid_url(self): + resources_name = ['selenium-node-config'] + count = 0 + for doc in LIST_OF_DOCUMENTS: + if doc['metadata']['name'] in resources_name and doc['kind'] == 'ConfigMap': + logger.info(f"Assert subPath is appended to node grid url") + self.assertTrue(doc['data']['SE_NODE_GRID_URL'] == 'http://admin:admin@selenium-router.default:4444/selenium') + count += 1 + self.assertEqual(count, len(resources_name), "No node config resources found") + + def test_sub_path_set_to_grid_env_var(self): + resources_name = ['selenium-router'] + is_present = False + for doc in LIST_OF_DOCUMENTS: + if doc['metadata']['name'] in resources_name and doc['kind'] == 'Deployment': + logger.info(f"Assert subPath is set to grid ENV variable") + list_env = doc['spec']['template']['spec']['containers'][0]['env'] + for env in list_env: + if env['name'] == 'SE_SUB_PATH' and env['value'] == '/selenium': + is_present = True + self.assertTrue(is_present, "ENV variable SE_SUB_PATH is not populated") + +if __name__ == '__main__': + failed = False + try: + FILE_NAME = sys.argv[1] + LIST_OF_DOCUMENTS = load_template(FILE_NAME) + suite = unittest.TestLoader().loadTestsFromTestCase(ChartTemplateTests) + test_runner = unittest.TextTestRunner(verbosity=3) + failed = not test_runner.run(suite).wasSuccessful() + except Exception as e: + logger.fatal(e) + failed = True + + if failed: + exit(1) diff --git a/tests/docker-compose-v3-test-video.yml b/tests/docker-compose-v3-test-video.yml index e80c56b92..dd07a4b98 100644 --- a/tests/docker-compose-v3-test-video.yml +++ b/tests/docker-compose-v3-test-video.yml @@ -4,7 +4,7 @@ version: "3" services: browser: - image: selenium/node-${BROWSER}:${TAG} + image: ${NAME}/node-${BROWSER}:${TAG} shm_size: 2gb depends_on: - selenium-hub @@ -12,11 +12,12 @@ services: - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + - SE_OPTS=--enable-managed-downloads true ports: - "6900:5900" browser_video: - image: selenium/video:${VIDEO_TAG} + image: ${NAME}/video:${VIDEO_TAG} volumes: - ./videos:/videos depends_on: @@ -26,7 +27,7 @@ services: - FILE_NAME=${VIDEO_FILE_NAME} selenium-hub: - image: selenium/hub:${TAG} + image: ${NAME}/hub:${TAG} container_name: selenium-hub ports: - "4442:4442" diff --git a/tests/test.py b/tests/test.py index ded685a97..02fb810a0 100644 --- a/tests/test.py +++ b/tests/test.py @@ -60,9 +60,12 @@ 'NodeFirefox': 'FirefoxTests', 'StandaloneFirefox': 'FirefoxTests', - # Chrome Images + # Chromium Images 'NodeChromium': 'ChromeTests', 'StandaloneChromium': 'ChromeTests', + + # Chart Parallel Test + 'ParallelAutoscaling': 'ParallelAutoscalingTests' } FROM_IMAGE_ARGS = { @@ -144,6 +147,8 @@ def launch_container(container, **kwargs): 'SE_EVENT_BUS_PUBLISH_PORT': 4442, 'SE_EVENT_BUS_SUBSCRIBE_PORT': 4443 } + if container != 'Hub': + environment['SE_OPTS'] = "--enable-managed-downloads true" container_id = client.containers.run("%s/%s:%s" % (NAMESPACE, IMAGE_NAME_MAP[container], VERSION), detach=True, environment=environment, @@ -168,7 +173,7 @@ def get_build_path(container): def standalone_browser_container_matches(container): - return re.match("(Standalone)(Chromium|Chrome|Firefox|Edge)", container) + return re.match("(Standalone)(Chrome|Firefox|Edge|Chromium)", container) if __name__ == '__main__': @@ -177,7 +182,7 @@ def standalone_browser_container_matches(container): use_random_user_id = USE_RANDOM_USER_ID == 'true' run_in_docker_compose = RUN_IN_DOCKER_COMPOSE == 'true' - random_user_id = random.randint(100000, 2147483647) + random_user_id = "%s:%s" % (random.randint(2000, 65000), random.randint(2001, 65001)) if use_random_user_id: logger.info("Running tests with a random user ID -> %s" % random_user_id) @@ -199,9 +204,9 @@ def standalone_browser_container_matches(container): """ ports = {'4444': 4444} if use_random_user_id: - test_container_id = launch_container(image, ports=ports, user=random_user_id) + test_container_id = launch_container(image, ports=ports, user=random_user_id) else: - test_container_id = launch_container(image, ports=ports) + test_container_id = launch_container(image, ports=ports) else: """ Hub / Node Configuration @@ -211,9 +216,9 @@ def standalone_browser_container_matches(container): hub_id = launch_hub("grid") ports = {'5555': 5555, '7900': 7900} if use_random_user_id: - test_container_id = launch_container(image, network='grid', ports=ports, user=random_user_id) + test_container_id = launch_container(image, network='grid', ports=ports, user=random_user_id) else: - test_container_id = launch_container(image, network='grid', ports=ports) + test_container_id = launch_container(image, network='grid', ports=ports) prune_networks() logger.info('========== / Containers ready to go ==========') @@ -251,13 +256,13 @@ def standalone_browser_container_matches(container): test_container.remove() if standalone: - logger.info("Standalone Cleaned up") + logger.info("Standalone Cleaned up") else: - # Kill the launched hub - hub = client.containers.get(hub_id) - hub.kill() - hub.remove() - logger.info("Hub / Node Cleaned up") + # Kill the launched hub + hub = client.containers.get(hub_id) + hub.kill() + hub.remove() + logger.info("Hub / Node Cleaned up") if failed: exit(1) diff --git a/update_tag_in_docs_and_files.sh b/update_tag_in_docs_and_files.sh index 43348cc37..11da34f95 100755 --- a/update_tag_in_docs_and_files.sh +++ b/update_tag_in_docs_and_files.sh @@ -4,20 +4,46 @@ LATEST_TAG=$1 NEXT_TAG=$2 LATEST_DATE=$(echo ${LATEST_TAG} | sed 's/.*-//') NEXT_DATE=$(echo ${NEXT_TAG} | sed 's/.*-//') +latest_chart_app_version=$(find . \( -type d -name .git -prune \) -o -type f -name 'Chart.yaml' -print0 | xargs -0 cat | grep ^appVersion | cut -d ':' -f 2 | tr -d '[:space:]') echo -e "\033[0;32m Updating tag displayed in docs and files...\033[0m" echo -e "\033[0;32m LATEST_TAG -> ${LATEST_TAG}\033[0m" echo -e "\033[0;32m NEXT_TAG -> ${NEXT_TAG}\033[0m" # If you want to test this locally and you are using macOS, do `brew install gnu-sed` and change `sed` for `gsed`. -find . \( -type d -name .git -prune \) -o -type f ! -name 'Chart.yaml' -print0 | xargs -0 sed -i "s/${LATEST_TAG}/${NEXT_TAG}/g" +find . \( -type d -name .git -prune \) -o -type f ! -name 'CHANGELOG.md' -print0 | xargs -0 sed -i "s/${LATEST_TAG}/${NEXT_TAG}/g" echo -e "\033[0;32m Updating date used in some docs and files...\033[0m" echo -e "\033[0;32m LATEST_DATE -> ${LATEST_DATE}\033[0m" echo -e "\033[0;32m NEXT_DATE -> ${NEXT_DATE}\033[0m" # If you want to test this locally and you are using macOS, do `brew install gnu-sed` and change `sed` for `gsed`. -find . \( -type d -name .git -prune \) -o -type f ! -name 'Chart.yaml' -print0 | xargs -0 sed -i "s/${LATEST_DATE}/${NEXT_DATE}/g" +find . \( -type d -name .git -prune \) -o -type f ! -name 'CHANGELOG.md' -print0 | xargs -0 sed -i "s/${LATEST_DATE}/${NEXT_DATE}/g" + +# Bump chart version and appVersion if next tag is different +if [ "$latest_chart_app_version" == $LATEST_TAG ] && [ "$latest_chart_app_version" != "$NEXT_TAG" ]; then + IFS='.' read -ra latest_version_parts <<< "$LATEST_TAG" + IFS='.' read -ra next_version_parts <<< "$NEXT_TAG" + latest_chart_version=$(find . \( -type d -name .git -prune \) -o -type f -name 'Chart.yaml' -print0 | xargs -0 cat | grep ^version | cut -d ':' -f 2 | tr -d '[:space:]') + IFS='.' read -ra latest_chart_version_parts <<< "$latest_chart_version" + if [ "${latest_version_parts[0]}" != "${next_version_parts[0]}" ]; then + ((latest_chart_version_parts[0]++)) + latest_chart_version_parts[1]=0 + latest_chart_version_parts[2]=0 + elif [ "${latest_version_parts[1]}" != "${next_version_parts[1]}" ]; then + ((latest_chart_version_parts[1]++)) + latest_chart_version_parts[2]=0 + elif [ "${latest_version_parts[2]}" != "${next_version_parts[2]}" ]; then + ((latest_chart_version_parts[2]++)) + fi + next_chart_version="${latest_chart_version_parts[0]}.${latest_chart_version_parts[1]}.${latest_chart_version_parts[2]}" + echo -e "\033[0;32m Updating chart version...\033[0m" + echo -e "\033[0;32m LATEST_CHART_VERSION -> ${latest_chart_version}\033[0m" + echo -e "\033[0;32m NEXT_CHART_VERSION -> ${next_chart_version}\033[0m" + # If you want to test this locally and you are using macOS, do `brew install gnu-sed` and change `sed` for `gsed`. + find . \( -type d -name .git -prune \) -o -type f -name 'Chart.yaml' -print0 | xargs -0 sed -i "s/${latest_chart_version}/${next_chart_version}/g" + find . \( -type d -name .git -prune \) -o -type f -name 'bug_report.yaml' -print0 | xargs -0 sed -i "s/${latest_chart_version}/${next_chart_version}/g" +fi git diff | cat