Skip to content
Français
Français
medium

dsoxlab: files, environment variables and exit codes

15 min read

Read this page in French

dsoxlab keeps its state in two places, and nowhere else: in the catalogue you are playing, for progress, and under your home directory, for the log, the caches and the Terraform state. This reference page lists every file, every environment variable and every exit code of version 0.2.5, with the gesture each one calls for. It is written for anyone who wants to save their progress, find a log after a failure, or call dsoxlab from a script. Those locations are held in step with the code by a test of the project: they do not drift silently.

What you will learn

  • Locate the progress database and the context, which live in the catalogue.
  • Find the log, the caches and the Terraform state under your home directory.
  • Set the language, the provider or the waiting delay through an environment variable.
  • Interpret every exit code, and tell a "no" from a "cannot".
  • Use the --json output without depending on a translated label.

In the catalogue you are playing

Progress belongs to the catalogue, not to the machine. Two catalogues side by side keep separate histories, copying a catalogue's directory copies your history with it, and deleting it deletes its own. That is what makes a backup trivial: a single file to copy.

PathContentWritten by
<catalogue>/.dsoxlab.dbSQLite database: the scores (results) and the hints taken (hint_requests)check, submit, hint
<catalogue>/.dsoxlab-context.jsonActive section, level, language, lab, target and provider, plus the reading position in the courseuse, run, course
<catalogue>/ssh/id_ed25519 and .pubThe SSH key pair deployed on the machines of vm labsinstructor bootstrap

The first two belong in the catalogue's .gitignore, and the published catalogues do so. The key pair is never committed either: the private half must not be, and the public one is useless without it, hence the rule of one pair per clone, generated on every machine that provisions.

Under your home directory

Everything else follows the XDG conventions and moves with the three matching variables. <catalog-id> is the repo.id of the catalogue's meta.yml, which is why two clones of the same catalogue share a single Terraform state and a single lock.

PathContentMoved by
~/.local/state/dsoxlab/dsoxlab.logFull log of every command, whatever the verbosityXDG_STATE_HOME
~/.local/state/dsoxlab/<catalog-id>/terraform/<provider>/Terraform working directory and state, one per providerXDG_STATE_HOME
~/.local/state/dsoxlab/<catalog-id>/cloud-init/cloud-init templates copied out of the tool for provisioningXDG_STATE_HOME
~/.local/state/dsoxlab/<catalog-id>/dsoxlab.lockWrite lock of this catalogueXDG_STATE_HOME
~/.local/state/dsoxlab/<catalog-id>/labs/Fingerprints of the working directory, taken by run, to tell a ready lab from one in progressXDG_STATE_HOME
~/.local/state/dsoxlab/catalogue-actifIdentifier of the active catalogueXDG_STATE_HOME
~/.cache/dsoxlab/<catalog-id>/inventory.jsonGenerated Ansible inventoryXDG_CACHE_HOME
~/.cache/dsoxlab/<catalog-id>/ssh_configGenerated OpenSSH configuration for the lab hostsXDG_CACHE_HOME
~/.cache/dsoxlab/version-check.jsonLast version seen on PyPI, and whenXDG_CACHE_HOME
~/.local/share/dsoxlab/demo/Demonstration catalogue installed by dsoxlab demoXDG_DATA_HOME
~/.local/share/dsoxlab/catalogs/Catalogues installed by catalog add, one subdirectory per identifierXDG_DATA_HOME
~/.ssh/config.d/<catalog-id>.confSSH fragment for the lab hosts, so that ssh, scp and your IDE reach them by namenone

Two of those files are caches, and losing them only costs a regeneration: inventory.json and ssh_config are rebuilt by the next command that needs them. Anything that would point at the generated ssh_config, an Include or an IDE profile, must therefore tolerate its disappearance. The fragment under ~/.ssh/config.d/ is the stable one, provided ~/.ssh/config carries an Include of that directory before any Host block: dsoxlab does not write that line for you, and warns at every provision while it is missing.

The Terraform state is deliberately outside the catalogue: a state file placed in a lab repository ends up committed, and a committed state lies. Shell completion writes outside both families, once: ~/.zfunc/_dsoxlab plus a line in ~/.zshrc, or ~/.bash_completion.d/dsoxlab plus a line in ~/.bashrc.

What does not exist

Three paths have been documented in the past and are listed here so that nobody looks for them again. There is no ~/.config/dsoxlab/config.yaml, and no user configuration file at all: what can be set goes through the contract of the meta.yml, the active context of dsoxlab use, or an environment variable. There is no ~/.local/share/dsoxlab/progress.db: scores live in <catalogue>/.dsoxlab.db. And XDG_CONFIG_HOME is read nowhere: the three XDG variables cited above are the only ones dsoxlab honours.

Environment variables

An environment variable lasts for one call, or for one shell session, and takes precedence over the catalogue's context file. It is the right tool for a one-off try, a command in a script, or a delay to lengthen on a slow machine.

