container-do

module
v1.2.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 30, 2024 License: Apache-2.0

README

license release GitHub release date Test CodeQL

container-do

Run project-level CLI tools in (Linux) containers. In particular,

  • use tools not available on your platform,
  • avoid managing version conflicts of tooling,
  • persist and share specific setups, and
  • minimize the blast radius of potential mishaps.

A more eloquent reasoning for using containers as development environment can be found in the documentation of kudulab/dojo.

Premise
  1. You have a "project directory", which we take to mean a directory which contains all files pertaining to the task at hand, and nothing else.
  2. You need a certain suite of tools (at certain versions) to perform this task.
  3. A compatible container image exists for this suite.

Install

Download the binary matching your OS and put it on the PATH.

As an alternative, you can compile from the sources like so:

go get github.com/reitzig/container-do/cmd/container-do

Find the binary at $GOPATH/bin.

Use

Prerequisites:

  • Docker installed and user can run docker.
  • Container has sh.

There are three special commands:

  • container-do --help -- print usage instructions.
  • container-do --init -- create config file (template).
  • container-do --kill -- kill the configured container (if it exists).

All other calls will be passed directly to the configured container. For instance:

container-do npm install

will run npm install inside the container and, more specifically, through the default SHELL in that container.

By default, container-do will try to stay out of your way and allow you to focus on the normal command output. However, you can enable rather more verbose logging by setting environment variable CONTAINER_DO_LOGGING to debug.

Config File

At the very least, you will have to tell container-do which image to use. Create a file ContainerDo.toml with the following content:

[container]
image = "my-image"

Alternatively, run container-do --init to get a full template. Here is a full list of the optional values:

  • container.os_flavor (Default: auto-detect)

    For some commands run in the container, we need to know which flavor of Linux it runs. While we will attempt to detect that automatically, this induces a slight performance over head (and may fail). Set to one of debian, fedora, alpine, gnu/linux, or busybox.

  • container.name (Default: ${project_dir}-do)

  • container.work_dir (Default: $WORKDIR)

    Use to override the working directory defined in the container image. Set to an absolute path.

  • container.mounts (Default: [.:$WORKDIR] / [])

    Unless the container working directory is /, the default is a bind-mount from the host working directory to it. Override with entries using the Docker --volume syntax; unlike docker, we will resolve relative host paths.

    Note: You can also use named volumes!

  • container.ports (Default: [])

    Given a list of port mapping strings, we publish the ports as specified.

  • container.keep_alive (Default: 15m)

    The duration to keep the container alive for after the last command was run in it. Set to any value compatible with Go time.

  • container.keep_stopped (Default: false)

    By default, we remove the container after it stops. Set to true to have the container stick around.

  • container.environment (Default: none)

    Set environment variables of the container.
    If a value is of the form "$VARIABLE_NAME", it will be replaced with the value of the thus named environment variable of the host, or the empty string if it is not set.

  • run.setup -- run once after container creation
    run.before -- run once before each command
    run.after -- run once after each (successful) command

    Run pre-defined shell commands, each section specified by:

    • run._.attach (Default: false)

      Set true in order to attach the current shell to the command runs.

    • run._.user (Default: $USER)

      Override the default container user to run the commands.

    • run._.script_file (Default: none)

      Set to a script file in (relative to container.work_dir). Run before any of the commands in the same section.

    • run._.commands (Default: [])

      Set to a list of shell commands run one after the other, so long as they are successful.

  • copy.setup -- copy files into the container after its creation, but before run.setup.
    copy.before -- copy files into the container before each command, and before run.before.
    copy.after -- copy files out of the container after each (successful) command, and after run.after.

    Note: Each of these can occur multiple times; include them as [[copy.setup]].

    • copy._.files (Default: none)

      A list of file names and/or glob strings; all matching files will be copied. Directories will be copied recursively.

    • copy._.to (Default: container.work_dir / .)

      Name of the target file or directory. The target will be a directory unless there is a single source file and the target file name does not end in /. If the target directory does not exist, it will be created.

    Relative paths are relative to the working directory (for copy.setup, copy.before) resp. container.work_dir (for copy.after).

Note: When errors happen during run.setup or copy.setup, we can not recover graciously. Therefore, we immediately kill the container so _.setup will be retried before the next command.

Examples

Explore some use cases:

Short ADRs

  • Why containers?

    While this is not about running services, containers seem a prudent way to isolate versioned tooling from the host system without too much overhead. Also, the approach eliminates the need for tools specific to a certain ecosystem such as venv, rvm, etc.

  • Why Go?

    Efficient binaries seem prudent here. Also, Go seems to be prevalent in the OCI space. If we were to use docker/client or libpod as libraries, those are written in Go.

  • Why TOML?

    Comparing to the most common alternatives, TOML seems to provide a better trade-off between expressiveness, cleanliness and usability than either of INI, JSON, YAML.

  • Why not use docker/client resp. libpod as libraries?

    That would mean higher maintenance debt (security patches etc.) and put the duty of ensuring compatibility with user systems on us.

Other Tools

  • kudulab/dojo -- Similar vision. Requires custom images but handles more complex setups.
  • batect/batect -- More of a build tool than an environment specification.

Acknowledgements

Parts of this project were created during 20% time graciously provided by codecentric. Thank you!

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL