11.5. Contexts

11.5.1. About

Contexts are top-level objects that allow dr-provision to support tasks for machines that run somewhere other than on the machines. The docker-context plugin utilizes several common container engines (currently podman and docker) to manage containers by attaching drp tools to an existing, standard container.

11.5.2. Changes

Prior to v4.8.0, containers needed to be built with drpcli added to the container and set to run drpcli machines processjobs as the endpoint in the Dockerfile. Now, the docker-context plugin bind-mounts drpcli and sets the endpoint automatically.

Previous versions of context content used Meta.Dockerfile to build a new container, Meta.Dockerepull to pull via podman/docker pull, and Meta.Imagepull to upload from a URL. Now, Meta.Dockerfile and Meta.Dockerpull not used. The plugin checks Image with Meta.Checksum against the image found in the default catalog.

It is now best practice to provide a version with Image in the form of $(Image)_v1.2.0 where _v1.2.0 is an arbitrary, incremental version (preferrably semver) of the context content or container image (such as ansible-runner_v1.2.0).

Declaring Meta.Imagepull with or without the use of Meta.Checksum is also supported for custom Contexts. If you do not provide a Meta.Checksum (sha256sum is used to verify the checksum), it will overwrite the image every time the task is run. If Meta.Checksum is provided, the existing Meta.Image will be verified. If the checksum is correct, the plguin will not attempt to fetch the image, otherwise it will fetch and overwrite the image.

Images are saved to files/docker-contexts/$(meta.Image).

11.5.3. Setup

When running the suggested install of dr-provision, the install will run the universal-bootstrap workflow on the endpoiont. It includes the bootstrap-drp-endpoint task to install podman or docker and the bootstrap-contexts task to upload the images needed for universal workflows.

If upgrading from 4.7.0 or newer, run the universal-bootstrap workflow on the endpoint after adding the boostrap-drp-endpoint profile to it.

11.5.4. Create Custom Context Content

Before creating custom contexts, review the example-content repo to understand how to create content for drp.

Contexts are placed in the content tree under contexts, which is found at the same level as workflows, params, stages, etc. It is good practice to name the context file as context-name.yaml. The most basic content needed to declare a context is similar to the example below.

---
Name: alpine-runner
Engine: docker-context
Image: alpine-runner_v0.0.1
Meta:
  Imagepull: https://localhost:8090/files/alpine-runner_v0.0.1.tar.gz
  Checksum: 0d42767249b3bc02c337e4c45f4383c0091f4430e735c7e25990616a757d5c1b

The following example adds bash as a requirement since alpine doesn’t come with bash by default. Build the new container, save it locally, upload it to our file server and get the sha256sum. Information from these commands to were used to create the content above.

$ echo -e "FROM alpine:latest\nRUN apk add --no-cache bash curl" > Dockerfile

$ cat Dockerfile
FROM alpine:latest
RUN apk add --no-cache bash curl

$ docker build --rm -t drpcli-alpine:latest .
STEP 1/2: FROM alpine:latest
STEP 2/2: RUN apk add --no-cache bash
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz
(1/4) Installing ncurses-terminfo-base (6.2_p20210612-r0)
(2/4) Installing ncurses-libs (6.2_p20210612-r0)
(3/4) Installing readline (8.1.0-r0)
(4/4) Installing bash (5.1.4-r0)
Executing bash-5.1.4-r0.post-install
Executing busybox-1.33.1-r3.trigger
OK: 8 MiB in 18 packages
COMMIT drpcli-alpine:latest
--> 14a89913f41
Successfully tagged localhost/drpcli-alpine:latest
14a89913f4168a31e86f82536b0a3effb5e96f2f84ea03e0138a7358a33bf90e

$ docker save localhost/drpcli-alpine:latest -o drp-alpine_v0.0.1.tar.gz
Copying blob e2eb06d8af82 done
Copying blob e6aede22edd1 done
Copying config 14a89913f4 done
Writing manifest to image destination
Storing signatures

$ sha256sum drp-alpine_v0.0.1.tar.gz
0d42767249b3bc02c337e4c45f4383c0091f4430e735c7e25990616a757d5c1b  drp-alpine_v0.0.1.tar.gz

$ drpcli files upload drp-alpine_v0.0.1.tar.gz as drp-alpine_v0.0.1.tar.gz
{
  "Path": "/drp-alpine_v0.0.1.tar.gz",
  "Size": 10477568
}

The sums come from sha256sum on the tarball saved locally. Do not use the signatures from docker pull or docker inspect.

The image should be hosted somewhere accessible to the endpoint. This example uses https://drp:8092/files (drp itself) as the file server. Using drp to host the image is not necessary.

Instead of bundlind and uploading the context content, the context can be created using drpcli directly as shown below.