VariableEffect
LAB_HOMERoot of the catalogue to play, instead of automatic detection
DSOXLAB_LANGDisplay language, en or fr, wins over the context file
DSOXLAB_PROVIDERInfrastructure provider, wins over dsoxlab use --provider
DSOXLAB_TARGETDefault target of a vm lab, when the session sets none
DSOXLAB_PAGERPager for course and challenge, then PAGER, default less -R
DSOXLAB_LOGDSOXLAB_LOG=debug is the same as -vv
DSOXLAB_HOST_READY_TIMEOUTSeconds to wait for a freshly provisioned host, default 180
DSOXLAB_NO_UPDATE_CHECKSet to 1, silences the daily notice of a new version
DSOXLAB_OUTSCALE_PROFILE, DSOXLAB_AWS_PROFILECredentials profile of those providers

Two more are set by dsoxlab for the tests of a lab to read, and are not meant to be set by hand: DSOXLAB_TARGET_HOST, the host the tests must inspect, which is how a multi-distribution lab validates the chosen target, and DSOXLAB_LAB_SESSION, the lab identifier inside the session opened by run.

The exit codes

An exit code is the hardest contract the tool exposes: a JSON document can gain a field, a translated sentence can change a word, but a code, once a script reads it, cannot move without breaking that caller silently. They all live in the same place in the code, in an enumeration, and two tests keep the table honest: no value is used twice, and every code must appear in the documentation.

CodeWhat it meansThe gesture it calls for
0The command did what was askedNothing
1The command ran, and the answer is no: unknown lab identifier, failing test, host that does not answer, no active contextRead the message; the answer is about your work, not your installation
2The command could not run: infrastructure not provisioned, provider not packaged, declared fixture missing, required restore point unobtainablePrepare something; this is not a mistake in your work
3Terraform is not installed, so provision and destroy have no way to actInstall it
4Terraform answered, and it failedRead its output; dsoxlab names the causes it recognises, such as a full or missing storage pool
5A provision left orphan machines, defined on the hypervisor but absent from the state, or found some before startingRun the virsh undefine line the message prints
6A destroy could not remove those orphansRemove them by hand, then run destroy again
7Another dsoxlab command already holds the lock of this catalogueRetry; it is the only code where retrying is right, and the message names the process holding it
8A provision returned without every targeted host answeringdsoxlab status says which one and why; often more time or more vCPU
9doctor --strict: a required check failed, and that is establishedRepair what the table names
10doctor --strict: a required check could not be measuredMeasure again; nothing is concluded, so nothing is validated
127An expected executable is not in the PATHInstall it; 127 is the code the shell itself returns here
130A Ctrl-CThe message gives the gesture to resume

Three distinctions are deliberate, and they are what justifies this table. 7 is the only code to retry on, because its cause is temporary by nature; every other one describes a state that will not change on its own. 9 and 10 are separate because the gestures differ, repair against measure again, and when both coexist, 9 wins: a certainty outweighs an ignorance. 1 and 2 split "no" from "cannot": a check that fails exits 1, the tool has worked; a run whose fixture is missing exits 2, nothing was measured.

doctor without --strict exits 0 whatever it finds, deliberately: for a human, a diagnosis is not a failure, and the verdict lives in the ok field of --json. It is --strict, not --json, that turns the diagnosis into 9 or 10.

The machine output, in three rules

Everything dsoxlab prints is made for eyes, tables whose width follows the terminal, colours, progress bars, and it is made to move. --json renders a document instead, and three rules govern it. Standard output carries the document and nothing else: the context banner, the tips and the update notice go to standard error. Every document carries a schema field, its first, whose current value is 1. And a verdict is read from a key and a stable token, key, state, kind, status, cause, never from a translated label placed next to it for display.

One consequence deserves to be stated on its own: --json changes the shape of the output, never the verdict nor the exit code. A check on a failing lab exits 1 with or without it, and prints its document first, so that a caller receiving a non-zero code can read what went wrong. Read the exit code first, the document second.

Terminal window
dsoxlab list-labs --json | jq '.count'
dsoxlab doctor --json --strict; echo "code: $?"
dsoxlab check premiers-pas --json | jq '.check.ok'

doctor --json --fix is refused, and says so on standard error: the remediation commands write to standard output, and the document would become unreadable. Read the diagnosis first, act second.

Key points

  • Progress lives in <catalogue>/.dsoxlab.db, per catalogue: one file to copy to save it.
  • The full log is always in ~/.local/state/dsoxlab/dsoxlab.log, and the Terraform state under ~/.local/state/dsoxlab/<catalog-id>/.
  • There is no user configuration file: contract, active context or environment variable.
  • A DSOXLAB_* variable takes precedence over the context file, for one call.
  • 7 is retried, 9 is repaired, 10 is measured again; 1 is a "no", 2 a "cannot".
  • --json changes the shape, never the verdict nor the code; read the code first.

Next steps

Is this site useful to you?

Fewer than 1% of readers support this site.

I maintain this site for free, with no ads, no ad profiling and no account to create. Any support, even a symbolic one, helps cover hosting and keeps these resources free. Thank you for the help.

The form does not show? Open Ko-fi in a new tab.

Subscribe and follow my DevSecOps work on LinkedIn