Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Generating a configuration reference #105

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 166 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,42 @@ use std::collections::BTreeMap;
use std::fs::File;
use std::path;

/// Specify the shell(s) for floki to run.
///
/// Floki runs the commands under [`init`][init] in an
/// "outer" shell, and then drops the user into an "inner"
/// shell.
///
/// [init]: ./struct.FlokiConfig.html#structfield.init
///
/// ---
///
/// Back to:
/// - [Floki Config](./struct.FlokiConfig.html#structfield.shell)
///
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum Shell {
/// Provide a string to specify both shells as the
/// same.
///
/// e.g.
///
/// ```yaml
/// bash
/// ```
///
Shell(String),

/// Specify both shells separately.
///
/// e.g.
///
/// ```yaml
/// inner: bash
/// outer: sh
/// ```
///
TwoShell { inner: String, outer: String },
}

Expand All @@ -31,10 +63,32 @@ impl Shell {
}
}

/// Enable Docker-in-Docker inside the container that
/// floki runs.
///
/// ---
///
/// Back to:
/// - [Floki Config](./struct.FlokiConfig.html#structfield.dind)
///
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum DindConfig {
/// Provide a boolean.
///
/// If `true` is given, floki will enable Docker-in-Docker
/// using dind image: `docker:stable-dind`.
///
Toggle(bool),

/// Enable Docker-in-Docker and specify the dind image to use.
///
/// e.g.
///
/// ```yaml
/// image: docker:19.03-dind
/// ```
///
Image { image: String },
}

