Skip to content

Commit

Permalink
[feat][test]: add env vars to generate config for Relay in node-base
Browse files Browse the repository at this point in the history
Signed-off-by: Viet Nguyen Duc <nguyenducviet4496@gmail.com>
  • Loading branch information
VietND96 committed May 9, 2024
1 parent 739ebea commit 58043d6
Show file tree
Hide file tree
Showing 15 changed files with 251 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ on:
pull_request:
paths-ignore:
- '**.md'
schedule:
- cron: '0 0 * * *'

permissions:
contents: read
Expand All @@ -45,6 +43,8 @@ jobs:
test-video: true
- test-strategy: test_parallel
test-video: false
- test-strategy: test_node_relay
test-video: false
steps:
- uses: actions/checkout@main
- name: Set up QEMU
Expand All @@ -61,6 +61,12 @@ jobs:
with:
python-version: '3.11'
check-latest: true
- name: Enable KVM
if: matrix.test-strategy == 'test_node_relay'
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Get branch name (only for push to branch)
if: github.event_name == 'push'
run: echo "BRANCH=$(echo ${PUSH_BRANCH##*/})" >> $GITHUB_ENV
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/helm-chart-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ on:
description: 'Test parameter for different log level'
required: false
default: 'FINE'
schedule:
- cron: '0 0 * * *'

permissions:
contents: read
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/nightly.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ on:
- cron: '0 1 * * *'

jobs:
docker-test:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
uses: ./.github/workflows/docker-test.yml

helm-chart-test:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
uses: ./.github/workflows/helm-chart-test.yml

deploy:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
name: Nightly build
runs-on: ubuntu-latest
permissions: write-all
Expand Down
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,27 @@ test_video: video hub chrome firefox edge
done
make test_video_integrity

test_node_relay: hub node_base standalone_firefox
sudo rm -rf ./tests/tests
for node in NodeFirefox Android ; do \
cd ./tests || true ; \
echo TAG=$(TAG_VERSION) > .env ; \
echo LOG_LEVEL=$(or $(LOG_LEVEL), "INFO") >> .env ; \
echo REQUEST_TIMEOUT=$(or $(REQUEST_TIMEOUT), 300) >> .env ; \
echo SESSION_TIMEOUT=$(or $(SESSION_TIMEOUT), 300) >> .env ; \
echo ANDROID_BASED_NAME=$(or $(ANDROID_BASED_NAME),budtmo) >> .env ; \
echo ANDROID_BASED_IMAGE=$(or $(ANDROID_BASED_IMAGE),docker-android) >> .env ; \
echo ANDROID_BASED_TAG=$(or $(ANDROID_BASED_TAG),emulator_14.0) >> .env ; \
echo ANDROID_PLATFORM_API=$(or $(ANDROID_PLATFORM_API),14) >> .env ; \
echo TEST_DELAY_AFTER_TEST=$(or $(TEST_DELAY_AFTER_TEST), 15) >> .env ; \
echo NODE=$$node >> .env ; \
echo TEST_NODE_RELAY=$$node >> .env ; \
echo UID=$$(id -u) >> .env ; \
echo BINDING_VERSION=$(BINDING_VERSION) >> .env ; \
docker compose -f docker-compose-v3-test-node-relay.yml up --no-log-prefix --exit-code-from tests --build ; \
if [ $$? -ne 0 ]; then exit 1; fi ; \
done

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
Expand Down
2 changes: 1 addition & 1 deletion NodeBase/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ COPY --chown="${SEL_UID}:${SEL_GID}" start-selenium-node.sh \
start-xvfb.sh \
start-vnc.sh \
start-novnc.sh \
generate_config /opt/bin/
generate_config generate_relay_config /opt/bin/

# Selenium Grid logo as wallpaper for Fluxbox
COPY selenium_grid_logo.png /usr/share/images/fluxbox/ubuntu-light.png
Expand Down
34 changes: 22 additions & 12 deletions NodeBase/generate_config
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,34 @@ echo "session-timeout = \"${SE_NODE_SESSION_TIMEOUT}\"" >> "$FILENAME"
echo "override-max-sessions = ${SE_NODE_OVERRIDE_MAX_SESSIONS}" >> "$FILENAME"
echo "detect-drivers = false" >> "$FILENAME"
echo "drain-after-session-count = ${DRAIN_AFTER_SESSION_COUNT:-$SE_DRAIN_AFTER_SESSION_COUNT}" >> "$FILENAME"
echo "max-sessions = ${SE_NODE_MAX_SESSIONS}
# When node is handled both browser and relay, SE_NODE_MAX_CONCURRENCY is used to configure max concurrency based on sum of them
echo "max-sessions = ${SE_NODE_MAX_CONCURRENCY:-${SE_NODE_MAX_SESSIONS}}
" >> "$FILENAME"

