Skip to content

Squash Orchestrator

Squash Orchestrator is the distributed execution component of the Squash stack. It receives jobs from Squash TM (signed JWT RS512), routes them via SSH to the test runner, collects results, and returns them to Squash TM for aggregation into campaigns.

Source of truth

This page is drawn from the perfshop-orchestrator block of the compose files and the four bind-mounted YAML configuration files: squash-orchestrator/pools.yaml, sshee.yaml, arranger.yaml, squashtf.yaml.

Architecture — OpenTestFactory

Squash Orchestrator is a reference implementation of the OpenTestFactory (OTF) framework developed by Squashtest and Henix. The framework is modular: several "services" (eventbus, sshee, arranger, workflow) communicate with each other through an internal event bus, and each service has its own configuration.

flowchart LR
  STM["perfshop-testmgmt<br/>(Squash TM)"]

  subgraph ORCH["perfshop-orchestrator"]
    direction TB
    EB(["eventbus"])
    AR["arranger<br/>(receives HTTP jobs)"]
    WF["workflow<br/>(runs the step sequence)"]
    SSHEE["sshee<br/>(SSH executor engine)"]
    AR --> EB
    WF --> EB
    SSHEE --> EB
    EB --> WF
    EB --> SSHEE
  end

  TR["perfshop-test-runner<br/>(OpenSSH server)"]

  STM -->|"POST /jobs<br/>JWT RS512"| AR
  SSHEE -->|"SSH :22<br/>user=root password=perfshop"| TR

The perfshop-orchestrator service

perfshop-orchestrator:
  image: squashtest/squash-orchestrator:latest
  container_name: perfshop-orchestrator
  depends_on:
    perfshop-testmgmt:
      condition: service_healthy
    perfshop-test-runner:
      condition: service_started
  environment:
    - SQUASH_JWT_SECRET=${SQUASH_JWT_SECRET}
    - SQUASH_TM_URL=http://perfshop-testmgmt:8080/squash
    - SQUASH_TM_USER=${SQUASH_ADMIN_LOGIN:-admin}
    - SQUASH_TM_PASSWORD=${SQUASH_ADMIN_PASSWORD:-Squash2026*}
  volumes:
    - ./squash-orchestrator/pools.yaml:/etc/squashtf/pools.yaml:ro
    - ./squash-orchestrator/sshee.yaml:/etc/squashtf/sshee.yaml:ro
    - ./squash-orchestrator/arranger.yaml:/etc/squashtf/arranger.yaml:ro
    - ./squash-orchestrator/squashtf.yaml:/etc/squashtf/squashtf.yaml:ro
    - ./squash-seed/trusted_key.pub:/etc/squashtf/trusted_key.pub:ro

Environment variables

Variable Role
SQUASH_JWT_SECRET Same secret as perfshop-testmgmt — used to verify JWTs signed by Squash TM
SQUASH_TM_URL Internal URL of Squash TM (for result callbacks)
SQUASH_TM_USER / _PASSWORD Account used to post results back to Squash TM

The four YAML files

pools.yaml — execution pool definition

pools:
  default:
    - host: perfshop-test-runner
      port: 22
      username: root
      password: perfshop
      tags:
        - linux
        - robotframework
      namespaces: "default"

A single pool is defined, named default, containing a single target: perfshop-test-runner on SSH port 22 with user root and password perfshop.

Field Value Effect
host perfshop-test-runner Internal Docker DNS
port 22 Standard SSH port of the test runner container
username: root / password: perfshop Credentials defined in the test runner Dockerfile
tags [linux, robotframework] Routing tags — a job declaring runs-on: robotframework will be routed to this pool
namespaces: "default" Forces registration in the "default" namespace that the Java agentchannel uses to route workflows

SSH password authentication

The test runner accepts SSH authentication by password (hard-coded in the Dockerfile). This is acceptable because the container is not exposed externally (port 22 only on the internal Docker network), but would not be suitable for public exposure. The Dockerfile documents this explicitly: "Root access with password 'perfshop' — pedagogical use only".

