hw4: finalize selenoid and ansible workflow with citrus tests
This commit is contained in:
19
ansible/roles/common/tasks/main.yml
Normal file
19
ansible/roles/common/tasks/main.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Update apt cache
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
|
||||
- name: Upgrade apt packages (optional)
|
||||
ansible.builtin.apt:
|
||||
upgrade: dist
|
||||
when: run_apt_upgrade | bool
|
||||
|
||||
- name: Install base packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- ca-certificates
|
||||
- curl
|
||||
- gnupg
|
||||
- lsb-release
|
||||
state: present
|
||||
32
ansible/roles/docker/tasks/main.yml
Normal file
32
ansible/roles/docker/tasks/main.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
- name: Check whether docker CLI is already available
|
||||
ansible.builtin.command: docker --version
|
||||
register: docker_cli_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Install Docker packages when docker is missing
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- docker.io
|
||||
- docker-compose-v2
|
||||
state: present
|
||||
when: docker_cli_check.rc != 0
|
||||
|
||||
- name: Enable Docker service when installed by role and systemd is available
|
||||
ansible.builtin.service:
|
||||
name: docker
|
||||
state: started
|
||||
enabled: true
|
||||
when:
|
||||
- docker_cli_check.rc != 0
|
||||
- ansible_service_mgr == "systemd"
|
||||
|
||||
- name: Add current ansible user to docker group
|
||||
ansible.builtin.user:
|
||||
name: "{{ ansible_user }}"
|
||||
groups: docker
|
||||
append: true
|
||||
when:
|
||||
- ansible_user is defined
|
||||
- docker_cli_check.rc != 0
|
||||
37
ansible/roles/ggr_gateway/tasks/main.yml
Normal file
37
ansible/roles/ggr_gateway/tasks/main.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
- name: Create GGR directory structure
|
||||
ansible.builtin.file:
|
||||
path: "{{ ggr_dir }}/{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- quota
|
||||
- nginx
|
||||
|
||||
- name: Render GGR quota file
|
||||
ansible.builtin.template:
|
||||
src: quota.xml.j2
|
||||
dest: "{{ ggr_dir }}/quota/{{ ggr_quota_name }}.xml"
|
||||
mode: "0644"
|
||||
|
||||
- name: Render nginx config
|
||||
ansible.builtin.template:
|
||||
src: nginx.conf.j2
|
||||
dest: "{{ ggr_dir }}/nginx/default.conf"
|
||||
mode: "0644"
|
||||
|
||||
- name: Render docker compose for GGR gateway
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.ggr.yml.j2
|
||||
dest: "{{ ggr_dir }}/{{ ggr_compose_file }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Stop previous GGR gateway stack and remove orphans
|
||||
ansible.builtin.command:
|
||||
cmd: docker compose -f {{ ggr_compose_file }} down --remove-orphans
|
||||
chdir: "{{ ggr_dir }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Start GGR gateway stack
|
||||
ansible.builtin.command:
|
||||
cmd: docker compose -f {{ ggr_compose_file }} up -d --force-recreate
|
||||
chdir: "{{ ggr_dir }}"
|
||||
@@ -0,0 +1,46 @@
|
||||
services:
|
||||
ggr:
|
||||
image: "{{ ggr_image }}"
|
||||
container_name: ggr
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "{{ ggr_gateway_port }}:4444"
|
||||
volumes:
|
||||
- "./quota:/etc/grid-router/quota:ro"
|
||||
command:
|
||||
- "-listen"
|
||||
- ":4444"
|
||||
- "-quotaDir"
|
||||
- "/etc/grid-router/quota"
|
||||
- "-guests-allowed"
|
||||
|
||||
ggr-ui:
|
||||
image: "{{ ggr_ui_image }}"
|
||||
container_name: ggr-ui
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "{{ ggr_ui_public_port }}:8888"
|
||||
volumes:
|
||||
- "./quota:/etc/grid-router/quota:ro"
|
||||
command:
|
||||
- "-listen"
|
||||
- ":8888"
|
||||
- "-quota-dir"
|
||||
- "/etc/grid-router/quota"
|
||||
|
||||
nginx:
|
||||
image: "{{ nginx_image }}"
|
||||
container_name: grid-nginx
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- ggr
|
||||
- ggr-ui
|
||||
ports:
|
||||
- "{{ ggr_nginx_public_port }}:80"
|
||||
volumes:
|
||||
- "./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro"
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: "{{ selenoid_container_network }}"
|
||||
external: true
|
||||
53
ansible/roles/ggr_gateway/templates/nginx.conf.j2
Normal file
53
ansible/roles/ggr_gateway/templates/nginx.conf.j2
Normal file
@@ -0,0 +1,53 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
location /ws/ {
|
||||
proxy_pass http://selenoid:4444/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /vnc/ {
|
||||
proxy_pass http://selenoid:4444/vnc/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /wd/hub/ {
|
||||
proxy_pass http://ggr:4444/wd/hub/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /ggr/ {
|
||||
proxy_pass http://ggr-ui:8888/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://selenoid-ui:8080/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
21
ansible/roles/ggr_gateway/templates/quota.xml.j2
Normal file
21
ansible/roles/ggr_gateway/templates/quota.xml.j2
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<qa:browsers xmlns:qa="urn:config.gridrouter.qatools.ru">
|
||||
<browser name="chrome" defaultVersion="{{ selenoid_chrome_versions | first }}">
|
||||
{% for version in selenoid_chrome_versions %}
|
||||
<version number="{{ version }}">
|
||||
<region name="default">
|
||||
<host name="{{ ggr_selenoid_host_override | default(ansible_host | default(inventory_hostname)) }}" port="{{ selenoid_host_port }}" count="{{ selenoid_limit }}"/>
|
||||
</region>
|
||||
</version>
|
||||
{% endfor %}
|
||||
</browser>
|
||||
<browser name="firefox" defaultVersion="{{ selenoid_firefox_versions | first }}">
|
||||
{% for version in selenoid_firefox_versions %}
|
||||
<version number="{{ version }}">
|
||||
<region name="default">
|
||||
<host name="{{ ggr_selenoid_host_override | default(ansible_host | default(inventory_hostname)) }}" port="{{ selenoid_host_port }}" count="{{ selenoid_limit }}"/>
|
||||
</region>
|
||||
</version>
|
||||
{% endfor %}
|
||||
</browser>
|
||||
</qa:browsers>
|
||||
68
ansible/roles/selenoid/tasks/main.yml
Normal file
68
ansible/roles/selenoid/tasks/main.yml
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
- name: Create Selenoid directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ selenoid_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Create logs directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ selenoid_dir }}/{{ item }}"
|
||||
state: directory
|
||||
mode: "0777"
|
||||
loop:
|
||||
- logs
|
||||
|
||||
- name: Render browsers.json
|
||||
ansible.builtin.template:
|
||||
src: browsers.json.j2
|
||||
dest: "{{ selenoid_dir }}/browsers.json"
|
||||
mode: "0644"
|
||||
|
||||
- name: Render docker compose for Selenoid
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.selenoid.yml.j2
|
||||
dest: "{{ selenoid_dir }}/{{ selenoid_compose_file }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Ensure default chrome version is in the configured versions list
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (selenoid_chrome_versions | length) >= 2
|
||||
- (selenoid_firefox_versions | length) >= 2
|
||||
fail_msg: "Invalid Selenoid config: provide at least 2 chrome and 2 firefox versions"
|
||||
|
||||
- name: Check shared Docker network for grid stack
|
||||
ansible.builtin.command:
|
||||
cmd: docker network inspect {{ selenoid_container_network }}
|
||||
register: shared_grid_network
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Create shared Docker network for grid stack
|
||||
ansible.builtin.command:
|
||||
cmd: docker network create {{ selenoid_container_network }}
|
||||
when: shared_grid_network.rc != 0
|
||||
|
||||
- name: Stop previous Selenoid stack and remove orphans
|
||||
ansible.builtin.command:
|
||||
cmd: docker compose -f {{ selenoid_compose_file }} down --remove-orphans
|
||||
chdir: "{{ selenoid_dir }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Pull required Chrome images for Selenoid
|
||||
ansible.builtin.command:
|
||||
cmd: "docker pull selenoid/vnc:chrome_{{ item }}"
|
||||
loop: "{{ selenoid_chrome_versions }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Pull required Firefox images for Selenoid
|
||||
ansible.builtin.command:
|
||||
cmd: "docker pull selenoid/vnc:firefox_{{ item }}"
|
||||
loop: "{{ selenoid_firefox_versions }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Start Selenoid stack
|
||||
ansible.builtin.command:
|
||||
cmd: docker compose -f {{ selenoid_compose_file }} up -d
|
||||
chdir: "{{ selenoid_dir }}"
|
||||
26
ansible/roles/selenoid/templates/browsers.json.j2
Normal file
26
ansible/roles/selenoid/templates/browsers.json.j2
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"chrome": {
|
||||
"default": "{{ selenoid_chrome_versions | first }}",
|
||||
"versions": {
|
||||
{% for version in selenoid_chrome_versions %}
|
||||
"{{ version }}": {
|
||||
"image": "selenoid/vnc:chrome_{{ version }}",
|
||||
"port": "4444",
|
||||
"path": "/"
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
},
|
||||
"firefox": {
|
||||
"default": "{{ selenoid_firefox_versions | first }}",
|
||||
"versions": {
|
||||
{% for version in selenoid_firefox_versions %}
|
||||
"{{ version }}": {
|
||||
"image": "selenoid/vnc:firefox_{{ version }}",
|
||||
"port": "4444",
|
||||
"path": "/wd/hub"
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
services:
|
||||
selenoid:
|
||||
image: "{{ selenoid_image }}"
|
||||
container_name: selenoid
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- DOCKER_API_VERSION=1.45
|
||||
ports:
|
||||
- "{{ selenoid_host_port }}:4444"
|
||||
volumes:
|
||||
- "./browsers.json:/etc/selenoid/browsers.json:ro"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
- "./logs:/opt/selenoid/logs"
|
||||
command:
|
||||
- "-limit"
|
||||
- "{{ selenoid_limit }}"
|
||||
- "-conf"
|
||||
- "/etc/selenoid/browsers.json"
|
||||
- "-log-output-dir"
|
||||
- "/opt/selenoid/logs"
|
||||
- "-container-network"
|
||||
- "{{ selenoid_container_network }}"
|
||||
- "-timeout"
|
||||
- "3m"
|
||||
- "-session-attempt-timeout"
|
||||
- "2m"
|
||||
- "-service-startup-timeout"
|
||||
- "2m"
|
||||
|
||||
selenoid-ui:
|
||||
image: "{{ selenoid_ui_image }}"
|
||||
container_name: selenoid-ui
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- selenoid
|
||||
ports:
|
||||
- "{{ selenoid_ui_port }}:8080"
|
||||
command:
|
||||
- "--selenoid-uri"
|
||||
- "http://selenoid:4444"
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: "{{ selenoid_container_network }}"
|
||||
external: true
|
||||
|
||||
38
ansible/roles/test_runner/tasks/main.yml
Normal file
38
ansible/roles/test_runner/tasks/main.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
- name: Install Java and Maven for test execution
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- openjdk-21-jdk
|
||||
- maven
|
||||
state: present
|
||||
update_cache: true
|
||||
when: test_runner_execute_mode == "target"
|
||||
|
||||
- name: Run UI smoke test via Selenoid endpoint
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
mvn
|
||||
"-Dexecution.mode=selenoid"
|
||||
"-Dbrowser={{ test_runner_browser }}"
|
||||
"-Dbrowser.version={{ selenoid_chrome_versions[0] }}"
|
||||
"-Dselenoid.url=http://{{ (ansible_host | default('127.0.0.1')) if test_runner_execute_mode == 'control' else '127.0.0.1' }}/wd/hub"
|
||||
"-Dtest={{ test_runner_ui_test_class }}"
|
||||
test
|
||||
chdir: "{{ test_runner_control_project_dir if test_runner_execute_mode == 'control' else test_runner_project_dir }}"
|
||||
environment:
|
||||
JAVA_HOME: /usr/lib/jvm/java-21-openjdk-amd64
|
||||
delegate_to: "{{ 'localhost' if test_runner_execute_mode == 'control' else omit }}"
|
||||
become: false
|
||||
register: ui_test_result
|
||||
changed_when: false
|
||||
|
||||
- name: Run Citrus API tests
|
||||
ansible.builtin.command:
|
||||
cmd: mvn -f citrus-tests/pom.xml test
|
||||
chdir: "{{ test_runner_control_project_dir if test_runner_execute_mode == 'control' else test_runner_project_dir }}"
|
||||
environment:
|
||||
JAVA_HOME: /usr/lib/jvm/java-21-openjdk-amd64
|
||||
delegate_to: "{{ 'localhost' if test_runner_execute_mode == 'control' else omit }}"
|
||||
become: false
|
||||
register: citrus_test_result
|
||||
changed_when: false
|
||||
40
ansible/roles/verify/tasks/main.yml
Normal file
40
ansible/roles/verify/tasks/main.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
- name: Wait for Selenoid status endpoint
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ selenoid_host_port }}/status"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: selenoid_status_response
|
||||
retries: 20
|
||||
delay: 3
|
||||
until: selenoid_status_response.status == 200
|
||||
|
||||
- name: Wait for GGR ping endpoint
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ ggr_gateway_port }}/ping"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: ggr_ping_response
|
||||
retries: 20
|
||||
delay: 3
|
||||
until: ggr_ping_response.status == 200
|
||||
|
||||
- name: Wait for gateway UI endpoint
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ ggr_nginx_public_port }}/"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: ggr_ui_response
|
||||
retries: 20
|
||||
delay: 3
|
||||
until: ggr_ui_response.status == 200
|
||||
|
||||
- name: Wait for gateway webdriver status endpoint
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ ggr_nginx_public_port }}/wd/hub/status"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: wd_status_response
|
||||
retries: 20
|
||||
delay: 3
|
||||
until: wd_status_response.status == 200
|
||||
Reference in New Issue
Block a user