Hi !
Just found a way to play with squash-devops in my gitlab-ci.
I'll drop here some file contents, maybe it will help ?
Also i will take any good advice on some errors i could have made.
.gitlab-ci.yml
Code : Tout sélectionner
default:
image: "$CI_REGISTRY/$CI_PROJECT_PATH/squash-devops-gitlab-runner:1.0.0" # dedicated image in gitlab's project's container registry (see Docckerfile below)
stages:
- acceptance
opentf-workflow:
stage: acceptance
variables:
SQUASH_HOST: "$OPENTF_SERVER" # defined as gitlab ci/cd/variables (ex: "https://host.domain.tld/squash")
SQUASH_TOKEN: "$OPENTF_TOKEN" # defined as gitlab ci/cd/variables (see: https://opentestfactory.org/installation.html#creating-jwt-tokens)
SQUASH_NAMESPACE: "squash-devops" # allows to not interfere with default namespace agents, and use quash-devops-agent-robotframework service)
SQUASH_NAME: "squash-devops-pipeline-$CI_PIPELINE_ID" # this displays in squash-tm orchestrator environments during pipeline activity)
SQUASH_PORT: "443" # best practice is to hide everything behind a reverse https proxy like traefik
services:
- $CI_REGISTRY/$CI_PROJECT_PATH/squash-devops-agent-robotframework:1.0.0 # squash agent lives in gitlab-ci pipeline as a service
script:
- mkdir -p ~/.opentf && cp $OPENTF_CONFIG ~/.opentf/config # configuration file for opentf-ctl auth
- AGENT_ID=$(opentf-ctl get agents|grep $SQUASH_NAME|cut -d',' -f1) && echo AGENT_ID=$AGENT_ID # remember agent uuid to delete at end of workflow
- RUN_WORKFLOW=$(opentf-ctl run workflow squash-workflow.yml) && echo RUN_WORKFLOW=$RUN_WORKFLOW # runs squash-workflow.yml
- WORKFLOW_ID=$(echo $RUN_WORKFLOW | cut -d' ' -f2) && echo WORKFLOW_ID=$WORKFLOW_ID # remember workflow uuid to watch + qualitygate
- opentf-ctl get workflow $WORKFLOW_ID --watch > squash-tm.log # wait for completion by agent and stores results in file for gitlab pipeline artifact
- opentf-ctl delete agent $AGENT_ID # allows the agent to be deregistered from orchestrator
- opentf-ctl get qualitygate $WORKFLOW_ID --mode=strict # pipeline fails if qualitygate fails
artifacts:
paths:
- squash-tm.log # stores results as gitlab pipeline artifacts (results are also in squash-tm with all reports)
squash-workflow.yml
Code : Tout sélectionner
apiVersion: opentestfactory.org/v1alpha1
kind: Workflow
metadata:
name: squash-devops-pipeline-workflow
namespace: squash-devops
jobs:
iteration:
generator: tm.squashtest.org/tm.generator@v1
with:
testPlanType: Iteration
testPlanUuid: <iteration uuid found in squash-tm iteration information tab>
squashTMUrl: https://squash-tm.domain.tld/squash
squashTMAutomatedServerLogin: <tfserver user> #This user must be defined as an automation server in squash-tm, with clearance to the project of iteration launched
squashTMAutomatedServerPassword: <tfserver password>
OPENTF_CONFIG defined in ci/cd variables (Option: 'Expanded')
Code : Tout sélectionner
apiVersion: opentestfactory.org/v1alpha1
contexts:
- context:
orchestrator: default
user: default
name: default
current-context: default
kind: CtlConfig
orchestrators:
- name: default
orchestrator:
insecure-skip-tls-verify: false
server: $OPENTF_SERVER
services:
agentchannel:
port: 443
eventbus:
port: 443
killswitch:
port: 443
observer:
port: 443
force-base-url: true # needed with https otherwise some requests are made with http
qualitygate:
port: 443
receptionist:
port: 443
users:
- name: default
user:
token: $OPENTF_TOKEN
squash-devops-gitlab-runner/Dockerfile
Code : Tout sélectionner
FROM python:3.11.1-slim
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y git --no-install-recommends && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir opentf-tools
squash-devops-agent-robotframework/Dockerfile
Code : Tout sélectionner
FROM python:3.11.1-slim
ENV SQUASH_HOST=https://squash-tm.external.url.fqdn
ENV SQUASH_PORT=443
ENV SQUASH_NAMESPACE=squash-devops
ENV SQUASH_NAME=squash-devops-agent-robotframework
ENV SQUASH_TOKEN=defineYourTokenWithThisEnvVariableinDotEnvFile
ENV POLLING_DELAY=5
ENV LIVENESS_PROBE=60
ENV TZ=Europe/Paris
ENV PATH="${PATH}:/app/.local/bin/"
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime \
&& useradd --create-home --home-dir=/app squash
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y git --no-install-recommends && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
USER squash
RUN pip install --no-cache-dir \
opentf-tools \
opentf-agent \
robotframework \
allure-robotframework \
squash-tf-services
WORKDIR /app
ENTRYPOINT ["/bin/dash", "-c", "/app/.local/bin/opentf-agent --tags linux,robotframework --host $SQUASH_HOST --port $SQUASH_PORT --token $SQUASH_TOKEN --name $SQUASH_NAME --namespace $SQUASH_NAMESPACE --polling_delay $POLLING_DELAY --liveness_probe $LIVENESS_PROBE --verify true --debug"]
docker-compose.yml
Code : Tout sélectionner
version: '3.9'
services:
squash-postgres:
container_name: squash-postgres
image: ${POSTGRES_IMAGE}:${POSTGRES_RELEASE_TAG}
environment:
POSTGRESQL_DATABASE: ${DB_DATABASE}
POSTGRESQL_USERNAME: ${DB_USERNAME}
POSTGRESQL_PASSWORD: ${DB_PASSWORD}
volumes:
- squash-tm-postgresql-data:/var/lib/postgresql/data
labels:
- "traefik.enable=false"
networks:
- backend
restart: always
squash-tm:
container_name: squash-tm
depends_on:
- squash-postgres
environment:
SQTM_DB_TYPE: postgresql
SQTM_DB_HOST: "squash-postgres"
SQTM_DB_USERNAME: ${DB_USERNAME}
SQTM_DB_PASSWORD: ${DB_PASSWORD}
SQTM_DB_NAME: ${DB_DATABASE}
TZ: "Europe/Paris"
image: squashtest/squash-tm:${SQUASHTM_RELEASE_TAG}
labels:
- "traefik.enable=true"
- "traefik.http.routers.squash-tm.rule=Host(`host.domain.tld`) && PathPrefix(`/squash`)"
- "traefik.http.routers.squash-tm.entrypoints=websecure"
- "traefik.http.routers.squash-tm.tls=true"
- "traefik.http.routers.squash-tm.tls.certresolver=letsencrypt"
- "traefik.http.routers.squash-tm.service=squash-tm"
- "traefik.http.services.squash-tm.loadbalancer.server.port=8080"
- "traefik.http.services.squash-tm.loadbalancer.server.scheme=http"
# redirect web to websecure
- "traefik.http.routers.squash-tm-web.rule=Host(`host.domain.tld`) && PathPrefix(`/squash`)"
- "traefik.http.routers.squash-tm-web.entrypoints=web"
- "traefik.http.routers.squash-tm-web.middlewares=squash-tm-redirect-websecure"
- "traefik.http.middlewares.squash-tm-redirect-websecure.redirectscheme.scheme=https"
volumes:
- squash-tm-logs:/opt/squash-tm/logs
- ./trusted_keys/trusted_key.pub:/etc/squashtf/trusted_key.pub
- ./trusted_keys/gitlab_runner.pub:/etc/squashtf/gitlab_runner.pub
- ./squash-tm/plugins/community:/opt/squash-tm/plugins/community
networks:
- backend
restart: always
traefik:
container_name: traefik
image: traefik:${TRAEFIK_VERSION:-latest}
command:
- "--api.insecure=false"
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80" # http will be redirected to https
- "--entrypoints.websecure.address=:443" # https
- "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=acme-dns" # Choose and configure your provider
- "--certificatesresolvers.letsencrypt.acme.email=<admin email>"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" # This is where Traefik stores certificates (needs persistance)
ports:
- "80:80" # HTTP main entry point (for orchestrator)
- "443:443" # HTTPS main entry point (for squash-tm)
labels:
- "traefik.enable=true"
# dashboard
- "traefik.http.routers.dashboard.rule=Host(`host.domain.tld`) && PathPrefix(`/dashboard`, `/api`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls=true"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.middlewares=auth"
# command to get basicauth string : echo $(htpasswd -Bbn <user> <password>| sed -e s/\\$/\\$\\$/g)
- "traefik.http.middlewares.auth.basicauth.users=<user>:<hash>
# redirect web to websecure
- "traefik.http.routers.dashboard-web.rule=Host(`squash-tm.cloud-espace.si.c-s.fr`) && PathPrefix(`/dashboard`, `/api`)"
- "traefik.http.routers.dashboard-web.entrypoints=web"
- "traefik.http.routers.dashboard-web.middlewares=squash-tm-redirect-websecure"
- "traefik.http.middlewares.dashboard-redirect-websecure.redirectscheme.scheme=https"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt" # Holds acme auth (acmedns.json) and acme certificates (acme.json)
environment:
ACME_DNS_API_BASE: "https://acme-dns-server.domain.tld" # adapt your env for your provider (here : acme-dns)
ACME_DNS_STORAGE_PATH: "/letsencrypt/acmedns.json"
networks:
- backend
- frontend
restart: always
squash-orchestrator:
container_name: squash-orchestrator
image: squashtest/squash-orchestrator:${SQUASH_ORCHESTRATOR_RELEASE_TAG}
restart: always
volumes:
- "./trusted_keys/trusted_key.pub:/etc/squashtf/trusted_key.pub" # trusted key for squash-autom default namespace JWT token validation
- "./trusted_keys/gitlab_runner.pub:/etc/squashtf/gitlab_runner.pub" # trusted key for squash-devops namespace JWT token validation
- "./squash-orchestrator/trusted-authorities.csv:/app/trusted-authorities.csv" # declares squash-autom default namespace + squash-devops namespace
labels:
- "traefik.enable=true"
# receptionist
- "traefik.http.routers.receptionist.rule=PathPrefix(`/workflows`) && Method(`POST`)"
- "traefik.http.routers.receptionist.service=receptionist"
- "traefik.http.services.receptionist.loadbalancer.server.port=7774"
- "traefik.http.routers.receptionist.tls=true"
- "traefik.http.routers.receptionist.tls.certresolver=letsencrypt"
- "traefik.http.routers.receptionist.entrypoints=websecure"
# observer
- "traefik.http.routers.observer.rule=(Path(`/workflows`) || Path(`/channels`) || Path(`/namespaces`) || Path(`/channelhandlers`) || PathPrefix(`/workflows/{[a-z0-9-]+}/status`)) && Method(`GET`)"
- "traefik.http.routers.observer.service=observer"
- "traefik.http.services.observer.loadbalancer.server.port=7775"
- "traefik.http.routers.observer.tls=true"
- "traefik.http.routers.observer.tls.certresolver=letsencrypt"
- "traefik.http.routers.observer.entrypoints=websecure"
# qualitygate
- "traefik.http.routers.qualitygate.rule=PathPrefix(`/workflows/{[a-z0-9-]+}/qualitygate`)"
- "traefik.http.routers.qualitygate.service=qualitygate"
- "traefik.http.services.qualitygate.loadbalancer.server.port=12312"
- "traefik.http.routers.qualitygate.tls=true"
- "traefik.http.routers.qualitygate.tls.certresolver=letsencrypt"
- "traefik.http.routers.qualitygate.entrypoints=websecure"
# agents
- "traefik.http.routers.agents.rule=PathPrefix(`/agents`)"
- "traefik.http.routers.agents.service=agents"
- "traefik.http.services.agents.loadbalancer.server.port=24368"
- "traefik.http.routers.agents.tls=true"
- "traefik.http.routers.agents.tls.certresolver=letsencrypt"
- "traefik.http.routers.agents.entrypoints=websecure"
# eventbus
- "traefik.http.routers.eventbus.rule=PathPrefix(`/subscriptions`) || PathPrefix(`/publications`)"
- "traefik.http.routers.eventbus.service=eventbus"
- "traefik.http.services.eventbus.loadbalancer.server.port=38368"
- "traefik.http.routers.eventbus.tls=true"
- "traefik.http.routers.eventbus.tls.certresolver=letsencrypt"
- "traefik.http.routers.eventbus.entrypoints=websecure"
# killswitch
- "traefik.http.routers.killswitch.rule=PathPrefix(`/workflows/{[a-z0-9-]+}`)"
- "traefik.http.routers.killswitch.service=killswitch"
- "traefik.http.services.killswitch.loadbalancer.server.port=7776"
- "traefik.http.routers.killswitch.tls=true"
- "traefik.http.routers.killswitch.tls.certresolver=letsencrypt"
- "traefik.http.routers.killswitch.entrypoints=websecure"
environment:
OPENTF_AUTHORIZATION_MODE: JWT
OPENTF_TRUSTEDKEYS_AUTH_FILE: /app/trusted-authorities.csv # declares default namespace token + squash-devops namespace token
no_proxy: "squash-tm,.si.c-s.fr,localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
NO_PROXY: "squash-tm,.si.c-s.fr,localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
TZ: "Europe/Paris"
networks:
- backend
squash-autom-agent-robotframework:
container_name: squash-autom-agent-robotframework
depends_on:
- squash-orchestrator
build: squash-autom-agent-robotframework
environment:
SQUASH_NAME: "squash-autom-agent-robotframework"
SQUASH_HOST: "http://squash-orchestrator"
SQUASH_PORT: "24368"
SQUASH_TOKEN: ${SQUASH_TOKEN}
restart: always
labels:
- "traefik.enable=false"
networks:
- backend
volumes:
squash-tm-logs:
squash-tm-postgresql-data:
external: true
networks:
backend:
frontend:
squash-orchestrator/trusted-authorities.csv
Code : Tout sélectionner
/etc/squashtf/trusted_key.pub,Administrators,,"*"
/etc/squashtf/gitlab_runner.pub,squash-devops,,"squash-devops"
gitlab job logs
Code : Tout sélectionner
Running with gitlab-runner 15.1.0 (76984217)
Getting source from Git repository 00:01
Fetching changes with git depth set to 20...
Initialized empty Git repository
Created fresh repository.
Checking out 4fe48dae as main...
Skipping Git submodules setup
Executing "step_script" stage of the job script 01:14
$ mkdir -p ~/.opentf && cp $OPENTF_CONFIG ~/.opentf/config
$ AGENT_ID=$(opentf-ctl get agents|grep $SQUASH_NAME|cut -d',' -f1) && echo AGENT_ID=$AGENT_ID
AGENT_ID=f288736b-248c-446f-b673-036988982810
$ RUN_WORKFLOW=$(opentf-ctl run workflow squash-workflow-ett.yml) && echo RUN_WORKFLOW=$RUN_WORKFLOW
RUN_WORKFLOW=Workflow 5b570b25-6029-4ffb-a861-04b0af21b99b is running.
$ WORKFLOW_ID=$(echo $RUN_WORKFLOW | cut -d' ' -f2) && echo WORKFLOW_ID=$WORKFLOW_ID
WORKFLOW_ID=5b570b25-6029-4ffb-a861-04b0af21b99b
$ opentf-ctl get workflow $WORKFLOW_ID --watch
Workflow test
(running in namespace 'squash-devops')
[2023-07-03T17:34:45] [job 70bde7ee-945d-4f05-885b-b3f45d69f677] Running function iteration
[2023-07-03T17:34:47] [job 5166760f-0565-4236-89b4-1a7736854684] Requesting execution environment providing ['robotframework'] in namespace 'squash-devops' for job 'squashTMJob-0'
[2023-07-03T17:35:09] [job 5166760f-0565-4236-89b4-1a7736854684] Cloning into 'squash-devops'...
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] ==============================================================================
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Data Driven :: Example test cases using the data-driven testing approach.
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] ==============================================================================
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Addition | PASS |
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] ------------------------------------------------------------------------------
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Subtraction | PASS |
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] ------------------------------------------------------------------------------
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Multiplication | PASS |
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] ------------------------------------------------------------------------------
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Division | PASS |
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] ------------------------------------------------------------------------------
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Calculation error | PASS |
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] ------------------------------------------------------------------------------
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Data Driven :: Example test cases using the data-driven testing ap... | PASS |
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] 5 tests, 5 passed, 0 failed
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] ==============================================================================
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Output: /app/5166760f-0565-4236-89b4-1a7736854684/output.xml
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Log: /app/5166760f-0565-4236-89b4-1a7736854684/log.html
[2023-07-03T17:35:17] [job 5166760f-0565-4236-89b4-1a7736854684] Report: /app/5166760f-0565-4236-89b4-1a7736854684/report.html
[2023-07-03T17:35:55] [job 5166760f-0565-4236-89b4-1a7736854684] Releasing execution environment for job 'squashTMJob-0'
Workflow completed successfully.
$ opentf-ctl get workflow $WORKFLOW_ID > squash-tm.log
$ opentf-ctl delete agent $AGENT_ID
Agent f288736b-248c-446f-b673-036988982810 de-registered.
$ opentf-ctl get qualitygate $WORKFLOW_ID --mode=strict
Workflow 5b570b25-6029-4ffb-a861-04b0af21b99b successfully passed the qualitygate using mode strict.
Uploading artifacts for successful job 00:01
Uploading artifacts...
squash-tm.log: found 1 matching files and directories
Uploading artifacts as "archive" to coordinator... 201 Created id=55227 responseStatus=201 Created token=64_p47yw
Cleaning up project directory and file based variables 00:00
Job succeeded
Tool to check new releases
Code : Tout sélectionner
#!/bin/bash
get_latest () {
url=$1 # needs hub.docker.com URL
result=$(curl -sL ${url} \
| jq '.results[].name' -r \
| grep -vE "snapshot|latest|nightly|2021|2022|2023" \
| sort --version-sort \
| tail -1 )
echo ${result}
}
echo "# ------------------------------"
echo "# Latest dockerhub release tags:"
echo "# ------------------------------"
echo LATEST_SQUASHTM_RELEASE_TAG=$( get_latest https://hub.docker.com/v2/repositories/squashtest/squash-tm/tags?page_size=1000 )
echo LATEST_SQUASH_ORCHESTRATOR_RELEASE_TAG=$( get_latest https://hub.docker.com/v2/repositories/squashtest/squash-orchestrator/tags?page_size=1000 )
echo LATEST_PGADMIN_RELEASE_TAG=$( get_latest https://hub.docker.com/v2/repositories/dpage/pgadmin4/tags?page_size=1000 )
echo LATEST_POSTGRES_RELEASE_TAG=$( get_latest https://hub.docker.com/v2/repositories/bitnami/postgresql/tags?page_size=1000 )
echo
echo "# -------------------------------------"
echo "# Latest Squash-TM plugins release tag:"
echo "# -------------------------------------"
echo LATEST_SQUASHTM_PLUGINS_RELEASE_TAG=$( curl -sL https://nexus.squashtest.org/nexus/repository/public-releases/tm/compatibility-version-plugins.json|jq '.[]|.squashTMVersion' -r|sort -V|tail -1 )
echo
echo "# -------------------------"
echo "# Latest PyPi release tags:"
echo "# -------------------------"
for pypack in opentf-tools opentf-agent robotframework allure-robotframework squash-tf-services
do
echo "${pypack}==$(curl -s https://pypi.org/rss/project/${pypack}/releases.xml | yq -p xml '.rss.channel.item[].title'|sort -V|tail -1)"
done
Tool to download plugins in docker-compose folder wich is mounted in a volume (adapt to your need or call it in squash-tm entrypoint override)
Code : Tout sélectionner
#!/usr/bin/bash
source ../.env # reads docker compose env vars
if [ -z "${SQUASHTM_PLUGINS_RELEASE_TAG}" ]
then
echo '[ERROR] SQUASHTM_PLUGINS_RELEASE_TAG variable misconfiguration !'
exit 1
else
echo "[INFO] SQUASHTM_PLUGINS_RELEASE_TAG=$SQUASHTM_PLUGINS_RELEASE_TAG"
fi
compatibility_version_plugins_url="https://nexus.squashtest.org/nexus/repository/public-releases/tm/compatibility-version-plugins.json"
compatibility_version_plugins_json=$( curl -s ${compatibility_version_plugins_url} )
return_code=$?
if [ ${return_code} -eq 0 ]
then
echo [INFO] Fetched squash-tm compatibility-version-plugins.json
else
echo [ERROR] Fetching squash-tm compatibility-version-plugins.json
exit 1
fi
#my_unused_plugins='"api-rest","bugzilla","mantis","xsquash4jira"' (api-rest,report-books-editable,qualitativecoverage plugins are already in squash-tm image)
compatibility_version_plugins_community_download_links=$( \
echo ${compatibility_version_plugins_json} \
| jq -r --arg SQUASHTM_PLUGINS_RELEASE_TAG "$SQUASHTM_PLUGINS_RELEASE_TAG" --arg SQUASH_COMMUNITY_PLUGINS "$SQUASH_COMMUNITY_PLUGINS" ' .[]
| select( .squashTMVersion==$SQUASHTM_PLUGINS_RELEASE_TAG).plugins[]
| select( .id | IN("gitlab","report-books-pdf","result-publisher-community","scm-git","squashautom-community","testplan-retriever-community","xsquash4gitlab") )
| select( .isPremium=="false").downloadTarGz'
)
mkdir -p plugins/community
cd plugins/community
echo [INFO] Downloading plugins archives...
for link in ${compatibility_version_plugins_community_download_links}
do
wget -nv ${link} 2>&1
done
echo [INFO] Extracting and cleaning plugins archives...
for targz in *.tar.gz
do
echo ${targz}
tar xzf ${targz} && rm ${targz}
done