sshee.yaml — SSH Executor Engine

apiVersion: opentestfactory.org/v1alpha1
kind: SSHServiceConfig
current-context: default
contexts:
- context:
    port: 443
    host: 127.0.0.1
    ssl_context: adhoc
    logfile: sshee.log
    eventbus:
      endpoint: https://127.0.0.1:38368
      token: reuse
    targets: [default]
  name: default
- context:
    port: 7786
    host: 127.0.0.1
    ssl_context: disabled
    trusted_authorities:
    - /etc/squashtf/*
    logfile: sshee.log
    enable_insecure_login: true
    eventbus:
        endpoint: http://127.0.0.1:38368
        token: reuse
    targets: [default]
  name: allinone
- context:
    port: 9365
    host: 127.0.0.1
    ssl_context: disabled
    # ...
  name: insecure

Three contexts are declared:

Context Port SSL Usage
default 443 adhoc (self-signed certificate generated at startup) Primary mode, HTTPS self-signed on eventbus https://127.0.0.1:38368
allinone 7786 disabled "All-in-one" mode where eventbus, arranger, and sshee run in the same process with SSL disabled; trusted_authorities reads the public key from /etc/squashtf/*
insecure 9365 disabled Explicit insecure mode, for debugging

The connection between sshee and the internal eventbus goes through local HTTP (127.0.0.1) — this is intra-container loopback, not the Docker network. All OpenTestFactory sub-services run in the same container.

arranger.yaml — job scheduler

apiVersion: opentestfactory.org/v1beta2
kind: ServiceConfig
current-context: default
contexts:
- context:
    port: 443
    host: 127.0.0.1
    ssl_context: adhoc
    logfile: arranger.log
    eventbus:
      endpoint: https://127.0.0.1:38368
      token: reuse
    max_jobs: 1024
    max_job_steps: 10240
    max_workers: 4
    default_timeout_minutes: 360
  name: default
# ... similar allinone and insecure contexts

The arranger is the HTTP frontend that receives jobs from Squash TM. It exposes the API that accepts POST /jobs requests and places them onto the internal eventbus for processing by the workflow engine.

Parameter Value Effect
max_jobs 1024 Cap on the number of jobs simultaneously pending
max_job_steps 10240 Cap on the total number of steps in a job
max_workers 4 Number of concurrent workers dequeuing jobs
default_timeout_minutes 360 (6 hours) Default timeout per job
pending_timeout_minutes (allinone context) 5 Cap on the wait time before execution

squashtf.yaml — workflow engine configuration

eventbus: python3 -m opentf.core.eventbus
services:
  - ${{ CORE }}/core
  - ${{ CORE }}/qualitygate
plugins:
  - ${{ CORE }}/plugins
  - /app/plugins
  - ${{ CORE }}/../squash/orchestrator
  - /app/tm-connector
aggregated:
- actionprovider
- cucumber
- cypress
- robotframework
- junit
- postman
- skf
- soapui
- playwright
disabled:
  - HelloWorld
  - localpublisher
  - dummygenerator
  - agentchannel

This is the manifest of the workflow engine that ties all plugins together.

9 aggregated plugins

Plugin Usage
actionprovider Base plugin exposing unit actions
cucumber Cucumber BDD tests
cypress Cypress E2E tests
robotframework Robot Framework tests — the main plugin used by PerfShop
junit JUnit tests
postman Postman/Newman tests
skf Squash Keyword Framework (keyword-driven tests)
soapui SoapUI tests
playwright Playwright E2E tests

All plugins are present in the official image — PerfShop does not curate them, this is the vendor's standard configuration. Students can therefore experiment with any of them without reconfiguration.

4 disabled plugins

Disabled plugin Why?
HelloWorld Example plugin with no value
localpublisher Local publisher, replaced by the Squash TM connection
dummygenerator Fake data generator, useless
agentchannel CRITICAL — the Java agentchannel answers "not found" before sshee and cancels the workflow. Documented at the top of the file. Without this disable, no job executes.

agentchannel disabled — real-world case

The comment at the top of squashtf.yaml is explicit: "agentchannel disabled: it answers 'not found' before sshee and cancels the workflow. sshee handles SSH channels directly via sshee.yaml". This is an interesting pedagogical discovery — it shows how a bad dispatch order in an event bus can silently block an entire system, and how explicitly disabling the offender fixes the problem.

The JWT RS512 job format

Squash TM signs its jobs in RS512 (RSA SHA-512) with the private key trusted_key.pem (stored in the Squash TM container); the orchestrator verifies with trusted_key.pub (bind-mounted at /etc/squashtf/trusted_key.pub).

sequenceDiagram
  autonumber
  participant STM as Squash TM
  participant AR as orchestrator<br/>arranger
  participant SSHEE as orchestrator<br/>sshee
  participant TR as perfshop-test-runner

  STM->>STM: Generates jobspec<br/>(script, repo, commit, env vars)
  STM->>STM: Signs with trusted_key.pem<br/>(RS512)
  STM->>AR: POST /jobs<br/>Authorization: Bearer <JWT>

  AR->>AR: Verifies JWT with trusted_key.pub
  alt Invalid JWT
    AR-->>STM: 401 Unauthorized
  else Valid JWT
    AR->>AR: Places on eventbus (pending)
    AR-->>STM: 200 Accepted, job_id=...
  end

  AR->>SSHEE: Dispatch job (pool=default)
  SSHEE->>TR: SSH root@perfshop-test-runner<br/>(password perfshop)
  SSHEE->>TR: SCP script.robot
  SSHEE->>TR: Exec robot script.robot
  TR-->>SSHEE: Exit code + stdout/stderr
  SSHEE->>AR: Result (through eventbus)
  AR->>STM: POST /squash/api/rest/executions/...<br/>{status:PASS|FAIL, log:...}

Key points:

  • Mutual authentication: Squash TM proves its identity with its private key, the orchestrator verifies with the public key. Without this bind-mounted public key, no job can be accepted.
  • SSH channel: once the job is accepted, the orchestrator opens a real SSH connection to the test runner (no event bus, no external queue). It's simple and robust.
  • Result return: the orchestrator uses SQUASH_TM_URL + SQUASH_TM_USER/PASSWORD to post results back to the Squash TM REST API.

Startup order

The orchestrator depends on two services:

flowchart LR
  STM[("perfshop-testmgmt<br/>healthy")] --> ORCH
  TR[("perfshop-test-runner<br/>started")] --> ORCH
  ORCH["perfshop-orchestrator<br/>(starts)"]
  • perfshop-testmgmt: service_healthy — to be able to post results back
  • perfshop-test-runner: service_started — the orchestrator does not wait for any particular healthcheck of the runner (it will open an SSH connection to it when needed)

Volumes

Volume Mount point Content
./squash-orchestrator/pools.yaml (bind RO) /etc/squashtf/pools.yaml SSH pool definitions
./squash-orchestrator/sshee.yaml (bind RO) /etc/squashtf/sshee.yaml SSH executor engine contexts
./squash-orchestrator/arranger.yaml (bind RO) /etc/squashtf/arranger.yaml Arranger configuration (max_jobs, max_workers)
./squash-orchestrator/squashtf.yaml (bind RO) /etc/squashtf/squashtf.yaml Workflow engine manifest (plugins)
./squash-seed/trusted_key.pub (bind RO) /etc/squashtf/trusted_key.pub RSA public key for verifying JWTs

Ports

The perfshop-orchestrator service exposes no port to the host — it is only reachable from the internal Docker network. HTTPS port 443 (arranger) and HTTP port 38368 (internal eventbus) are intra-container loopback.

Going further