Design
cdflow2
is a standalone binary program written in Go. It is designed
to orchestrate building and storing artefacts and then deploying them into a set of
environments using Terraform as part of a continuous delivery pipeline.
The core of cdflow2
is quite small, using plugins running in Docker
containers to perform configuration, perform the builds and run terraform. This page describes
how these plugin containers work.
Contents
Config Plugin
You must choose one config container to use cdflow2
via the config
> image
value in
cdflow.yaml. It is responsible for:
- Performing interactive setup with the setup command.
- Providing configuration for the build(s) at the start of the release command.
- Saving the release at the end of the release command.
- Retrieving the release at the start of a deploy, destroy or shell command and providing the configuration to run Terraform.
A working example of a config container is https://github.com/mergermarket/cdflow2-config-acuris - this is only suitable for building software within ION Analytics, but it may be useful to see how it works. It builds on https://github.com/mergermarket/cdflow2-config-common, which is a common foundation for building config containers.
The config container is started and the container entrypoint runs as a server accepting
remote procedure calls from cdflow
- the mechanism for sending these RPCs is described
below. Input to and output from the container (i.e. what it reads from STDIN and writes
to STDOUT and STDERR) are sent directly to the user, so the config container is able to
run interactively.
Remote Proceedure Calls (RPCs) Interface
In addition to the interface with the user via stdio, cdflow2
makes calls to the config
container. It does this by using Docker to execute an additional command within the
container: /app forward
. The job of this process is to forward requests and responses
to/from the main process within the container (the mechanism for this is outside the
scope of cdflow2
itself, but the implementation in
cdflow2-config-common does this via a server listening
on a UNIX domain socket within the container.
cdflow2
sends requests as JSON lines to STDIN of the forwarding
process, and receives responses as JSON lines from STDOUT of the
forwarding process. The format of these lines is defined by the request and response
structures defined in
config/container.go
and described below (each JSON document contains the listed fields).
Two eDocker volumes are also mapped into the config container for all commands except setup:
/release
- during the release command this is used to collect the information to save in the release. For the commands that run Terraform this is where the release is retrieved to./cache
- available as a place to cache data between runs.
Setup RPC
The Setup RPC is invoked when the user runs the setup
command.
SetupRequest Properties
Action
: Always "setup".
Commit
: The id of the Git commit.
Component
: The name of the component inferred from the Git repo name (or passed explicitly by the user).
Config
: Config in cdflow.yaml under config
> params
.
Env
: The environment variables set for the main cdflow2
process.
ReleaseRequirements
: This is a map of string arrays. The keys are the names of builds and the values are "needs" declared by each build (this is described in more detail in the ConfigureRelease RPC below).
SetupResponse Properties
Success
: Boolean value indicating success or failure.
ConfigureRelease RPC
The ConfigureRelease RPC is invoked at the almost at start of the release command. There is first
a call to each build container to collect the "needs" of each build (an array of string identifiers), then this RPC
is invoked to provide the config for each build in the form of environment variables\n: for example a build that
needs to upload a lambda function would indicate a "lambda" need. The config container would then add environment
variables containing the AWS credentials and the name of the bucket to upload to. The exact details of these interfaces
are a contract between the build and config containers and are outside the scope of cdflow2
itself.
ConfigureReleaseRequest Properties
Action
: Always "configure_release".
Commit
: The id of the Git commit.
Component
: The name of the component inferred from the Git repo name (or passed explicitly by the user).
Config
: Config in cdflow.yaml under config
> params
.
Env
: The environment variables set for the main cdflow2
process.
ReleaseRequirements
: This is a map of string arrays. The keys are the names of builds and the values are "needs" declared by each build (described above).
Version
: The version string passed to the release command.
ConfigureReleaseResponse Properties
AdditionalMetadata
: String keys and values that are added to the release
Terraform variable to make additional infomration available during deployment (e.g. the config container might receive a team
parameter and want to make this available to the Terraform code).
Env
: A map of environment maps. The keys at the top level are the names of the builds (i.e. the keys user builds
in cdflow.yaml) and the values are maps of environment variable names and values for each build.
Success
: Boolean value indicating success or failure.
UploadRelease RPC
The UploadRelease RPC is invoked at the end of the release command in order to persist the release
data collected in the /release
volume. Since the container persists since the preceeding ConfigureRelease RPC the data
provided is not repeated and the configure container must hold onto what it needs.
UploadReleaseRequest Properties
Action
: Always "upload_release"
TerraformImage
: The unique Terraform docker image id to ensure that the same version of Terraform is always used with this release. The config container is responsible for adding this to the persisted release (not sure why this should be the config container rather than cdflow2
adding it to the release volume directly - could be a future simplification of this interface).
UploadReleaseResponse Properties
Message
: A message to be displayed to the user (not sure why this is here since the config container can interact directly with the user now - could be removed as a future simplification of this interface).
Success
: Boolean value indicating success or failure.
PrepareTerraform RPC
The PrepareTerraform RPC is invoked at the start of a command that runs Terraform (i.e.
deploy, destroy or shell). Its job is to download the saved
release to the /release
mapped volume, provide the Terraform backend configuration, and to provide environment variables passed to terraform (typically credentials used by Terraform providers).
PrepareTerraformRequest Properties
Action
: Always "prepare_terraform".
Commit
: The id of the Git commit.
Component
: The name of the component inferred from the Git repo name (or passed explicitly by the user).
Config
: Config in cdflow.yaml under config
> params
.
Env
: The environment variables set for the main cdflow2
process.
EnvName
: The name of the environment passed to the command (e.g. deploy).
StateShouldExist
: Optional boolean. Where present will cause validation that the statefile either does or doesn't exist (always provided for deploy).
Version
: The version string passed to the command (e.g. deploy).
PrepareTerraformResponse Properties
Env
: Map of environment variable names and values to use for the Terraform container.
Success
: Boolean value indicating success or failure.
TerraformImage
: The Terraform image identifier to run.
TerraformBackendType
: The Terraform backend type: e.g. "s3".
TerraformBackendConfig
: DEPRECATED: Old mechanism for passing Terraform backend config that didn't support hiding sensitive values.
TerraformBackendConfigParameters
: Map of Terraform backend config parameters. Each value is a futher map containing Value
and DisplayValue
. DisplayValue
should be provided where the value is sensitive (the display value will be displayed instead between square brackets to indicate it is a placeholder for the actual value).
Build Plugins
cdflow.yaml can container zero or more named builds under the builds
key. Each build
must have an image
property, which is used to create a build container for that build. Build containers are
executed when the release command is run. They are often dependent on config
provided by a config container but are otherwise decoupled from them. The interface is much simpler than a config
container and as a result they are simpler to create.
The build container is invoked twice. The first time is to get a list of "needs" - string identfiers that are passed
to the config container to indicate what configuration is needed. This is a contract between the build and config
containers and which identifiers exist or what they mean is outside the scope of cdflow2
itself. The second
invocation is to perform the build. This will typically be some kind of software build (e.g. building a Docker container)
and then uploading it to an archive (e.g. a Docker image registry). The build will then return information about
this artefact, which will be made available to Terraform in a variable with the same name as the build.
Needs
The container's entrypoint is first invoked with a single "requirements" parameter. It must then write a JSON document to STDOUT containing an array of string identifiers and then exit.
Build
Once the config PrepareRelase RPC has been called the build container will be invoked again, this time without any parameters. In addition to the environment variables provided by the config container, the following will also be set:
VERSION
: The version string passed to the release command.
COMPONENT
: The name of the component inferred from the Git repo name (or passed explicitly by the user).
COMMIT
: The id of the Git commit.
BUILD_ID
: The name of the build in cdflow.yaml.
MANIFEST_PARAMS
: The params
key under the build in cdflow.yaml encoded in JSON.
The release volume will also be mapped within the container as /build
so it can save data within the release. See
https://github.com/mergermarket/cdflow2-build-files for an example build plugin that makes use of this.
At the end of the build the container should write a /release-metadata.json
file withing the container. The
keys and values within this JSON document will be provided as a Terraform map variable with the same name as
the build.
Terraform Container
Terraform is run through a container. The image to use is configured in cdflow.yaml in
the terraform
> image
key. This might be an official
Terraform Image from Hashicorp, or might be a custom one
(e.g. based on an official image but with additional dependencies installed).
In addition to the Terraform binary the image must include /bin/sleep
(the official ones do) - cdflow2
reuses
the same Terraform container as an optimisation, this is used as the entrypoint with the Terraform commands exec'd
within the same container.