Skip to content

Commit d4ab85a

Browse files
committed
feat: add docker-compose video upload
Signed-off-by: Viet Nguyen Duc <[email protected]>
1 parent e250a80 commit d4ab85a

File tree

18 files changed

+294
-157
lines changed

18 files changed

+294
-157
lines changed

.github/workflows/helm-chart-test.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ jobs:
5252
echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
5353
echo "IMAGE_REGISTRY=artifactory/selenium" >> $GITHUB_ENV
5454
- name: Setup Kubernetes environment
55-
run: make chart_setup_env
55+
uses: nick-invision/retry@master
56+
with:
57+
timeout_minutes: 10
58+
max_attempts: 3
59+
command: make chart_setup_env
5660
- name: Build Helm charts
5761
run: |
5862
BUILD_DATE=${BUILD_DATE} make chart_build

Makefile

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ MAJOR_MINOR_PATCH := $(word 1,$(subst -, ,$(TAG_VERSION)))
1919
FFMPEG_TAG_VERSION := $(or $(FFMPEG_TAG_VERSION),$(FFMPEG_TAG_VERSION),ffmpeg-6.1)
2020
FFMPEG_BASED_NAME := $(or $(FFMPEG_BASED_NAME),$(FFMPEG_BASED_NAME),ndviet)
2121
FFMPEG_BASED_TAG := $(or $(FFMPEG_BASED_TAG),$(FFMPEG_BASED_TAG),6.1-ubuntu2204)
22-
RCLONE_BASED_TAG := $(or $(RCLONE_BASED_TAG),$(RCLONE_BASED_TAG),1.65)
23-
RCLONE_TAG_VERSION := $(or $(RCLONE_TAG_VERSION),$(RCLONE_TAG_VERSION),rclone-1.65)
2422

2523
all: hub \
2624
distributor \
@@ -434,29 +432,29 @@ chart_test_template:
434432
./tests/charts/bootstrap.sh
435433