SE_NODE_BROWSER_NAME=$(cat /opt/selenium/browser_name)
SE_NODE_BROWSER_VERSION=$(short_version $(cat /opt/selenium/browser_version))
SE__BROWSER_BINARY_LOCATION=$(cat /opt/selenium/browser_binary_location)
if [ -f /opt/selenium/browser_name ]; then
SE_NODE_BROWSER_NAME=$(cat /opt/selenium/browser_name)
fi
if [ -f /opt/selenium/browser_version ]; then
SE_NODE_BROWSER_VERSION=$(short_version $(cat /opt/selenium/browser_version))
fi
if [ -f /opt/selenium/browser_binary_location ]; then
SE__BROWSER_BINARY_LOCATION=$(cat /opt/selenium/browser_binary_location)
fi

if [[ -z "$SE_NODE_STEREOTYPE" ]]; then
# 'browserName' is mandatory for default stereotype
if [[ -z "${SE_NODE_STEREOTYPE}" ]] && [[ -n "${SE_NODE_BROWSER_NAME}" ]]; 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"
SE_NODE_STEREOTYPE="${SE_NODE_STEREOTYPE}"
fi

echo "[[node.driver-configuration]]" >> "$FILENAME"
echo "display-name = \"${SE_NODE_BROWSER_NAME}\"" >> "$FILENAME"
echo "stereotype = '${SE_NODE_STEREOTYPE}'" >> "$FILENAME"
echo "max-sessions = ${SE_NODE_MAX_SESSIONS}
" >> "$FILENAME"

# 'stereotype' setting is mandatory
if [[ -n "${SE_NODE_STEREOTYPE}" ]]; then
echo "[[node.driver-configuration]]" >> "$FILENAME"
echo "display-name = \"${SE_NODE_BROWSER_NAME}\"" >> "$FILENAME"
echo "stereotype = '${SE_NODE_STEREOTYPE}'" >> "$FILENAME"
echo "max-sessions = ${SE_NODE_MAX_SESSIONS}
" >> "$FILENAME"
fi


24 changes: 24 additions & 0 deletions NodeBase/generate_relay_config
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

if [[ -z "$CONFIG_FILE" ]]; then
FILENAME="/opt/selenium/config.toml"
else
FILENAME="$CONFIG_FILE"
fi

if [[ -n "${SE_NODE_RELAY_URL}" ]]; then
echo "[relay]" >> "$FILENAME"
echo "url = \"${SE_NODE_RELAY_URL}\"" >> "$FILENAME"
if [[ -z "${SE_NODE_RELAY_STATUS_ENDPOINT}" ]]; then
echo "status-endpoint = \"/status\"" >> "$FILENAME"
else
echo "status-endpoint = \"${SE_NODE_RELAY_STATUS_ENDPOINT}\"" >> "$FILENAME"
fi
if [[ -n "${SE_NODE_RELAY_PROTOCOL_VERSION}" ]]; then
echo "protocol-version = \"${SE_NODE_RELAY_PROTOCOL_VERSION}\"" >> "$FILENAME"
fi
echo "configs = [
\"${SE_NODE_RELAY_MAX_SESSIONS}\", \"{\\\"browserName\\\": \\\"${SE_NODE_RELAY_BROWSER_NAME}\\\", \\\"platformName\\\": \\\"${SE_NODE_RELAY_PLATFORM_NAME}\\\", \\\"appium:platformVersion\\\": \\\"${SE_NODE_RELAY_PLATFORM_VERSION}\\\", \\\"se:vnc\\\": \\\"${SE_NODE_RELAY_WEB_VNC}\\\"}\"
]
" >> "$FILENAME"
fi
1 change: 1 addition & 0 deletions NodeBase/start-selenium-node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ fi
if [ "$GENERATE_CONFIG" = true ]; then
echo "Generating Selenium Config"
/opt/bin/generate_config
/opt/bin/generate_relay_config
fi

