From 5b427e35feb3b3f564f38e59daf1e330e3598168 Mon Sep 17 00:00:00 2001 From: tarquin-the-brave Date: Wed, 15 Jul 2020 13:23:45 +0100 Subject: [PATCH 1/2] POC for config reference built with rust docs Signed-off-by: tarquin-the-brave --- src/config.rs | 22 ++++++++++++++++++++++ src/image.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/config.rs b/src/config.rs index 6f48b74..51cf45d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -71,10 +71,32 @@ pub(crate) struct Volume { 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, + + /// TODO #[serde(default = "Vec::new")] pub(crate) init: Vec, #[serde(default = "default_shell")] diff --git a/src/image.rs b/src/image.rs index 03faaed..9bca6af 100644 --- a/src/image.rs +++ b/src/image.rs @@ -7,9 +7,24 @@ 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) +/// #[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, + + /// TODO #[serde(default = "default_dockerfile")] dockerfile: PathBuf, #[serde(default = "default_context")] @@ -31,11 +46,38 @@ 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 { build: BuildSpec }, + + /// TODO Yaml { yaml: YamlSpec }, } From 8f9929d3e78db3ba95f556cd9480889fc742ee11 Mon Sep 17 00:00:00 2001 From: tarquin-the-brave Date: Tue, 25 Aug 2020 14:52:16 +0100 Subject: [PATCH 2/2] complete first attempt at documenting each possible config field Signed-off-by: tarquin-the-brave --- src/config.rs | 152 +++++++++++++++++++++++++++++++++++++++++++++++--- src/image.rs | 52 ++++++++++++++++- 2 files changed, 195 insertions(+), 9 deletions(-) diff --git a/src/config.rs b/src/config.rs index 51cf45d..e387512 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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 }, } @@ -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 }, } @@ -59,15 +113,35 @@ 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, } @@ -96,21 +170,85 @@ pub(crate) struct FlokiConfig { /// pub(crate) image: image::Image, - /// TODO + /// _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, + + /// _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, + + /// _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, } diff --git a/src/image.rs b/src/image.rs index 9bca6af..c4fc39b 100644 --- a/src/image.rs +++ b/src/image.rs @@ -14,6 +14,7 @@ use crate::errors::{FlokiError, FlokiSubprocessExitStatus}; /// /// 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 { @@ -24,17 +25,55 @@ pub struct BuildSpec { /// name: String, - /// TODO + /// _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, } +/// 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, } @@ -77,7 +116,16 @@ pub enum Image { /// Build { build: BuildSpec }, - /// TODO + /// 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 { yaml: YamlSpec }, }