$ drpcli contexts create '{"Name": "alpine-runner", "Engine": "docker-context", "Image": "drp-alpine_v0.0.1", "Meta": {"Checksum": "0d42767249b3bc02c337e4c45f4383c0091f4430e735c7e25990616a757d5c1b", "Imagepull": "https://localhost:8090/files/drp-apline_v0.0.1.tar.gz"}}'
{
  "Available": true,
  "Bundle": "",
  "Description": "",
  "Documentation": "",
  "Endpoint": "",
  "Engine": "docker-context",
  "Errors": [],
  "Image": "drp-alpine_v0.0.1",
  "Meta": {
    "Checksum": "0d42767249b3bc02c337e4c45f4383c0091f4430e735c7e25990616a757d5c1b",
    "Imagepull": "https://localhost:8090/files/drp-apline_v0.0.1.tar.gz"
  },
  "Name": "alpine-runner",
  "ReadOnly": false,
  "Validated": true
}

The context has been created, but the image hasn’t been uploaded to the correct location. It can be uploaded from the UX by selecting the download icon for the context, run universal-bootstrap on the endpoint, or manually upload the image to the correct location. For the last option, the following example is provided. Note the as drp-alpine_v0.0.1 without the .tar.gz.

$ drpcli files upload https://localhost:8090/files/drp-alpine_v0.0.1.tar.gz as contexts/docker-context/drp-alpine_v0.0.1
{
  "Path": "/contexts/docker-context/drp-alpine_v0.0.1",
  "Size": 10477568
}

It is important to note that docker-context does not manage the images once it has been added to the container engine locally (ie podman images). If you are working on the container image, you need to declare the new version label. You will need to manually remove the image from the container engine.

It is also important to understand what user docker-context is running as when using podman or rootless docker.

11.5.5. Using Custom Contexts

In order to use the custom context, create a machine that uses the new context.

$ drpcli contexts list | jq '.[] | select(.Name == "alpine-runner")'
{
  "Available": true,
  "Bundle": "",
  "Description": "",
  "Documentation": "",
  "Endpoint": "",
  "Engine": "docker-context",
  "Errors": [],
  "Image": "drp-alpine_v0.0.1",
  "Meta": {
    "Checksum": "0d42767249b3bc02c337e4c45f4383c0091f4430e735c7e25990616a757d5c1b",
    "Imagepull": "https://localhost:8092/files/drp-apline_v0.0.1.tar.gz"
  },
  "Name": "alpine-runner",
  "ReadOnly": false,
  "Validated": true
}

$ drpcli machines create  '{"Name": "alpine-machine", "Meta": {"BaseContext": "alpine-runner"}}'
{
  "Address": "",
  "Arch": "amd64",
  "Available": true,
  "BootEnv": "sledgehammer",
  "Bundle": "",
  "Context": "alpine-runner",
  "CurrentJob": "",
  "CurrentTask": -1,
  "Description": "",
  "Endpoint": "",
  "Errors": [],
  "Fingerprint": {
    "CSNHash": "",
    "CloudInstanceID": "",
    "MemoryIds": [],
    "SSNHash": "",
    "SystemUUID": ""
  },
  "HardwareAddrs": [],
  "Locked": false,
  "Meta": {
    "BaseContext": "alpine-runner",
    "feature-flags": "change-stage-v2",
    "machine-role": "machine"
  },
  "Name": "alpine-machine",
  "OS": "",
  "Params": {},
  "Partial": false,
  "PendingWorkOrders": 0,
  "Pool": "default",
  "PoolAllocated": false,
  "PoolStatus": "Free",
  "Profiles": [],
  "ReadOnly": false,
  "RetryTaskAttempt": 0,
  "Runnable": true,
  "Secret": "Y1GLuGVCndn-RO4t",
  "Stage": "discover",
  "TaskErrorStacks": [],
  "Tasks": [
    "stage:discover",
    "bootenv:sledgehammer",
    "update-pipeline",
    "enforce-sledgehammer",
    "set-machine-ip-in-sledgehammer",
    "reserve-dhcp-address",
    "ssh-access",
    "stage:universal-discover-start-callback",
    "callback-task",
    "stage:universal-discover-pre-flexiflow",
    "flexiflow-start",
    "flexiflow-stop",
    "stage:inventory-minimal",
    "inventory-minimal",
    "stage:centos-setup-repos",
    "centos-drp-only-repos",
    "stage:cloud-inventory",
    "cloud-inventory",
    "stage:raid-inventory",
    "raid-tools-install",
    "raid-inventory",
    "stage:ipmi-inventory",
    "ipmi-install",
    "ipmi-discover",
    "ipmi-inventory",
    "stage:bios-inventory",
    "bios-tools-install",
    "bios-current-config",
    "stage:network-lldp",
    "network-lldp",
    "gohai",
    "stage:inventory",
    "inventory-check",
    "stage:universal-discover-post-flexiflow",
    "flexiflow-start",
    "flexiflow-stop",
    "stage:universal-discover-classification",
    "classify-stage-list-start",
    "classify-stage-list-stop",
    "stage:universal-discover-post-validation",
    "validation-start",
    "validation-stop",
    "stage:universal-discover-complete-callback",
    "callback-task",
    "stage:universal-chain-workflow",
    "universal-chain-workflow",
    "stage:complete-nobootenv"
  ],
  "Uuid": "cfb152f3-9c50-4baa-8d74-b7148da8b083",
  "Validated": true,
  "WorkOrderMode": false,
  "Workflow": "universal-discover",
  "WorkflowComplete": false
}

The new machine is discovered and workflows can be run against it like any other machine.