From d4caf9de78383d4740b389d98ba64832af5290c4 Mon Sep 17 00:00:00 2001 From: Viet Nguyen Duc Date: Wed, 1 May 2024 05:39:53 +0000 Subject: [PATCH] feat: Video recording with dynamic file name based on metadata in tests Signed-off-by: Viet Nguyen Duc --- .github/workflows/helm-chart-test.yml | 18 ++++- .github/workflows/test-video.yml | 30 ++++----- Makefile | 65 ++++++++++++------ NodeBase/Dockerfile | 1 + README.md | 55 ++++++++++++++++ Video/Dockerfile | 3 +- Video/upload.sh | 3 +- Video/video.sh | 66 ++++++++++--------- Video/video_graphQLQuery.sh | 20 +++++- charts/selenium-grid/README.md | 2 +- charts/selenium-grid/templates/_helpers.tpl | 4 +- .../templates/recorder-configmap.yaml | 4 +- docker-compose-v3-video-upload.yml | 3 + docker-compose-v3-video.yml | 12 +++- tests/SeleniumTests/__init__.py | 17 +++-- tests/charts/ci/NoAutoscaling-values.yaml | 38 +++++++++++ tests/charts/ci/NodeChrome-values.yaml | 14 ---- tests/charts/ci/NodeEdge-values.yaml | 14 ---- tests/charts/ci/NodeFirefox-values.yaml | 14 ---- tests/charts/ci/base-recorder-values.yaml | 4 ++ tests/charts/ci/local-pvc.yaml | 30 +++++++++ tests/charts/make/chart_test.sh | 38 +++++++++-- tests/docker-compose-v3-test-node-docker.yaml | 2 +- tests/docker-compose-v3-test-video.yml | 5 +- 24 files changed, 328 insertions(+), 134 deletions(-) create mode 100644 tests/charts/ci/NoAutoscaling-values.yaml delete mode 100644 tests/charts/ci/NodeChrome-values.yaml delete mode 100644 tests/charts/ci/NodeEdge-values.yaml delete mode 100644 tests/charts/ci/NodeFirefox-values.yaml create mode 100644 tests/charts/ci/local-pvc.yaml diff --git a/.github/workflows/helm-chart-test.yml b/.github/workflows/helm-chart-test.yml index 6459528d35..9c32a99cf0 100644 --- a/.github/workflows/helm-chart-test.yml +++ b/.github/workflows/helm-chart-test.yml @@ -37,6 +37,12 @@ jobs: fail-fast: false matrix: include: + - k8s-version: 'v1.25.16' + test-strategy: disabled + cluster: 'minikube' + helm-version: 'v3.10.3' + test-existing-keda: false + test-upgrade: true - k8s-version: 'v1.26.15' test-strategy: job cluster: 'minikube' @@ -158,7 +164,7 @@ jobs: timeout_minutes: 30 max_attempts: 3 command: | - NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} TEST_EXISTING_KEDA=${TEST_EXISTING_KEDA} make chart_test_autoscaling_${{ matrix.test-strategy }} + NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} TEST_EXISTING_KEDA=${TEST_EXISTING_KEDA} TEST_UPGRADE_CHART=false make chart_test_autoscaling_${{ matrix.test-strategy }} - name: Test chart upgrade if: (matrix.test-upgrade == true) run: | @@ -179,3 +185,13 @@ jobs: name: ${{ env.ARTIFACT_NAME }}-artifacts path: ./tests/tests/ if-no-files-found: ignore + - name: Check video integrity + if: always() + run: | + make test_video_integrity + - name: Upload test video artifacts + if: always() + uses: actions/upload-artifact@main + with: + name: ${{ env.ARTIFACT_NAME }}-videos + path: ./tests/videos/ diff --git a/.github/workflows/test-video.yml b/.github/workflows/test-video.yml index 664d80c9d8..4613abc5c1 100644 --- a/.github/workflows/test-video.yml +++ b/.github/workflows/test-video.yml @@ -36,7 +36,15 @@ jobs: strategy: fail-fast: false matrix: - test-strategy: [test_video, test_parallel, test_node_docker] + include: + - test-strategy: test_video + test-video: true + - test-strategy: test_video_dynamic_name + test-video: true + - test-strategy: test_node_docker + test-video: true + - test-strategy: test_parallel + test-video: false steps: - uses: actions/checkout@main - name: Set up QEMU @@ -99,21 +107,9 @@ jobs: REQUEST_TIMEOUT: ${{ github.event.inputs.request-timeout || '400' }} - name: Run Docker Compose to ${{ matrix.test-strategy }} run: USE_RANDOM_USER_ID=${USE_RANDOM_USER} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make ${{ matrix.test-strategy }} - - name: Upload recorded Chrome video - if: matrix.test-strategy == 'test_video' + - name: Upload recorded video + if: matrix.test-video == true uses: actions/upload-artifact@main with: - name: chrome_video - path: ./tests/videos/chrome_video.mp4 - - name: Upload recorded Edge video - if: matrix.test-strategy == 'test_video' - uses: actions/upload-artifact@main - with: - name: edge_video - path: ./tests/videos/edge_video.mp4 - - name: Upload recorded Firefox video - if: matrix.test-strategy == 'test_video' - uses: actions/upload-artifact@main - with: - name: firefox_video - path: ./tests/videos/firefox_video.mp4 + name: "${{ matrix.test-strategy }}_artifacts" + path: ./tests/videos/ diff --git a/Makefile b/Makefile index e08272d08e..88469f0480 100644 --- a/Makefile +++ b/Makefile @@ -447,6 +447,10 @@ test_parallel: hub chrome firefox edge docker compose -f docker-compose-v3-test-parallel.yml up --no-log-prefix --exit-code-from tests --build ; \ done +test_video_dynamic_name: + VIDEO_FILE_NAME=auto TEST_DELAY_AFTER_TEST=10 \ + make test_video + # This should run on its own CI job. There is no need to combine it with the other tests. # Its main purpose is to check that a video file was generated. test_video: video hub chrome firefox edge @@ -460,31 +464,31 @@ test_video: video hub chrome firefox edge echo NODE=$$node >> .env ; \ echo UID=$$(id -u) >> .env ; \ echo BINDING_VERSION=$(BINDING_VERSION) >> .env ; \ + echo TEST_DELAY_AFTER_TEST=$(or $(TEST_DELAY_AFTER_TEST), 0) >> .env ; \ if [ $$node = "NodeChrome" ] ; then \ echo BROWSER=chrome >> .env ; \ - echo VIDEO_FILE_NAME=chrome_video.mp4 >> .env ; \ + echo VIDEO_FILE_NAME=$${VIDEO_FILE_NAME:-"chrome_video.mp4"} >> .env ; \ + echo VIDEO_FILE_NAME_SUFFIX=$${VIDEO_FILE_NAME_SUFFIX:-"true"} >> .env ; \ fi ; \ if [ $$node = "NodeEdge" ] ; then \ echo BROWSER=edge >> .env ; \ - echo VIDEO_FILE_NAME=edge_video.mp4 >> .env ; \ + echo VIDEO_FILE_NAME=$${VIDEO_FILE_NAME:-"edge_video.mp4"} >> .env ; \ + echo VIDEO_FILE_NAME_SUFFIX=$${VIDEO_FILE_NAME_SUFFIX:-"false"} >> .env ; \ fi ; \ if [ $$node = "NodeFirefox" ] ; then \ echo BROWSER=firefox >> .env ; \ - echo VIDEO_FILE_NAME=firefox_video.mp4 >> .env ; \ + echo VIDEO_FILE_NAME=$${VIDEO_FILE_NAME:-"firefox_video.mp4"} >> .env ; \ + echo VIDEO_FILE_NAME_SUFFIX=$${VIDEO_FILE_NAME_SUFFIX:-"true"} >> .env ; \ fi ; \ docker compose -f docker-compose-v3-test-video.yml up --abort-on-container-exit --build ; \ 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 -u $$(id -u) -v $$(pwd):$$(pwd) -w $$(pwd) --entrypoint="" $(FFMPEG_BASED_NAME)/ffmpeg:$(FFMPEG_BASED_TAG) ffmpeg -v error -i ./tests/videos/chrome_video.mp4 -f null - 2>error.log - docker run -u $$(id -u) -v $$(pwd):$$(pwd) -w $$(pwd) --entrypoint="" $(FFMPEG_BASED_NAME)/ffmpeg:$(FFMPEG_BASED_TAG) ffmpeg -v error -i ./tests/videos/firefox_video.mp4 -f null - 2>error.log - docker run -u $$(id -u) -v $$(pwd):$$(pwd) -w $$(pwd) --entrypoint="" $(FFMPEG_BASED_NAME)/ffmpeg:$(FFMPEG_BASED_TAG) ffmpeg -v error -i ./tests/videos/edge_video.mp4 -f null - 2>error.log + make test_video_integrity test_node_docker: hub standalone_docker standalone_chrome standalone_firefox standalone_edge video sudo rm -rf ./tests/tests sudo rm -rf ./tests/videos; mkdir -p ./tests/videos/Downloads sudo chmod -R 777 ./tests/videos - for node in DeploymentAutoscaling JobAutoscaling ; do \ + for node in NodeChrome NodeFirefox NodeEdge ; do \ cd tests || true ; \ DOWNLOADS_DIR="./videos/Downloads" ; \ sudo rm -rf $$DOWNLOADS_DIR/* ; \ @@ -496,6 +500,7 @@ test_node_docker: hub standalone_docker standalone_chrome standalone_firefox sta echo LOG_LEVEL=$(or $(LOG_LEVEL), "INFO") >> .env ; \ echo REQUEST_TIMEOUT=$(or $(REQUEST_TIMEOUT), 300) >> .env ; \ echo SELENIUM_ENABLE_MANAGED_DOWNLOADS=$(or $(SELENIUM_ENABLE_MANAGED_DOWNLOADS), "false") >> .env ; \ + echo TEST_DELAY_AFTER_TEST=$(or $(TEST_DELAY_AFTER_TEST), 5) >> .env ; \ echo NODE=$$node >> .env ; \ echo UID=$$(id -u) >> .env ; \ echo BINDING_VERSION=$(BINDING_VERSION) >> .env ; \ @@ -509,6 +514,7 @@ test_node_docker: hub standalone_docker standalone_chrome standalone_firefox sta exit 1 ; \ fi ; \ done + make test_video_integrity test_custom_ca_cert: VERSION=$(TAG_VERSION) NAMESPACE=$(NAMESPACE) ./tests/customCACert/bootstrap.sh @@ -528,20 +534,37 @@ chart_build_nightly: chart_build: VERSION=$(TAG_VERSION) ./tests/charts/make/chart_build.sh +test_video_integrity: + # Using ffmpeg to verify file integrity + # https://superuser.com/questions/100288/how-can-i-check-the-integrity-of-a-video-file-avi-mpeg-mp4 + list_files=$$(find ./tests/videos -type f -name "*.mp4"); \ + echo "Number of video files: $$(echo $$list_files | wc -w)"; \ + number_corrupted_files=0; \ + if [ -z "$$list_files" ]; then \ + echo "No video files found"; \ + exit 1; \ + fi; \ + for file in $$list_files; do \ + echo "Checking video file: $$file"; \ + docker run -u $$(id -u) -v $$(pwd):$$(pwd) -w $$(pwd) --entrypoint="" $(FFMPEG_BASED_NAME)/ffmpeg:$(FFMPEG_BASED_TAG) ffmpeg -v error -i "$$file" -f null - ; \ + if [ $$? -ne 0 ]; then \ + echo "Video file $$file is corrupted"; \ + number_corrupted_files=$$((number_corrupted_files+1)); \ + fi; \ + echo "------"; \ + done; \ + if [ $$((number_corrupted_files)) -gt 0 ]; then \ + echo "Number of corrupted video files: $$number_corrupted_files"; \ + exit 1; \ + fi + chart_test_template: ./tests/charts/bootstrap.sh -chart_test_chrome: - VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) BINDING_VERSION=$(BINDING_VERSION) \ - ./tests/charts/make/chart_test.sh NodeChrome - -chart_test_firefox: - VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) BINDING_VERSION=$(BINDING_VERSION) \ - ./tests/charts/make/chart_test.sh NodeFirefox - -chart_test_edge: +chart_test_autoscaling_disabled: + SELENIUM_GRID_AUTOSCALING=false TEST_DELAY_AFTER_TEST=15 CHART_ENABLE_TRACING=true SELENIUM_GRID_HOST=$$(hostname -i) RELEASE_NAME=selenium \ VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) BINDING_VERSION=$(BINDING_VERSION) \ - ./tests/charts/make/chart_test.sh NodeEdge + ./tests/charts/make/chart_test.sh NoAutoscaling chart_test_autoscaling_deployment_https: CHART_FULL_DISTRIBUTED_MODE=true CHART_ENABLE_INGRESS_HOSTNAME=true CHART_ENABLE_BASIC_AUTH=true SELENIUM_GRID_PROTOCOL=https SELENIUM_GRID_PORT=443 \ @@ -550,13 +573,13 @@ chart_test_autoscaling_deployment_https: ./tests/charts/make/chart_test.sh DeploymentAutoscaling chart_test_autoscaling_deployment: - CHART_ENABLE_TRACING=true SELENIUM_GRID_TEST_HEADLESS=true SELENIUM_GRID_HOST=$$(hostname -i) RELEASE_NAME=selenium \ + CHART_ENABLE_TRACING=true SELENIUM_GRID_HOST=$$(hostname -i) RELEASE_NAME=selenium \ SELENIUM_GRID_AUTOSCALING_MIN_REPLICA=1 \ VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) BINDING_VERSION=$(BINDING_VERSION) \ ./tests/charts/make/chart_test.sh DeploymentAutoscaling chart_test_autoscaling_job_https: - SELENIUM_GRID_TEST_HEADLESS=true SELENIUM_GRID_PROTOCOL=https CHART_ENABLE_BASIC_AUTH=true RELEASE_NAME=selenium SELENIUM_GRID_PORT=443 SUB_PATH=/ \ + SELENIUM_GRID_PROTOCOL=https CHART_ENABLE_BASIC_AUTH=true RELEASE_NAME=selenium SELENIUM_GRID_PORT=443 SUB_PATH=/ \ VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) BINDING_VERSION=$(BINDING_VERSION) \ ./tests/charts/make/chart_test.sh JobAutoscaling diff --git a/NodeBase/Dockerfile b/NodeBase/Dockerfile index fc9dc5534b..0838223585 100644 --- a/NodeBase/Dockerfile +++ b/NodeBase/Dockerfile @@ -72,6 +72,7 @@ RUN apt-get update -qqy \ # Xvfb #============== xvfb \ + libxcb1 \ xauth \ pulseaudio \ #===== diff --git a/README.md b/README.md index d902165604..5e4d7736fe 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Talk to us at https://www.selenium.dev/support/ * [Hub and Nodes](#hub-and-nodes) * [Fully distributed mode - Router, Queue, Distributor, EventBus, SessionMap and Nodes](#fully-distributed-mode---router-queue-distributor-eventbus-sessionmap-and-nodes) * [Video recording](#video-recording) +* [Video recording with dynamic file name based on metadata in tests](#video-recording-with-dynamic-file-name-based-on-metadata-in-tests) * [Video recording and uploading](#video-recording-and-uploading) * [Dynamic Grid](#dynamic-grid) * [Configuration example](#configuration-example) @@ -554,6 +555,41 @@ Here is an example using a Hub and a few Nodes: [`docker-compose-v3-video.yml`](docker-compose-v3-video.yml) +## Video recording with dynamic file name based on metadata in tests + +Based on the support of [Metadata in tests](https://www.selenium.dev/documentation/grid/getting_started/#metadata-in-tests). When the video recorder is sidecar deployed with the browser node with enabling `SE_VIDEO_FILE_NAME=auto` and adding metadata to your tests, video file name will extract value of capability `se:name` and use it as the video file name. + +For example in Python binding: + +```python +from selenium.webdriver.chrome.options import Options as ChromeOptions +from selenium import webdriver + +options = ChromeOptions() +options.set_capability('se:name', 'test_visit_basic_auth_secured_page (ChromeTests)') +driver = webdriver.Remote(options=options, command_executor="http://localhost:4444") +driver.get("https://selenium.dev") +driver.quit() +``` + +The output video file name will be `test_visit_basic_auth_secured_page_ChromeTests_.mp4`. + +If your test name is handled by the test framework, and it is unique for sure, you also can disable the session id appends to the video file name by setting `SE_VIDEO_FILE_NAME_SUFFIX=false`. + +File name will be trimmed to 255 characters to avoid long file names. Moreover, `space` character will be replaced by `_` and only characters alphabets, numbers, `-` (hyphen), `_` (underscore) are retained in the file name. + +The trim regex is able to be customized by setting `SE_VIDEO_FILE_NAME_TRIM_REGEX` environment variable. The default value is `[:alnum:]-_`. The regex should be compatible with the `tr` command in bash. + +At deployment level, the recorder container is up always. In addition, you can disable video recording process via session capability `se:recordVideo`. For example in Python binding: + +```python +options.set_capability('se:recordVideo', False) +``` + +In recorder container will perform query GraphQL in Hub based on Node SessionId and extract the value of `se:recordVideo` in capabilities before deciding to start video recording process or not. + +Notes: To reach the GraphQL endpoint, the recorder container needs to know the Hub URL. The Hub URL can be passed via environment variable `SE_NODE_GRID_URL`. For example `SE_NODE_GRID_URL` is `http://selenium-hub:4444`. + ## Video recording and uploading [RCLONE](https://rclone.org/) is installed in the video recorder image. You can use it to upload the videos to a cloud storage service. @@ -836,6 +872,25 @@ for example: After running a test, check the path you mounted to the Docker container, (`${PWD}/assets`), and you should see videos and session information. + +From language bindings, you can set the `se:name` capability to change output video file name dynamically. For example, in Python binding: + +```python +from selenium.webdriver.chrome.options import Options as ChromeOptions +from selenium import webdriver + +options = ChromeOptions() +options.set_capability('se:recordVideo', True) +options.set_capability('se:screenResolution', '1920x1080') +options.set_capability('se:name', 'test_visit_basic_auth_secured_page (ChromeTests)') +driver = webdriver.Remote(options=options, command_executor="http://localhost:4444") +driver.get("https://selenium.dev") +driver.quit() +``` + +After test executed, under (`${PWD}/assets`) you can see the video file name in path `//test_visit_basic_auth_secured_page_ChromeTests.mp4` + +The file name will be trimmed to 255 characters to avoid long file names. Moreover, the `space` character will be replaced by `_`, and only the characters alphabets, numbers, `-` (hyphen), and `_` (underscore) are retained in the file name. (This feat is available once this [PR](https://github.com/SeleniumHQ/selenium/pull/13907) merged) ___ ## Deploying to Kubernetes diff --git a/Video/Dockerfile b/Video/Dockerfile index ee21c92305..d851e943dd 100755 --- a/Video/Dockerfile +++ b/Video/Dockerfile @@ -30,7 +30,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ RUN apt-get -qqy update \ && apt-get upgrade -yq \ && apt-get -qqy --no-install-recommends install \ - supervisor x11-xserver-utils x11-utils curl jq python3-pip tzdata acl unzip \ + supervisor x11-xserver-utils x11-utils libxcb1-dev curl jq python3-pip tzdata acl unzip \ && python3 -m pip install --upgrade pip \ && python3 -m pip install --upgrade setuptools \ && python3 -m pip install --upgrade wheel \ @@ -108,5 +108,6 @@ ENV SE_FRAME_RATE 15 ENV SE_CODEC libx264 ENV SE_PRESET "-preset ultrafast" ENV SE_VIDEO_FILE_NAME video.mp4 +ENV SE_VIDEO_FILE_NAME_TRIM_REGEX "[:alnum:]-_" EXPOSE 9000 diff --git a/Video/upload.sh b/Video/upload.sh index 15f914691d..3a14412a33 100755 --- a/Video/upload.sh +++ b/Video/upload.sh @@ -48,9 +48,10 @@ function graceful_exit() { wait ${pid} done rm -rf ${FORCE_EXIT_FILE} + rm -rf ${UPLOAD_PIPE_FILE} || true echo "Uploader is ready to shutdown" } -trap graceful_exit EXIT +trap graceful_exit SIGTERM SIGINT EXIT while [ ! -p ${UPLOAD_PIPE_FILE} ]; do diff --git a/Video/video.sh b/Video/video.sh index 63aee79565..3f9a1e1058 100755 --- a/Video/video.sh +++ b/Video/video.sh @@ -16,6 +16,7 @@ SE_VIDEO_INTERNAL_UPLOAD=${SE_VIDEO_INTERNAL_UPLOAD:-"false"} SE_SERVER_PROTOCOL=${SE_SERVER_PROTOCOL:-"http"} SE_NODE_PORT=${SE_NODE_PORT:-"5555"} max_attempts=${SE_VIDEO_WAIT_ATTEMPTS:-50} +process_name="video.recorder" if [ "${SE_VIDEO_INTERNAL_UPLOAD}" = "true" ]; then @@ -31,7 +32,7 @@ fi function create_pipe() { if [[ "${VIDEO_UPLOAD_ENABLED}" != "false" ]] && [[ -n "${UPLOAD_DESTINATION_PREFIX}" ]]; then - echo "Create pipe if not exists for video upload stream" + echo "$(date +%FT%T%Z) [${process_name}] - Create pipe if not exists for video upload stream" if [[ ! -p ${UPLOAD_PIPE_FILE} ]]; then mkfifo ${UPLOAD_PIPE_FILE} @@ -46,7 +47,7 @@ function wait_util_uploader_shutdown() { then while [[ -f ${FORCE_EXIT_FILE} ]] && [[ ${wait} -lt ${max_wait} ]]; do - echo "Waiting for force exit file to be consumed by external upload container" + echo "$(date +%FT%T%Z) [${process_name}] - Waiting for force exit file to be consumed by external upload container" sleep 1 wait=$((wait+1)) done @@ -55,11 +56,11 @@ function wait_util_uploader_shutdown() { then while [[ $(pgrep rclone | wc -l) -gt 0 ]] do - echo "Recorder is waiting for RCLONE to finish" + echo "$(date +%FT%T%Z) [${process_name}] - Recorder is waiting for RCLONE to finish" sleep 1 done fi - echo "Ready to shutdown the recorder" + echo "$(date +%FT%T%Z) [${process_name}] - Ready to shutdown the recorder" } function send_exit_signal_to_uploader() { @@ -73,24 +74,24 @@ function send_exit_signal_to_uploader() { function exit_on_max_session_reach() { if [[ $max_recorded_count -gt 0 ]] && [[ $recorded_count -ge $max_recorded_count ]]; then - echo "Node will be drained since max sessions reached count number ($max_recorded_count)" + echo "$(date +%FT%T%Z) [${process_name}] - Node will be drained since max sessions reached count number ($max_recorded_count)" exit fi } function stop_recording() { - echo "Stopping to record video" - pkill -INT ffmpeg + echo "$(date +%FT%T%Z) [${process_name}] - Stopping to record video" + kill -INT $(pgrep ffmpeg) recorded_count=$((recorded_count+1)) recording_started="false" if [[ "${VIDEO_UPLOAD_ENABLED}" != "false" ]] && [[ -n "${UPLOAD_DESTINATION_PREFIX}" ]]; then upload_destination=${UPLOAD_DESTINATION_PREFIX}/${video_file_name} - echo "Add to pipe a signal Uploading video to $upload_destination" + echo "$(date +%FT%T%Z) [${process_name}] - Add to pipe a signal Uploading video to $upload_destination" echo $video_file ${UPLOAD_DESTINATION_PREFIX} >> ${UPLOAD_PIPE_FILE} & elif [[ "${VIDEO_UPLOAD_ENABLED}" != "false" ]] && [[ -z "${UPLOAD_DESTINATION_PREFIX}" ]]; then - echo Upload destination not known since UPLOAD_DESTINATION_PREFIX is not set. Continue without uploading. + echo "$(date +%FT%T%Z) [${process_name}] - Upload destination not known since UPLOAD_DESTINATION_PREFIX is not set. Continue without uploading." fi } @@ -105,25 +106,27 @@ function graceful_exit() { check_if_recording_inprogress send_exit_signal_to_uploader wait_util_uploader_shutdown + rm -rf ${UPLOAD_PIPE_FILE} || true kill -INT "$(cat /var/run/supervisor/supervisord.pid)" } if [[ "${VIDEO_UPLOAD_ENABLED}" != "true" ]] && [[ "${VIDEO_FILE_NAME}" != "auto" ]] && [[ -n "${VIDEO_FILE_NAME}" ]]; then return_code=1 attempts=0 - echo 'Checking if the display is open...' + echo "$(date +%FT%T%Z) [${process_name}] - Checking if the display is open..." until [[ $return_code -eq 0 ]] || [[ $attempts -eq $max_attempts ]]; do xset -display ${DISPLAY_CONTAINER_NAME}:${DISPLAY_NUM} b off > /dev/null 2>&1 return_code=$? if [[ $return_code -ne 0 ]]; then - echo 'Waiting before next display check...' + echo "$(date +%FT%T%Z) [${process_name}] - Waiting before next display check..." sleep 0.5 fi attempts=$((attempts+1)) done # exec replaces the video.sh process with ffmpeg, this makes easier to pass the process termination signal - exec ffmpeg -y -f x11grab -video_size ${VIDEO_SIZE} -r ${FRAME_RATE} -i ${DISPLAY_CONTAINER_NAME}:${DISPLAY_NUM}.0 -codec:v ${CODEC} ${PRESET} -pix_fmt yuv420p "$VIDEO_FOLDER/$VIDEO_FILE_NAME" + exec ffmpeg -hide_banner -loglevel warning -flags low_delay -threads 1 -fflags nobuffer+genpts -strict experimental -y -f x11grab \ + -video_size ${VIDEO_SIZE} -r ${FRAME_RATE} -i ${DISPLAY_CONTAINER_NAME}:${DISPLAY_NUM}.0 -codec:v ${CODEC} ${PRESET} -pix_fmt yuv420p "$VIDEO_FOLDER/$VIDEO_FILE_NAME" else create_pipe @@ -132,16 +135,16 @@ else attempts=0 - echo Checking if the display is open + echo "$(date +%FT%T%Z) [${process_name}] - Checking if the display is open" until xset b off || [[ $attempts = "$max_attempts" ]] do - echo Waiting before next display check + echo "$(date +%FT%T%Z) [${process_name}] - Waiting before next display check" sleep 0.5 attempts=$((attempts+1)) done if [[ $attempts = "$max_attempts" ]] then - echo Can not open display, exiting. + echo "$(date +%FT%T%Z) [${process_name}] - Can not open display, exiting." exit fi @@ -155,19 +158,19 @@ else max_recorded_count=${SE_DRAIN_AFTER_SESSION_COUNT:-0} recorded_count=0 - echo Checking if node API responds + echo "$(date +%FT%T%Z) [${process_name}] - Checking if node API responds" until curl -sk --request GET ${SE_SERVER_PROTOCOL}://${DISPLAY_CONTAINER_NAME}:${SE_NODE_PORT}/status || [[ $attempts = "$max_attempts" ]] do if [ $(($attempts % 60)) -eq 0 ]; then - echo Waiting before next API check + echo "$(date +%FT%T%Z) [${process_name}] - Waiting before next API check" fi sleep 0.5 attempts=$((attempts+1)) done if [[ $attempts = "$max_attempts" ]] then - echo Can not reach node API, exiting. + echo "$(date +%FT%T%Z) [${process_name}] - Can not reach node API, exiting." exit fi while curl -sk --request GET ${SE_SERVER_PROTOCOL}://${DISPLAY_CONTAINER_NAME}:${SE_NODE_PORT}/status > /tmp/status.json @@ -175,38 +178,41 @@ else session_id=$(jq -r '.[]?.node?.slots | .[0]?.session?.sessionId' /tmp/status.json) if [[ "$session_id" != "null" && "$session_id" != "" && "$recording_started" = "false" ]] then - echo "Session: $session_id is created" - caps_se_video_record=$(bash ${VIDEO_CONFIG_DIRECTORY}/video_graphQLQuery.sh "$session_id") + echo "$(date +%FT%T%Z) [${process_name}] - Session: $session_id is created" + return_list=($(bash ${VIDEO_CONFIG_DIRECTORY}/video_graphQLQuery.sh "$session_id")) + caps_se_video_record=${return_list[0]} + video_file_name="${return_list[1]}.mp4" + echo "$(date +%FT%T%Z) [${process_name}] - Start recording: $caps_se_video_record, video file name: $video_file_name" if [[ -f "/tmp/graphQL_$session_id.json" ]]; then - cat "/tmp/graphQL_$session_id.json"; echo + jq '.' "/tmp/graphQL_$session_id.json"; fi - echo "se:recordVideo value is: $caps_se_video_record" fi if [[ "$session_id" != "null" && "$session_id" != "" && "$recording_started" = "false" && "$caps_se_video_record" = "true" ]] then - video_file_name="$session_id.mp4" - video_file="${SE_VIDEO_FOLDER}/$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 & + video_file="${VIDEO_FOLDER}/$video_file_name" + echo "$(date +%FT%T%Z) [${process_name}] - Starting to record video" + ffmpeg -hide_banner -loglevel warning -flags low_delay -threads 1 -fflags nobuffer+genpts -strict experimental -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" + echo "$(date +%FT%T%Z) [${process_name}] - Video recording started" + sleep 2 elif [[ "$session_id" != "$prev_session_id" && "$recording_started" = "true" ]] then stop_recording if [[ $max_recorded_count -gt 0 ]] && [[ $recorded_count -ge $max_recorded_count ]]; then - echo "Node will be drained since max sessions reached count number ($max_recorded_count)" + echo "$(date +%FT%T%Z) [${process_name}] - Node will be drained since max sessions reached count number ($max_recorded_count)" exit fi elif [[ $recording_started = "true" ]] then - echo "Video recording in progress " + echo "$(date +%FT%T%Z) [${process_name}] - Video recording in progress " sleep 1 else sleep 1 fi prev_session_id=$session_id done - echo "Node API is not responding, exiting." + echo "$(date +%FT%T%Z) [${process_name}] - Node API is not responding, exiting." exit fi diff --git a/Video/video_graphQLQuery.sh b/Video/video_graphQLQuery.sh index 54ed011d19..d8d7a5e91c 100755 --- a/Video/video_graphQLQuery.sh +++ b/Video/video_graphQLQuery.sh @@ -4,6 +4,9 @@ SESSION_ID=$1 GRAPHQL_ENDPOINT=${2:-$SE_NODE_GRID_GRAPHQL_URL} VIDEO_CAP_NAME=${VIDEO_CAP_NAME:-"se:recordVideo"} +TEST_NAME_CAP=${TEST_NAME_CAP:-"se:name"} +VIDEO_FILE_NAME_TRIM=${SE_VIDEO_FILE_NAME_TRIM_REGEX:-"[:alnum:]-_"} +VIDEO_FILE_NAME_SUFFIX=${SE_VIDEO_FILE_NAME_SUFFIX:-"true"} if [ -z "${GRAPHQL_ENDPOINT}" ] && [ -n "${SE_NODE_GRID_URL}" ]; then GRAPHQL_ENDPOINT="${SE_NODE_GRID_URL}/graphql" @@ -17,10 +20,23 @@ if [ -n "${GRAPHQL_ENDPOINT}" ]; then -s "${GRAPHQL_ENDPOINT}" > /tmp/graphQL_${SESSION_ID}.json RECORD_VIDEO=$(jq -r '.data.session.capabilities | fromjson | ."'${VIDEO_CAP_NAME}'"' /tmp/graphQL_${SESSION_ID}.json) + TEST_NAME=$(jq -r '.data.session.capabilities | fromjson | ."'${TEST_NAME_CAP}'"' /tmp/graphQL_${SESSION_ID}.json) fi if [ "${RECORD_VIDEO,,}" = "false" ]; then - echo "${RECORD_VIDEO,,}" + RECORD_VIDEO=false else - echo true + RECORD_VIDEO=true fi + +if [ "${TEST_NAME}" != "null" ] && [ -n "${TEST_NAME}" ]; then + if [ "${VIDEO_FILE_NAME_SUFFIX,,}" = "true" ]; then + TEST_NAME="${TEST_NAME}_${SESSION_ID}" + fi + TEST_NAME="$(echo "${TEST_NAME}" | tr ' ' '_' | tr -dc "${VIDEO_FILE_NAME_TRIM}" | cut -c 1-251)" +else + TEST_NAME="${SESSION_ID}" +fi + +return_array=("${RECORD_VIDEO}" "${TEST_NAME}") +echo "${return_array[@]}" diff --git a/charts/selenium-grid/README.md b/charts/selenium-grid/README.md index 9a579b8b5e..12334fc814 100644 --- a/charts/selenium-grid/README.md +++ b/charts/selenium-grid/README.md @@ -479,7 +479,7 @@ options.set_capability('se:recordVideo', False) driver = webdriver.Remote(options=options, command_executor="http://localhost:4444") ``` -In Node will perform query GraphQL in Hub based on Node SessionId and extract the value of `se:recordVideo` in capabilities before deciding to start video recording process or not. You can customize by reading on section [Configuration extra scripts mount to container](#configuration-extra-scripts-mount-to-container). +In recorder container will perform query GraphQL in Hub based on Node SessionId and extract the value of `se:recordVideo` in capabilities before deciding to start video recording process or not. You can customize by reading on section [Configuration extra scripts mount to container](#configuration-extra-scripts-mount-to-container). #### Video uploader diff --git a/charts/selenium-grid/templates/_helpers.tpl b/charts/selenium-grid/templates/_helpers.tpl index 6b2cd9c634..0df4c7e59f 100644 --- a/charts/selenium-grid/templates/_helpers.tpl +++ b/charts/selenium-grid/templates/_helpers.tpl @@ -382,7 +382,7 @@ template: name: {{ template "seleniumGrid.recorder.configmap.fullname" $ }} - configMapRef: name: {{ template "seleniumGrid.server.configmap.fullname" $ }} - {{- if and $.Values.videoRecorder.uploader.enabled (not $.Values.videoRecorder.uploader.name) }} + {{- if and $.Values.videoRecorder.uploader.enabled (not (empty $.Values.videoRecorder.uploader.name)) }} - secretRef: name: {{ tpl (default (include "seleniumGrid.common.secrets.fullname" $) $.Values.uploaderConfigMap.secretVolumeMountName) $ }} {{- end }} @@ -647,7 +647,7 @@ Define terminationGracePeriodSeconds of the node pod. mountPath: {{ $.Values.recorderConfigMap.extraScriptsDirectory }}/{{ $fileName }} subPath: {{ $fileName }} {{- end }} -{{- if and .Values.videoRecorder.uploader.enabled (not .Values.videoRecorder.uploader.name) }} +{{- if and $.Values.videoRecorder.uploader.enabled (not (empty $.Values.videoRecorder.uploader.name)) }} {{- range $fileName, $value := .Values.uploaderConfigMap.secretFiles }} - name: {{ tpl (default (include "seleniumGrid.common.secrets.fullname" $) $.Values.uploaderConfigMap.secretVolumeMountName) $ }} mountPath: {{ $.Values.uploaderConfigMap.extraScriptsDirectory }}/{{ $fileName }} diff --git a/charts/selenium-grid/templates/recorder-configmap.yaml b/charts/selenium-grid/templates/recorder-configmap.yaml index b203a22d93..d2b2dbe51f 100644 --- a/charts/selenium-grid/templates/recorder-configmap.yaml +++ b/charts/selenium-grid/templates/recorder-configmap.yaml @@ -13,10 +13,10 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} data: - SE_VIDEO_UPLOAD_ENABLED: '{{ .Values.videoRecorder.uploader.enabled }}' + SE_VIDEO_UPLOAD_ENABLED: '{{ default "false" .Values.videoRecorder.uploader.enabled }}' SE_VIDEO_FILE_NAME: 'auto' {{- if and .Values.videoRecorder.uploader.enabled (empty .Values.videoRecorder.uploader.name) }} - SE_VIDEO_INTERNAL_UPLOAD: 'true' + SE_VIDEO_INTERNAL_UPLOAD: '{{ .Values.videoRecorder.uploader.enabled }}' {{- end }} VIDEO_CONFIG_DIRECTORY: '{{ .Values.recorderConfigMap.extraScriptsDirectory }}' SE_UPLOAD_DESTINATION_PREFIX: '{{ .Values.videoRecorder.uploader.destinationPrefix }}' diff --git a/docker-compose-v3-video-upload.yml b/docker-compose-v3-video-upload.yml index 373f9bdf49..af97c683d9 100644 --- a/docker-compose-v3-video-upload.yml +++ b/docker-compose-v3-video-upload.yml @@ -40,6 +40,7 @@ services: - chrome environment: - DISPLAY_CONTAINER_NAME=chrome + - SE_NODE_GRID_URL=http://selenium-hub:4444 - SE_VIDEO_FILE_NAME=auto - SE_VIDEO_UPLOAD_ENABLED=true - SE_VIDEO_INTERNAL_UPLOAD=true @@ -61,6 +62,7 @@ services: - edge environment: - DISPLAY_CONTAINER_NAME=edge + - SE_NODE_GRID_URL=http://selenium-hub:4444 - SE_VIDEO_FILE_NAME=auto - SE_VIDEO_UPLOAD_ENABLED=true - SE_VIDEO_INTERNAL_UPLOAD=true @@ -82,6 +84,7 @@ services: - firefox environment: - DISPLAY_CONTAINER_NAME=firefox + - SE_NODE_GRID_URL=http://selenium-hub:4444 - SE_VIDEO_FILE_NAME=auto - SE_VIDEO_UPLOAD_ENABLED=true - SE_VIDEO_INTERNAL_UPLOAD=true diff --git a/docker-compose-v3-video.yml b/docker-compose-v3-video.yml index 926955d7c2..36b028fed1 100644 --- a/docker-compose-v3-video.yml +++ b/docker-compose-v3-video.yml @@ -41,7 +41,9 @@ services: - chrome environment: - DISPLAY_CONTAINER_NAME=chrome - - FILE_NAME=chrome_video.mp4 + - SE_NODE_GRID_URL=http://selenium-hub:4444 +# - FILE_NAME=chrome_video.mp4 + - SE_VIDEO_FILE_NAME=auto edge_video: image: selenium/video:ffmpeg-6.1.1-20240425 @@ -51,7 +53,9 @@ services: - edge environment: - DISPLAY_CONTAINER_NAME=edge - - FILE_NAME=edge_video.mp4 + - SE_NODE_GRID_URL=http://selenium-hub:4444 +# - FILE_NAME=edge_video.mp4 + - SE_VIDEO_FILE_NAME=auto firefox_video: image: selenium/video:ffmpeg-6.1.1-20240425 @@ -61,7 +65,9 @@ services: - firefox environment: - DISPLAY_CONTAINER_NAME=firefox - - FILE_NAME=firefox_video.mp4 + - SE_NODE_GRID_URL=http://selenium-hub:4444 +# - FILE_NAME=firefox_video.mp4 + - SE_VIDEO_FILE_NAME=auto selenium-hub: image: selenium/hub:4.20.0-20240425 diff --git a/tests/SeleniumTests/__init__.py b/tests/SeleniumTests/__init__.py index e55ab6da71..ee77aa49bd 100644 --- a/tests/SeleniumTests/__init__.py +++ b/tests/SeleniumTests/__init__.py @@ -20,6 +20,7 @@ SELENIUM_ENABLE_MANAGED_DOWNLOADS = os.environ.get('SELENIUM_ENABLE_MANAGED_DOWNLOADS', 'true').lower() == 'true' WEB_DRIVER_WAIT_TIMEOUT = int(os.environ.get('WEB_DRIVER_WAIT_TIMEOUT', 60)) TEST_PARALLEL_HARDENING = os.environ.get('TEST_PARALLEL_HARDENING', 'false').lower() == 'true' +TEST_DELAY_AFTER_TEST = int(os.environ.get('TEST_DELAY_AFTER_TEST', 0)) if SELENIUM_GRID_USERNAME and SELENIUM_GRID_PASSWORD: SELENIUM_GRID_HOST = f"{SELENIUM_GRID_USERNAME}:{SELENIUM_GRID_PASSWORD}@{SELENIUM_GRID_HOST}" @@ -105,6 +106,8 @@ def test_download_file(self): def tearDown(self): try: + if TEST_DELAY_AFTER_TEST: + time.sleep(TEST_DELAY_AFTER_TEST) self.driver.quit() except Exception as e: print(f"::error::Exception: {str(e)}") @@ -119,6 +122,8 @@ def setUp(self): options.enable_downloads = SELENIUM_ENABLE_MANAGED_DOWNLOADS options.add_argument('disable-features=DownloadBubble,DownloadBubbleV2') options.set_capability('se:recordVideo', True) + options.set_capability('se:name', f"{self._testMethodName} ({self.__class__.__name__})") + options.set_capability('se:screenResolution', '1920x1080') if SELENIUM_GRID_TEST_HEADLESS: options.add_argument('--headless=new') start_time = time.time() @@ -127,7 +132,7 @@ def setUp(self): command_executor="%s://%s:%s" % (SELENIUM_GRID_PROTOCOL,SELENIUM_GRID_HOST,SELENIUM_GRID_PORT) ) end_time = time.time() - print(f"<< {self._testMethodName} ({self.__class__.__name__}) WebDriver initialization completed in {end_time - start_time} (s)") + print(f"Begin: {self._testMethodName} ({self.__class__.__name__}) WebDriver initialization completed in {end_time - start_time} (s)") except Exception as e: print(f"::error::Exception: {str(e)}") print(traceback.format_exc()) @@ -140,6 +145,8 @@ def setUp(self): options.enable_downloads = SELENIUM_ENABLE_MANAGED_DOWNLOADS options.add_argument('disable-features=DownloadBubble,DownloadBubbleV2') options.set_capability('se:recordVideo', True) + options.set_capability('se:name', f"{self._testMethodName} ({self.__class__.__name__})") + options.set_capability('se:screenResolution', '1920x1080') if SELENIUM_GRID_TEST_HEADLESS: options.add_argument('--headless=new') start_time = time.time() @@ -148,7 +155,7 @@ def setUp(self): command_executor="%s://%s:%s" % (SELENIUM_GRID_PROTOCOL,SELENIUM_GRID_HOST,SELENIUM_GRID_PORT) ) end_time = time.time() - print(f"<< {self._testMethodName} ({self.__class__.__name__}) WebDriver initialization completed in {end_time - start_time} (s)") + print(f"Begin: {self._testMethodName} ({self.__class__.__name__}) WebDriver initialization completed in {end_time - start_time} (s)") except Exception as e: print(f"::error::Exception: {str(e)}") print(traceback.format_exc()) @@ -164,6 +171,8 @@ def setUp(self): options.profile = profile options.enable_downloads = SELENIUM_ENABLE_MANAGED_DOWNLOADS options.set_capability('se:recordVideo', True) + options.set_capability('se:name', f"{self._testMethodName} ({self.__class__.__name__})") + options.set_capability('se:screenResolution', '1920x1080') if SELENIUM_GRID_TEST_HEADLESS: options.add_argument('-headless') start_time = time.time() @@ -172,7 +181,7 @@ def setUp(self): command_executor="%s://%s:%s" % (SELENIUM_GRID_PROTOCOL,SELENIUM_GRID_HOST,SELENIUM_GRID_PORT) ) end_time = time.time() - print(f"<< {self._testMethodName} ({self.__class__.__name__}) WebDriver initialization completed in {end_time - start_time} (s)") + print(f"Begin: {self._testMethodName} ({self.__class__.__name__}) WebDriver initialization completed in {end_time - start_time} (s)") except Exception as e: print(f"::error::Exception: {str(e)}") print(traceback.format_exc()) @@ -200,7 +209,7 @@ def run(self, test_classes): for future, test in zip(concurrent.futures.as_completed(futures), tests): try: completion_time = time.time() - start_times[test] - print(f">> {str(test)} completed in {str(completion_time)} (s)") + print(f"Finish: {str(test)} completed in {str(completion_time)} (s)") if not future.result().wasSuccessful(): raise Exception except Exception as e: diff --git a/tests/charts/ci/NoAutoscaling-values.yaml b/tests/charts/ci/NoAutoscaling-values.yaml new file mode 100644 index 0000000000..f65479a24e --- /dev/null +++ b/tests/charts/ci/NoAutoscaling-values.yaml @@ -0,0 +1,38 @@ +# This is used in Helm chart testing +# Configuration for chrome nodes +chromeNode: + port: 6666 + nameOverride: my-chrome-name + extraEnvironmentVariables: &extraEnvironmentVariables + - name: SE_NODE_ENABLE_MANAGED_DOWNLOADS + value: "true" + - name: SE_VNC_NO_PASSWORD + value: "true" + - name: SE_SCREEN_WIDTH + value: "1920" + - name: SE_SCREEN_HEIGHT + value: "1080" + - name: TZ + value: "Asia/Saigon" + readinessProbe: + enabled: &readinessProbe false + livenessProbe: + enabled: &livenessProbe true +# Configuration for edge nodes +edgeNode: + port: 8888 + nameOverride: my-edge-name + extraEnvironmentVariables: *extraEnvironmentVariables + readinessProbe: + enabled: *readinessProbe + livenessProbe: + enabled: *livenessProbe +# Configuration for firefox nodes +firefoxNode: + port: 7777 + nameOverride: my-firefox-name + extraEnvironmentVariables: *extraEnvironmentVariables + readinessProbe: + enabled: *readinessProbe + livenessProbe: + enabled: *livenessProbe diff --git a/tests/charts/ci/NodeChrome-values.yaml b/tests/charts/ci/NodeChrome-values.yaml deleted file mode 100644 index 5a612e0e6d..0000000000 --- a/tests/charts/ci/NodeChrome-values.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# This is used in Helm chart testing -# Configuration for chrome nodes -chromeNode: - port: 6666 - nameOverride: my-chrome-name - extraEnvironmentVariables: - - name: SE_NODE_ENABLE_MANAGED_DOWNLOADS - value: "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 deleted file mode 100644 index 3fdc12a6d6..0000000000 --- a/tests/charts/ci/NodeEdge-values.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# This is used in Helm chart testing -# Configuration for chrome nodes -chromeNode: - enabled: false -# Configuration for edge nodes -edgeNode: - port: 8888 - nameOverride: my-edge-name - extraEnvironmentVariables: - - name: SE_NODE_ENABLE_MANAGED_DOWNLOADS - value: "true" -# Configuration for firefox nodes -firefoxNode: - enabled: false diff --git a/tests/charts/ci/NodeFirefox-values.yaml b/tests/charts/ci/NodeFirefox-values.yaml deleted file mode 100644 index 574ce19e3f..0000000000 --- a/tests/charts/ci/NodeFirefox-values.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# This is used in Helm chart testing -# Configuration for chrome nodes -chromeNode: - enabled: false -# Configuration for edge nodes -edgeNode: - enabled: false -# Configuration for firefox nodes -firefoxNode: - port: 7777 - nameOverride: my-firefox-name - extraEnvironmentVariables: - - name: SE_NODE_ENABLE_MANAGED_DOWNLOADS - value: "true" diff --git a/tests/charts/ci/base-recorder-values.yaml b/tests/charts/ci/base-recorder-values.yaml index 97ad3b6b37..9ffa5d657b 100644 --- a/tests/charts/ci/base-recorder-values.yaml +++ b/tests/charts/ci/base-recorder-values.yaml @@ -12,6 +12,10 @@ videoRecorder: enabled: true + extraVolumes: + - name: videos + persistentVolumeClaim: + claimName: ${TEST_PV_CLAIM_NAME} uploader: enabled: "${UPLOAD_ENABLED}" destinationPrefix: "gs://${BUCKET_NAME}" diff --git a/tests/charts/ci/local-pvc.yaml b/tests/charts/ci/local-pvc.yaml new file mode 100644 index 0000000000..b506e9647d --- /dev/null +++ b/tests/charts/ci/local-pvc.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: pv-local +spec: + accessModes: + - "ReadWriteOnce" + capacity: + storage: "2Gi" + claimRef: + name: ${TEST_PV_CLAIM_NAME} + namespace: ${SELENIUM_NAMESPACE} + hostPath: + path: "${HOST_PATH}" + type: DirectoryOrCreate + persistentVolumeReclaimPolicy: Retain + storageClassName: "standard" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: ${TEST_PV_CLAIM_NAME} + namespace: ${SELENIUM_NAMESPACE} +spec: + accessModes: + - "ReadWriteOnce" + resources: + requests: + storage: "2Gi" + storageClassName: "standard" diff --git a/tests/charts/make/chart_test.sh b/tests/charts/make/chart_test.sh index c9e9be8e49..59cf0c999e 100755 --- a/tests/charts/make/chart_test.sh +++ b/tests/charts/make/chart_test.sh @@ -15,7 +15,7 @@ SELENIUM_GRID_PROTOCOL=${SELENIUM_GRID_PROTOCOL:-"http"} 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=${SELENIUM_GRID_AUTOSCALING:-"true"} SELENIUM_GRID_AUTOSCALING_MIN_REPLICA=${SELENIUM_GRID_AUTOSCALING_MIN_REPLICA:-"0"} WAIT_TIMEOUT=${WAIT_TIMEOUT:-"90s"} HUB_CHECKS_INTERVAL=${HUB_CHECKS_INTERVAL:-45} @@ -36,6 +36,8 @@ BASIC_AUTH_PASSWORD=${BASIC_AUTH_PASSWORD:-"myStrongPassword"} LOG_LEVEL=${LOG_LEVEL:-"INFO"} TEST_EXISTING_KEDA=${TEST_EXISTING_KEDA:-"true"} TEST_UPGRADE_CHART=${TEST_UPGRADE_CHART:-"false"} +TEST_PV_CLAIM_NAME=${TEST_PV_CLAIM_NAME:-"selenium-grid-pvc-local"} +LIMIT_RESOURCES=${LIMIT_RESOURCES:-"true"} cleanup() { # Get the list of pods @@ -43,7 +45,7 @@ cleanup() { # Iterate over the pods and print their logs for pod in $pods; do echo "Logs for pod $pod" - kubectl logs -n ${SELENIUM_NAMESPACE} $pod > tests/tests/pod_logs_${pod}.txt + kubectl logs -n ${SELENIUM_NAMESPACE} $pod --all-containers > tests/tests/pod_logs_${pod}.txt done if [ "${SKIP_CLEANUP}" = "false" ]; then echo "Clean up chart release and namespace" @@ -77,6 +79,9 @@ else export UPLOAD_ENABLED=false fi export RELEASE_NAME=${RELEASE_NAME} +export SELENIUM_NAMESPACE=${SELENIUM_NAMESPACE} +export TEST_PV_CLAIM_NAME=${TEST_PV_CLAIM_NAME} +export HOST_PATH=$(realpath ./tests/videos) if [ "${RELEASE_NAME}" = "selenium" ]; then export SELENIUM_TLS_SECRET_NAME="selenium-tls-secret" else @@ -86,6 +91,17 @@ RECORDER_VALUES_FILE=${TEST_VALUES_PATH}/base-recorder-values.yaml envsubst < ${RECORDER_VALUES_FILE} > ./tests/tests/base-recorder-values.yaml RECORDER_VALUES_FILE=./tests/tests/base-recorder-values.yaml +if [ "${TEST_UPGRADE_CHART}" = "false" ]; then + LOCAL_PVC_YAML="${TEST_VALUES_PATH}/local-pvc.yaml" + envsubst < ${LOCAL_PVC_YAML} > ./tests/tests/local-pvc.yaml + LOCAL_PVC_YAML=./tests/tests/local-pvc.yaml + sudo rm -rf ${HOST_PATH}; sudo mkdir -p ${HOST_PATH} + sudo chmod -R 777 ${HOST_PATH} + kubectl create ns ${SELENIUM_NAMESPACE} || true + kubectl apply -n ${SELENIUM_NAMESPACE} -f ${LOCAL_PVC_YAML} + kubectl describe pv,pvc -n ${SELENIUM_NAMESPACE} +fi + HELM_COMMAND_SET_IMAGES=" \ --set global.seleniumGrid.imageRegistry=${NAMESPACE} \ --set global.seleniumGrid.imageTag=${VERSION} \ @@ -102,7 +118,7 @@ if [ "${SELENIUM_GRID_AUTOSCALING}" = "true" ] && [ "${TEST_EXISTING_KEDA}" = "t --set autoscaling.enabled=false \ --set autoscaling.enableWithExistingKEDA=true \ " -elif [ "${SELENIUM_GRID_AUTOSCALING}" = "true" ] && [ "${TEST_EXISTING_KEDA}" = "true" ]; then +elif [ "${SELENIUM_GRID_AUTOSCALING}" = "true" ] && [ "${TEST_EXISTING_KEDA}" = "false" ]; then HELM_COMMAND_SET_IMAGES="${HELM_COMMAND_SET_IMAGES} \ --set autoscaling.enabled=true \ --set autoscaling.enableWithExistingKEDA=false \ @@ -158,9 +174,14 @@ fi HELM_COMMAND_SET_BASE_VALUES=" \ --values ${TEST_VALUES_PATH}/base-auth-ingress-values.yaml \ --values ${RECORDER_VALUES_FILE} \ ---values ${TEST_VALUES_PATH}/base-resources-values.yaml \ " +if [ "${LIMIT_RESOURCES}" = "true" ]; then + HELM_COMMAND_SET_BASE_VALUES="${HELM_COMMAND_SET_BASE_VALUES} \ + --values ${TEST_VALUES_PATH}/base-resources-values.yaml \ + " +fi + if [ "${SUB_PATH}" = "/selenium" ]; then HELM_COMMAND_SET_BASE_VALUES="${HELM_COMMAND_SET_BASE_VALUES} \ --values ${TEST_VALUES_PATH}/base-subPath-values.yaml \ @@ -212,7 +233,14 @@ export HUB_CHECKS_INTERVAL=${HUB_CHECKS_INTERVAL} export HUB_CHECKS_MAX_ATTEMPTS=${HUB_CHECKS_MAX_ATTEMPTS} export WEB_DRIVER_WAIT_TIMEOUT=${WEB_DRIVER_WAIT_TIMEOUT} export SELENIUM_GRID_TEST_HEADLESS=${SELENIUM_GRID_TEST_HEADLESS:-"false"} -./tests/bootstrap.sh ${MATRIX_BROWSER} +export TEST_DELAY_AFTER_TEST=${TEST_DELAY_AFTER_TEST:-"30"} +if [ "${MATRIX_BROWSER}" = "NoAutoscaling" ]; then + ./tests/bootstrap.sh NodeChrome + ./tests/bootstrap.sh NodeFirefox + ./tests/bootstrap.sh NodeEdge +else + ./tests/bootstrap.sh ${MATRIX_BROWSER} +fi echo "Get pods status" kubectl get pods -n ${SELENIUM_NAMESPACE} diff --git a/tests/docker-compose-v3-test-node-docker.yaml b/tests/docker-compose-v3-test-node-docker.yaml index 6728e44393..baa69a8f04 100644 --- a/tests/docker-compose-v3-test-node-docker.yaml +++ b/tests/docker-compose-v3-test-node-docker.yaml @@ -2,7 +2,6 @@ version: "3" services: node-docker: image: ${NAMESPACE}/node-docker:${TAG} - user: root volumes: - ./videos:/opt/selenium/assets - ./videos/config.toml:/opt/bin/config.toml @@ -47,4 +46,5 @@ services: - SELENIUM_GRID_HOST=selenium-hub - BINDING_VERSION=${BINDING_VERSION} - SELENIUM_ENABLE_MANAGED_DOWNLOADS=${SELENIUM_ENABLE_MANAGED_DOWNLOADS} + - TEST_DELAY_AFTER_TEST=${TEST_DELAY_AFTER_TEST} command: ["./bootstrap.sh", "${NODE}"] diff --git a/tests/docker-compose-v3-test-video.yml b/tests/docker-compose-v3-test-video.yml index ade2a36283..e842913816 100644 --- a/tests/docker-compose-v3-test-video.yml +++ b/tests/docker-compose-v3-test-video.yml @@ -25,8 +25,10 @@ services: depends_on: - browser environment: + - SE_NODE_GRID_URL=http://selenium-hub:4444 - DISPLAY_CONTAINER_NAME=browser - - FILE_NAME=${VIDEO_FILE_NAME} + - SE_VIDEO_FILE_NAME=${VIDEO_FILE_NAME} + - SE_VIDEO_FILE_NAME_SUFFIX=${VIDEO_FILE_NAME_SUFFIX} selenium-hub: image: selenium/hub:${TAG} @@ -48,4 +50,5 @@ services: - RUN_IN_DOCKER_COMPOSE=true - SELENIUM_GRID_HOST=selenium-hub - BINDING_VERSION=${BINDING_VERSION} + - TEST_DELAY_AFTER_TEST=${TEST_DELAY_AFTER_TEST} command: ["./bootstrap.sh", "${NODE}"]