Leviathan consists of 3 containers, Client, Core, and Worker
stateDiagram-v2
Client --> Core
Core --> Client
Core --> Worker
Worker --> Core
Worker --> DUT
DUT --> Worker
The Client performs the following functions:
- acts as the front end - users use the client to trigger test jobs
- finds a worker with the matching DUT type in a balena fleet of workers
- sends tests and test artifacts to the
Corevia a websocket connection - collects and reports logs from the
Core - it is configured using a
config.js, instructions here
The Core container is the container that runs the tests that are sent to it by the Client container.
- awaits tests and artifacts from client
- contains helper functions to be used in the tests. Notably this includes bindings to interact with the
Workerand SSH helpers to interact with the DUT - tests must be in a specific format to be executed by the core. See the e2e suite and the guide to writing tests
- checks that a test should be executed before running it, by checking its contract against the device type contract
The Worker is the container that provides a common interface to the DUT's "physical" interfaces.
- It has a HTTP webserver interface
- Contains endpoints to power on/off the DUT, flash the DUT, set up the networking environment, capture video output
- Can be used to interface to a physical DUT via a
testbotorautokit, or a virtualised DUT viaqemu, using the same interface - The worker can be found in its own github repo
When being used to interface with a physical DUT, the worker container will be on a testbot or autokit balena device, in a balena fleet.
- The
CoreandClientcontainers always are on a local or cloud host (i.e laptop, jenkins servers), while theWorkercontainer is on a balena device - The core container accesses the worker container http api via the balena devices public url, as it and the
Workerare on different hosts - The worker creates a network AP for the DUT to connect to, and provides its internet access. However this results in the DUT and the
Core, which is running the tests, being on different networks - The core container, running the tests, can access the DUT via SSH.
- Due to the
Coreand DUT being on different networks, this requires theCoreto use theWorkeras a jumping point - To set up a tunnel to the DUT, via the
Worker, theCorewill SSH into theWorkervia the balena cloud proxy - It will then SSH from the worker into the DUT, and set up port forwarding to forward port
22222of the DUT (the ssh port on a balena device), to an unused port on the worker - The
Corewill then set up port forwarding of theWorkerport that port22222of the DUT was mapped to, to port22222of theCore(itself) - This allows the
Coreto SSH into the DUT via this tunnel, atlocalhost:22222 - As we are using balena cloud proxy and therefor can't forward the ssh agent of the
Core, SSH keys required to access the DUT must also be sent from theCoreto theWorker. - In summary - a tunnel is set up between
Coreand the DUT via theWorker
- Due to the
When being used with a virtual DUT, the Worker container will be created on the same host as the Client and Core, and all be connected to the same docker bridge network.
The Worker container contains all QEMU dependencies, and will create a QEMU based DUT on demand.
- The worker container creates a bridge interface within it for the QEMU device to use
- iptables rules a DHCP server are set up
- an ip route is created in
Coreto access the DUT ip address, via theWorkerip address (on the docker bridge network thatCoreandWorkershare). The default docker bridge address range is used for this bridge currently. - The
Corecan thus directly SSH into theDUT, using theDUTip address. No tunnelling is required, no SSH keys must be sent to the worker