EXTRA_LIBS=""
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,35 @@ $ docker run -d \
--shm-size="2g" selenium/node-chrome:4.20.0-20240505
```

### Node configuration relay commands

Relaying commands to a service endpoint that supports WebDriver.
It is useful to connect an external service that supports WebDriver to Selenium Grid. An example of such service could be a cloud provider or an Appium server.
In this way, Grid can enable more coverage to platforms and versions not present locally.

The following is an en example of connecting an Appium server to Grid.

[docker-compose-v3-relay.yml](docker-compose-v3-relay.yml)

If you want to relay commands only, `selenium/node-base` is suitable and lightweight for this purpose.
In case you want to configure node with both browsers and relay commands, respective node images can be used.

To use environment variables for generate relay configs, set `SE_NODE_RELAY_URL` and other variables as below

```toml
[relay]
url = "${SE_NODE_RELAY_URL}"
status-endpoint = "${SE_NODE_RELAY_STATUS_ENDPOINT}"
protocol-version = "${SE_NODE_RELAY_PROTOCOL_VERSION}"
configs = [ '${SE_NODE_RELAY_MAX_SESSIONS}', '{"browserName": "${SE_NODE_RELAY_BROWSER_NAME}", "platformName": "${SE_NODE_RELAY_PLATFORM_NAME}", "appium:platformVersion": "${SE_NODE_RELAY_PLATFORM_VERSION}", "se:vnc": "${SE_NODE_RELAY_WEB_VNC}"}' ]
```

To run a sample test with the relayed node, you can clone the project and try below command:

```bash
make test_node_relay
```

### Setting Sub Path

By default, Selenium is reachable at `http://127.0.0.1:4444/`. Selenium can be configured to use a custom subpath by specifying the `SE_SUB_PATH`
Expand Down
1 change: 1 addition & 0 deletions Standalone/start-selenium-standalone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ if [ ! -z "$SE_NEW_SESSION_THREAD_POOL_SIZE" ]; then
fi

/opt/bin/generate_config
/opt/bin/generate_relay_config

echo "Selenium Grid Standalone configuration: "
cat /opt/selenium/config.toml
Expand Down
13 changes: 13 additions & 0 deletions tests/Dockerfile.emulator
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
ARG ANDROID_BASED_NAME
ARG ANDROID_BASED_IMAGE
ARG ANDROID_BASED_TAG
FROM ${ANDROID_BASED_NAME}/${ANDROID_BASED_IMAGE}:${ANDROID_BASED_TAG} AS android_based

ARG CHROME_DRIVER_URL
# Download appium chromedriver
RUN curl ${CHROME_DRIVER_URL} -o /tmp/chromedriver.zip \
&& rm -rf ~/.appium/node_modules/appium-uiautomator2-driver/node_modules/appium-chromedriver/chromedriver/linux \
&& mkdir -p ~/.appium/node_modules/appium-uiautomator2-driver/node_modules/appium-chromedriver/chromedriver/linux \
&& unzip /tmp/chromedriver.zip -d ~/.appium/node_modules/appium-uiautomator2-driver/node_modules/appium-chromedriver/chromedriver/linux \
&& ~/.appium/node_modules/appium-uiautomator2-driver/node_modules/appium-chromedriver/chromedriver/linux/chromedriver --version \
&& rm -rf /tmp/chromedriver.zip
13 changes: 13 additions & 0 deletions tests/SeleniumTests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@
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))
TEST_NODE_RELAY = os.environ.get('TEST_NODE_RELAY', 'false')
TEST_ANDROID_PLATFORM_API = os.environ.get('ANDROID_PLATFORM_API')

if SELENIUM_GRID_USERNAME and SELENIUM_GRID_PASSWORD:
SELENIUM_GRID_HOST = f"{SELENIUM_GRID_USERNAME}:{SELENIUM_GRID_PASSWORD}@{SELENIUM_GRID_HOST}"

if TEST_NODE_RELAY == 'Android':
time.sleep(120)

class SeleniumGenericTests(unittest.TestCase):