436434
chart_test_chrome:
437-
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) UPLOADER_TAG=$(RCLONE_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
435+
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
438436
./tests/charts/make/chart_test.sh NodeChrome
439437

440438
chart_test_firefox:
441-
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) UPLOADER_TAG=$(RCLONE_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
439+
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
442440
./tests/charts/make/chart_test.sh NodeFirefox
443441

444442
chart_test_edge:
445-
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) UPLOADER_TAG=$(RCLONE_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
443+
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
446444
./tests/charts/make/chart_test.sh NodeEdge
447445

448446
chart_test_autoscaling_deployment_https:
449447
SELENIUM_GRID_TEST_HEADLESS=true SELENIUM_GRID_PROTOCOL=https SELENIUM_GRID_PORT=443 make chart_test_autoscaling_deployment
450448

451449
chart_test_autoscaling_deployment:
452-
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) UPLOADER_TAG=$(RCLONE_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
450+
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
453451
./tests/charts/make/chart_test.sh DeploymentAutoscaling
454452

455453
chart_test_autoscaling_job_https:
456454
SELENIUM_GRID_TEST_HEADLESS=true SELENIUM_GRID_PROTOCOL=https SELENIUM_GRID_PORT=443 make chart_test_autoscaling_job
457455

458456
chart_test_autoscaling_job:
459-
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) UPLOADER_TAG=$(RCLONE_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
457+
VERSION=$(TAG_VERSION) VIDEO_TAG=$(FFMPEG_TAG_VERSION)-$(BUILD_DATE) NAMESPACE=$(NAMESPACE) \
460458
./tests/charts/make/chart_test.sh JobAutoscaling
461459

462460
.PHONY: \

README.md

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,45 +21,59 @@ Talk to us at https://www.selenium.dev/support/
2121

2222
## Contents
2323

24+
<!-- TOC -->
25+
* [Community](#community)
26+
* [Contents](#contents)
2427
* [Quick start](#quick-start)
28+
* [Try them out in a ready-to-use GitPod environment!](#try-them-out-in-a-ready-to-use-gitpod-environment)
2529
* [Experimental Mult-Arch aarch64/armhf/amd64 Images](#experimental-mult-arch-aarch64armhfamd64-images)
2630
* [Nightly Images](#nightly-images)
2731
* [Dev and Beta Channel Browser Images](#dev-and-beta-channel-browser-images)
28-
+ [Dev and Beta Standalone Mode](#dev-and-beta-standalone-mode)
29-
+ [Dev and Beta on the Grid](#dev-and-beta-on-the-grid)
32+
* [Dev and Beta Standalone Mode](#dev-and-beta-standalone-mode)
33+
* [Dev and Beta on the Grid](#dev-and-beta-on-the-grid)
3034
* [Execution modes](#execution-modes)
31-
+ [Standalone](#standalone)
32-
+ [Hub and Nodes](#hub-and-nodes)
33-
- [Docker networking](#docker-networking)
34-
- [Using different machines/VMs](#using-different-machinesvms)
35-
- [Docker Compose](#docker-compose)
36-
+ [Fully distributed mode - Router, Queue, Distributor, EventBus, SessionMap and Nodes](#fully-distributed-mode---router-queue-distributor-eventbus-sessionmap-and-nodes)
35+
* [Standalone](#standalone)
36+
* [Hub and Nodes](#hub-and-nodes)
37+
* [Fully distributed mode - Router, Queue, Distributor, EventBus, SessionMap and Nodes](#fully-distributed-mode---router-queue-distributor-eventbus-sessionmap-and-nodes)
3738
* [Video recording](#video-recording)
39+
* [Video recording and uploading](#video-recording-and-uploading)
3840
* [Dynamic Grid](#dynamic-grid)
39-
+ [Configuration example](#configuration-example)
40-
+ [Execution with Hub & Node roles](#execution-with-hub--node-roles)
41-
+ [Execution with Standalone roles](#execution-with-standalone-roles)
42-
+ [Using Dynamic Grid in different machines/VMs](#using-dynamic-grid-in-different-machinesvms)
43-
+ [Execution with Docker Compose](#execution-with-docker-compose)
44-
+ [Video recording, screen resolution, and time zones in a Dynamic Grid](#video-recording-screen-resolution-and-time-zones-in-a-dynamic-grid)
45-
* [Kubernetes](#deploying-to-kubernetes)
41+
* [Configuration example](#configuration-example)
42+
* [Execution with Hub & Node roles](#execution-with-hub--node-roles)
43+
* [Execution with Standalone roles](#execution-with-standalone-roles)
44+
* [Using Dynamic Grid in different machines/VMs](#using-dynamic-grid-in-different-machinesvms)
45+
* [Execution with Docker Compose](#execution-with-docker-compose)
46+
* [Configuring the child containers](#configuring-the-child-containers)
47+
* [Video recording, screen resolution, and time zones in a Dynamic Grid](#video-recording-screen-resolution-and-time-zones-in-a-dynamic-grid)
48+
* [Deploying to Kubernetes](#deploying-to-kubernetes)
4649
* [Configuring the containers](#configuring-the-containers)
47-
+ [SE_OPTS Selenium Configuration Options](#se_opts-selenium-configuration-options)
48-
+ [SE_JAVA_OPTS Java Environment Options](#se_java_opts-java-environment-options)
49-
+ [Node configuration options](#node-configuration-options)
50-
+ [Setting Sub Path](#setting-sub-path)
51-
+ [Setting Screen Resolution](#setting-screen-resolution)
52-
+ [Grid Url and Session Timeout](#grid-url-and-session-timeout)
53-
+ [Session request timeout](#session-request-timeout)
54-
+ [Increasing session concurrency per container](#increasing-session-concurrency-per-container)
55-
+ [Running in Headless mode](#running-in-headless-mode)
50+
* [SE_OPTS Selenium Configuration Options](#se_opts-selenium-configuration-options)
51+
* [SE_JAVA_OPTS Java Environment Options](#se_java_opts-java-environment-options)
52+
* [Node configuration options](#node-configuration-options)
53+
* [Setting Sub Path](#setting-sub-path)
54+
* [Setting Screen Resolution](#setting-screen-resolution)
55+
* [Grid Url and Session Timeout](#grid-url-and-session-timeout)
56+
* [Session request timeout](#session-request-timeout)
57+
* [Increasing session concurrency per container](#increasing-session-concurrency-per-container)
58+
* [Running in Headless mode](#running-in-headless-mode)
59+
* [Stopping the Node/Standalone after N sessions have been executed](#stopping-the-nodestandalone-after-n-sessions-have-been-executed)
5660
* [Building the images](#building-the-images)
5761
* [Waiting for the Grid to be ready](#waiting-for-the-grid-to-be-ready)
62+
* [Adding a HEALTHCHECK to the Grid](#adding-a-healthcheck-to-the-grid)
63+
* [Using a bash script to wait for the Grid](#using-a-bash-script-to-wait-for-the-grid)
64+
* [Install certificates for Chromium-based browsers](#install-certificates-for-chromium-based-browsers)
65+
* [Alternative method: Add certificates to existing Selenium based images for browsers](#alternative-method-add-certificates-to-existing-selenium-based-images-for-browsers)
5866
* [Debugging](#debugging)
59-
* [Install certificates for Chromium based browsers](#install-certificates-for-Chromium-based-browsers)
67+
* [Using a VNC client](#using-a-vnc-client)
68+
* [Using your browser (no VNC client is needed)](#using-your-browser-no-vnc-client-is-needed)
69+
* [Disabling VNC](#disabling-vnc)
6070
* [Tracing in Grid](#tracing-in-grid)
6171
* [Troubleshooting](#troubleshooting)
62-
72+
* [`--shm-size="2g"`](#--shm-size2g)
73+
* [Headless](#headless)
74+
* [Mounting volumes to retrieve downloaded files](#mounting-volumes-to-retrieve-downloaded-files)
75+
* [Mounting volumes to retrieve video files](#mounting-volumes-to-retrieve-video-files)
76+
<!-- TOC -->
6377

6478
## Quick start
6579

@@ -521,6 +535,47 @@ Here is an example using a Hub and a few Nodes:
521535

522536
[`docker-compose-v3-video.yml`](docker-compose-v3-video.yml)
523537

538+
## Video recording and uploading
539+
540+
[RCLONE](https://rclone.org/) is installed in the video recorder image. You can use it to upload the videos to a cloud storage service.
541+
Besides the video recording mentioned above, you can enable the upload functionality by setting the following environment variables:
542+
543+
```yaml
544+
version: "3"
545+
services:
546+
chrome_video:
547+
image: selenium/video:nightly
548+
depends_on:
549+
- chrome
550+
environment:
551+
- DISPLAY_CONTAINER_NAME=chrome
552+
- SE_VIDEO_FILE_NAME=auto
553+
- SE_VIDEO_UPLOAD_ENABLED=true
554+
- SE_VIDEO_INTERNAL_UPLOAD=true
555+
- SE_UPLOAD_DESTINATION_PREFIX=s3://mybucket/path
556+
- RCLONE_CONFIG_S3_TYPE=s3
557+
- RCLONE_CONFIG_S3_PROVIDER=GCS
558+
- RCLONE_CONFIG_S3_ENV_AUTH=true
559+
- RCLONE_CONFIG_S3_REGION=asia-southeast1
560+
- RCLONE_CONFIG_S3_LOCATION_CONSTRAINT=asia-southeast1
561+
- RCLONE_CONFIG_S3_ACL=private
562+
- RCLONE_CONFIG_S3_ACCESS_KEY_ID=xxx
563+
- RCLONE_CONFIG_S3_SECRET_ACCESS_KEY=xxx
564+
- RCLONE_CONFIG_S3_ENDPOINT=https://storage.googleapis.com
565+
```
566+
567+
`SE_VIDEO_FILE_NAME=auto` will use the session id as the video file name. This ensures that the video file name is unique to upload.
568+
569+
`SE_VIDEO_UPLOAD_ENABLED=true` will enable the video upload feature. In the background, it will create a pipefile with file and destination for uploader to consume and proceed.
570+
571+
`SE_VIDEO_INTERNAL_UPLOAD=true` will use RCLONE installed in the container for upload. If you want to use another container for upload, set it to `false`.
572+
573+
For environment variables with prefix `RCLONE_` is used to pass remote configuration to RCLONE. You can find more information about RCLONE configuration [here](https://rclone.org/docs/).
574+
575+
[`docker-compose-v3-video-upload.yml`](docker-compose-v3-video-upload.yml)
576+
577+
Note that upload function is not supported for Dynamic Grid. If you want it, please create a feature request.
578+
524579
___
525580

526581
## Dynamic Grid
@@ -1336,4 +1391,11 @@ After doing this, you should be able to download files
13361391
to the mounted directory. If you have a better workaround,
13371392
please send us a pull request!
13381393

1394+
### Mounting volumes to retrieve video files
1395+
1396+
Similar to mount volumes to retrieve downloaded files. For video files, you might need to do the same
13391397

1398+
```bash
1399+
mkdir /tmp/videos
1400+
chown 1200:1201 /tmp/videos
1401+
```

Video/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ COPY --chown="${SEL_UID}:${SEL_GID}" upload.sh upload.conf /opt/bin/
8585
ENV SE_VIDEO_UPLOAD_ENABLED false
8686
ENV SE_UPLOAD_DESTINATION_PREFIX ""
8787
ENV SE_VIDEO_INTERNAL_UPLOAD false
88+
ENV UPLOAD_OPTS "-P"
8889

8990
ENV SE_VIDEO_FOLDER /videos
9091
RUN mkdir -p /var/run/supervisor /var/log/supervisor $SE_VIDEO_FOLDER \

Video/upload.sh

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,51 @@ UPLOAD_CONFIG_FILE_NAME=${UPLOAD_CONFIG_FILE_NAME:-"upload.conf"}
66
UPLOAD_COMMAND=${UPLOAD_COMMAND:-"copy"}
77
UPLOAD_RETAIN_LOCAL_FILE=${SE_UPLOAD_RETAIN_LOCAL_FILE:-"false"}
88
UPLOAD_PIPE_FILE_NAME=${UPLOAD_PIPE_FILE_NAME:-"uploadpipe"}
9-
SE_VIDEO_INTERNAL_UPLOAD=${SE_VIDEO_INTERNAL_UPLOAD:-"true"}
9+
SE_VIDEO_INTERNAL_UPLOAD=${SE_VIDEO_INTERNAL_UPLOAD:-"false"}
1010
VIDEO_UPLOAD_ENABLED=${VIDEO_UPLOAD_ENABLED:-SE_VIDEO_UPLOAD_ENABLED}
11+
SE_VIDEO_UPLOAD_BATCH_CHECK=${SE_VIDEO_UPLOAD_BATCH_CHECK:-"10"}
1112

12-
function consume_force_exit() {
13-
rm -f ${FORCE_EXIT_FILE}
14-
echo "Force exit signal consumed"
15-
}
13+
if [ "${SE_VIDEO_INTERNAL_UPLOAD}" = "true" ];
14+
then
15+
# If using RCLONE in the same container, write signal to /tmp internally
16+
UPLOAD_PIPE_FILE="/tmp/${UPLOAD_PIPE_FILE_NAME}"
17+
FORCE_EXIT_FILE="/tmp/force_exit"
18+
else
19+
# If using external container for uploading, write signal to the video folder
20+
UPLOAD_PIPE_FILE="${SE_VIDEO_FOLDER}/${UPLOAD_PIPE_FILE_NAME}"
21+
FORCE_EXIT_FILE="${SE_VIDEO_FOLDER}/force_exit"
22+
fi
23+
24+
if [ "${UPLOAD_RETAIN_LOCAL_FILE}" = "false" ];
25+
then
26+
echo "UPLOAD_RETAIN_LOCAL_FILE is set to false, force to use RCLONE command: move"
27+
UPLOAD_COMMAND="move"
28+
fi
1629

1730
function rename_rclone_env() {
1831
# This script is used to support passing environment variables for RCLONE configuration in Dynamic Grid
1932
# Dynamic Grid accepts environment variables with the prefix SE_*
2033
# RCLONE accepts environment variables with the prefix RCLONE_*
2134
# To pass the ENV vars to Dynamic Grid then to RCLONE, we need to rename the ENV vars from SE_RCLONE_* to RCLONE_*
2235
for var in $(env | cut -d= -f1); do
23-
if [[ "$var" == SE_RCLONE_* ]]; then
36+
if [[ "$var" == SE_RCLONE_* ]];
37+
then
2438
suffix="${var#SE_RCLONE_}"
2539
new_var="RCLONE_$suffix"
2640
export "$new_var=${!var}"
2741
fi
2842
done
2943
}
3044

31-
if [ "${SE_VIDEO_INTERNAL_UPLOAD}" = "true" ];
32-
then
33-
UPLOAD_PIPE_FILE="/tmp/${UPLOAD_PIPE_FILE_NAME}"
34-
FORCE_EXIT_FILE="/tmp/force_exit"
35-
else
36-
UPLOAD_PIPE_FILE="${SE_VIDEO_FOLDER}/${UPLOAD_PIPE_FILE_NAME}"
37-
FORCE_EXIT_FILE="${SE_VIDEO_FOLDER}/force_exit"
38-
fi
39-
40-
trap consume_force_exit EXIT
45+
function graceful_exit() {
46+
for pid in "${list_rclone_pid[@]}";
47+
do
48+
wait ${pid}
49+
done
50+
rm -rf ${FORCE_EXIT_FILE}
51+
echo "Uploader is ready to shutdown"
52+
}
53+
trap graceful_exit EXIT
4154

4255
while [ ! -p ${UPLOAD_PIPE_FILE} ];
4356
do
@@ -49,23 +62,28 @@ echo "Waiting for video files put into pipe for proceeding to upload"
4962

5063
rename_rclone_env
5164

65+
list_rclone_pid=()
5266
while read FILE DESTINATION < ${UPLOAD_PIPE_FILE}
5367
do
5468
if [ "${FILE}" = "exit" ];
5569
then
5670
exit
57-
else [ "$FILE" != "" ] && [ "$DESTINATION" != "" ];
71+
elif [ "$FILE" != "" ] && [ "$DESTINATION" != "" ];
72+
then
5873
echo "Uploading ${FILE} to ${DESTINATION}"
59-
rclone --config ${UPLOAD_CONFIG_DIRECTORY}/${UPLOAD_CONFIG_FILE_NAME} ${UPLOAD_COMMAND} ${UPLOAD_OPTS} "${FILE}" "${DESTINATION}"
60-
if [ $? -eq 0 ] && [ "${UPLOAD_RETAIN_LOCAL_FILE}" = "false" ];
74+
rclone --config ${UPLOAD_CONFIG_DIRECTORY}/${UPLOAD_CONFIG_FILE_NAME} ${UPLOAD_COMMAND} ${UPLOAD_OPTS} "${FILE}" "${DESTINATION}" &
75+
list_rclone_pid+=($!)
76+
else
77+
# Wait for a batch rclone processes to finish
78+
if [ ${#list_rclone_pid[@]} -eq ${SE_VIDEO_UPLOAD_BATCH_CHECK} ]
6179
then
62-
rm -rf $FILE
80+
for pid in "${list_rclone_pid[@]}";
81+
do
82+
wait ${pid}
83+
done
84+
list_rclone_pid=()
6385
fi
6486
fi
65-
if [ -f ${FORCE_EXIT_FILE} ] && [ ! -s ${UPLOAD_PIPE_FILE} ];
66-
then
67-
exit
68-
fi
6987
done
7088

71-
consume_force_exit
89+
graceful_exit

0 commit comments

Comments
 (0)