Expand All @@ -59,36 +113,142 @@ impl DindConfig {
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
/// The Volume structure captures configuration for floki volumes
/// # Floki Volumes
///
/// Floki has the ability to use volumes for caching build artifacts between
/// runs of the container (amongst other things).
///
/// Floki creates directories on the host to back these volumes
/// in `~/.floki/volumes`.
///
/// ---
///
/// Back to:
/// - [Floki Config](./struct.FlokiConfig.html#structfield.volumes)
///
pub(crate) struct Volume {
#[serde(default = "default_to_false")]
/// A shared volume is reused by containers which also use a
/// shared volume by the same name. Volumes which are not
/// shared are localised to a particular floki configuration file.
/// _Optional_
///
/// _Default:_ `false`
///
/// Share this volume with other containers instantiated from
/// different floki config files.
///
/// If `false`, this volume is only accessible to containers
/// instantiated using this config file.
///
pub(crate) shared: bool,
/// The mount path is the path at which the volume is mounted
/// inside the floki container.

/// The path to which the volume is mounted inside the container.
///
pub(crate) mount: path::PathBuf,
}

/// # Floki Configuration Reference
///
/// By default floki looks for its configuration file in `floki.yaml`
/// (See `floki --help` for how to override this).
///
/// This document serves as a complete reference for all that can be
/// included in this configuration file. See the [user documentation][ud]
/// for installation instructions, usage guidance, and recipes for
/// a number of use cases.
///
/// [ud]: https://metaswitch.github.io/floki/
///
/// Floki config is defined as a [YAML][yaml] document with the structure
/// detailed below.
///
/// [yaml]: https://yaml.org/spec/1.2/spec.html
///
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct FlokiConfig {
/// Specify how floki sources or builds the image to host your
/// environment.
///
pub(crate) image: image::Image,

/// _Optional_
///
/// Commands to be run before dropping the user into an interactive
/// shell.
///
/// If "inner" and "outer" shells are defined under [`shell`](#structfield.shell),
/// these commands run in the outer shell.
///
#[serde(default = "Vec::new")]
pub(crate) init: Vec<String>,

/// _Optional_
///
/// _Default:_ `sh`
///
/// Specify the shell for floki to run.
///
#[serde(default = "default_shell")]
pub(crate) shell: Shell,

/// _Optional_
///
/// _Default:_ `/src`
///
/// Path inside the container that floki will mount the
/// current working directory to.
///
#[serde(default = "default_mount")]
pub(crate) mount: path::PathBuf,

/// _Optional_
///
/// Extra command line arguments to pass to docker.
///
/// NOTE: This is a back door and if you find you have repeated use
/// of the same invocation using `docker_switches`, consider raising
/// an issue to request the use case be covered with mainline
/// floki features.
///
#[serde(default = "Vec::new")]
pub(crate) docker_switches: Vec<String>,

/// _Optional_
///
/// _Default:_ `false`
///
/// Forward your ssh-agent into the container.
///
/// NOTE: You will need to have an ssh-agent running on the host
/// before launching floki.
///
#[serde(default = "default_to_false")]
pub(crate) forward_ssh_agent: bool,

/// _Optional_
///
/// _Default:_ `false`
///
/// Enable Docker-in-Docker inside the container.
///
#[serde(default = "DindConfig::deactivated")]
pub(crate) dind: DindConfig,

/// _Optional_
///
/// _Default:_ `false`
///
/// Run interactive shell in the container as the host user.
///
#[serde(default = "default_to_false")]
pub(crate) forward_user: bool,

/// _Optional_
///
/// Specify the volumes to mount in the container as a mapping
/// of a name to [volume config][vol].
///
/// [vol]: ./struct.Volume.html
///
#[serde(default = "BTreeMap::new")]
pub(crate) volumes: BTreeMap<String, Volume>,
}
Expand Down
90 changes: 90 additions & 0 deletions src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,73 @@ use yaml_rust::YamlLoader;

use crate::errors::{FlokiError, FlokiSubprocessExitStatus};

/// Build Spec provides floki with the information needed to build
/// a container image.
///
/// ---
///
/// Back to:
/// - [Image Config](./enum.Image.html#variant.Build)
/// - [Floki Config](../config/struct.FlokiConfig.html#structfield.image)
///
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct BuildSpec {
/// The name to give the container.
///
/// NOTE: floki will append ":floki" to provide a docker tag. Do not
/// include a tag following a ":" in this name.
///
name: String,

/// _Optional_
///
/// _Default:_ `Dockerfile`
///
/// Path to the dockerfile to build the image from.
///
#[serde(default = "default_dockerfile")]
dockerfile: PathBuf,

/// _Optional_
///
/// _Default:_ `"."`
///
/// Path to the root of the build context for the dockerfile.
///
#[serde(default = "default_context")]
context: PathBuf,

/// _Optional_
///
/// Target to use, for multi-stage dockerfiles.
///
target: Option<String>,
}

/// Data to reference an image name from the key of another YAML file.
///
/// ---
///
/// Back to:
/// - [Image Config](./enum.Image.html#variant.Yaml)
/// - [Floki Config](../config/struct.FlokiConfig.html#structfield.image)
///
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct YamlSpec {
/// Path to foreign YAML file.
///
pub file: PathBuf,

/// Key path to image name data.
///
/// - Key path looks like: `path.to.key`
/// + A period separated list of YAML keys to lookup.
/// + Keys may only be strings of integers.
/// + `jq` style referencing of arrays (`key.[0].other_key` or
/// `key.[].other_key`) is _not supported_.
/// + Preceding period will be read as the top level key
/// being the empty string, e.g: `.path.to.key => "".path.to.key`.
///
key: String,
}

Expand All @@ -31,11 +85,47 @@ fn default_context() -> PathBuf {
".".into()
}

/// Configure the container image to use.
///
/// ---
///
/// Back to:
///
/// - [Floki Config](../config/struct.FlokiConfig.html#structfield.image)
///
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Image {
/// Provide the name of a prebuilt image in a docker registry, e.g:
///
/// ```yaml
/// debian:sid
/// ```
///
Name(String),

/// Instruct floki to build the image.
///
/// Provide a [Build Spec](./struct.BuildSpec.html) under the `build`
/// key.
///
/// ```yaml
/// build:
/// <Build Spec>
/// ```
///
Build { build: BuildSpec },

/// Get the image name by referencing a field in another YAML file.
///
/// Provide a [Yaml Image Reference](./struct.YamlSpec.html) under the
/// `yaml` key.
///
/// ```yaml
/// yaml:
/// <Yaml Image Reference>
/// ```
///
Yaml { yaml: YamlSpec },
}

Expand Down