def test_title(self):
Expand Down Expand Up @@ -126,6 +131,14 @@ def setUp(self):
options.set_capability('se:screenResolution', '1920x1080')
if SELENIUM_GRID_TEST_HEADLESS:
options.add_argument('--headless=new')
if TEST_NODE_RELAY == 'Android':
options.set_capability('platformName', TEST_NODE_RELAY)
options.set_capability('appium:platformVersion', TEST_ANDROID_PLATFORM_API)
options.set_capability('appium:deviceName', 'Samsung Galaxy S10')
options.set_capability('appium:automationName', 'uiautomator2')
options.set_capability('appium:browserName', 'chrome')
else:
options.set_capability('platformName', 'Linux')
start_time = time.time()
self.driver = webdriver.Remote(
options=options,
Expand Down
90 changes: 90 additions & 0 deletions tests/docker-compose-v3-test-node-relay.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
version: "3"
services:
node-relay-emulator:
image: selenium/node-base:${TAG}
shm_size: 2gb
depends_on:
- selenium-hub
- appium-emulator
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
- SE_LOG_LEVEL=${LOG_LEVEL}
- SE_NODE_SESSION_TIMEOUT=${SESSION_TIMEOUT}
- SE_NODE_RELAY_URL=http://appium-emulator:4723
- SE_NODE_RELAY_PROTOCOL_VERSION=HTTP/1.1
- SE_NODE_RELAY_MAX_SESSIONS=1
- SE_NODE_RELAY_PLATFORM_NAME=Android
- SE_NODE_RELAY_PLATFORM_VERSION=14
- SE_NODE_RELAY_BROWSER_NAME=chrome
- SE_NODE_RELAY_WEB_VNC=ws://appium-emulator:6080/websockify

node-relay-standalone:
image: selenium/node-base:${TAG}
shm_size: 2gb
depends_on:
- selenium-hub
- firefox-receiver
volumes:
- ./relay_config.toml:/opt/selenium/config.toml
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
- SE_LOG_LEVEL=${LOG_LEVEL}
- GENERATE_CONFIG=false

selenium-hub:
image: selenium/hub:${TAG}
container_name: selenium-hub
environment:
- SE_LOG_LEVEL=${LOG_LEVEL}
- SE_SESSION_REQUEST_TIMEOUT=${REQUEST_TIMEOUT}
ports:
- "4442:4442"
- "4443:4443"
- "4444:4444"

tests:
image: docker-selenium-tests:latest
build:
context: ./
dockerfile: ./Dockerfile
depends_on:
- selenium-hub
environment:
- RUN_IN_DOCKER_COMPOSE=true
- SELENIUM_GRID_HOST=selenium-hub
- BINDING_VERSION=${BINDING_VERSION}
- SELENIUM_ENABLE_MANAGED_DOWNLOADS=false
- TEST_NODE_RELAY=${TEST_NODE_RELAY}
- ANDROID_PLATFORM_API=${ANDROID_PLATFORM_API}
- TEST_DELAY_AFTER_TEST=${TEST_DELAY_AFTER_TEST}
command: ["./bootstrap.sh", "${NODE}"]

firefox-receiver:
image: selenium/standalone-firefox:${TAG}
shm_size: 2gb
container_name: firefox-receiver

appium-emulator:
image: ${ANDROID_BASED_NAME}/${ANDROID_BASED_IMAGE}:latest
build:
args:
ANDROID_BASED_NAME: ${ANDROID_BASED_NAME}
ANDROID_BASED_IMAGE: ${ANDROID_BASED_IMAGE}
ANDROID_BASED_TAG: ${ANDROID_BASED_TAG}
CHROME_DRIVER_URL: https://chromedriver.storage.googleapis.com/113.0.5672.63/chromedriver_linux64.zip
dockerfile: ./Dockerfile.emulator
container_name: appium-emulator
environment:
- EMULATOR_DEVICE=Samsung Galaxy S10
- WEB_VNC=true
- APPIUM=true
- WEB_LOG=true
devices:
- /dev/kvm
ports:
- "6080:6080"
- "4723:4723"
17 changes: 17 additions & 0 deletions tests/relay_config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[events]
publish = "tcp://selenium-hub:4442"
subscribe = "tcp://selenium-hub:4443"

[node]
session-timeout = "300"
override-max-sessions = false
detect-drivers = false
drain-after-session-count = 0
max-sessions = 1

[relay]
url = "http://firefox-receiver:4444/wd/hub"
status-endpoint = "/status"
configs = [
'1', '{"browserName":"firefox","platformName":"linux"}'
]
2 changes: 2 additions & 0 deletions tests/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
}

TEST_NAME_MAP = {
"Android": "ChromeTests",

# Chrome Images
'NodeChrome': 'ChromeTests',
'StandaloneChrome': 'ChromeTests',
Expand Down

0 comments on commit 58043d6

Please sign in to comment.