Skip to content

Commit 223098c

Browse files
authored
custom-assembly: add iac content (#40)
Add an example of how you could manage Chainguard Containers declaratively.
1 parent 0b2b076 commit 223098c

6 files changed

Lines changed: 201 additions & 0 deletions

File tree

custom-assembly/iac/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Custom Assembly: Infrastructure as Code Example
2+
3+
This is an example of how you could manage Custom Assembly images
4+
declaratively with an IaC/GitOps approach.
5+
6+
## How it Works
7+
8+
Each YAML file under [`images/`](images/) represents a Chainguard image
9+
repository with the naming convention `images/{name}.yaml`.
10+
11+
This example does not recommend a specific CI/CD solution. Instead it provides
12+
generic scripts that could be ran in response to GitHub repository events in
13+
any given workflow engine.
14+
15+
### Pre Submit
16+
17+
The `presubmit.sh` script is designed to run as part of the checks on a Pull
18+
Request. It validates that the yaml files are valid.
19+
20+
It performs the following checks.
21+
22+
1. Ensures that the image described by the file exists and is enabled for
23+
Custom Assembly.
24+
2. Validates that each package described by the configuration exists in the
25+
private APK repository that Custom Assembly uses.
26+
27+
### Post Submit
28+
29+
The `postsubmit.sh` script is designed to run when changes are merged to the
30+
`main` branch. It applies each of the yaml files with `chainctl image repo build
31+
apply`.
32+
33+
## Usage
34+
35+
### IAM
36+
37+
Before running the scripts, you must login to Chainguard with `chainctl auth
38+
login`.
39+
40+
You must be an `owner` in the organization. Or, you can construct a [custom
41+
role](https://edu.chainguard.dev/chainguard/administration/iam-organizations/roles-role-bindings/roles-role-bindings/)
42+
with the `repo.update` permission.
43+
44+
For your automation, create an [Assumable
45+
Identity](https://edu.chainguard.dev/chainguard/administration/assumable-ids/assumable-ids/)
46+
to run the scripts with. The kind of identity will depend on the platform your
47+
pipelines are hosted in.
48+
49+
### Checking Available Packages
50+
51+
Not all packages are available via Custom Assembly. Before adding a package to
52+
an image, you can check if it's available by querying the index of the private
53+
APK repository used by Custom Assembly.
54+
55+
You must have the `apk (list)` capability in your organization to do this. This
56+
is provided by the `apk.pull` role or other more privileged roles like `viewer`
57+
or `owner`.
58+
59+
```shell
60+
export ORGANIZATION=your.org
61+
62+
curl \
63+
-sSf \
64+
-u "_token:$(chainctl auth token --audience apk.cgr.dev)" \
65+
"https://apk.cgr.dev/${ORGANIZATION}/x86_64/APKINDEX.tar.gz" \
66+
| tar -xOz APKINDEX \
67+
| grep '^P:curl$'
68+
```

custom-assembly/iac/demo.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#! env bash
2+
. ../../base.sh
3+
4+
ORGANIZATION="cs-ttt-demo.dev"
5+
6+
# Check we have the required tools installed to run the script
7+
cmds="
8+
yq
9+
"
10+
missing_cmds=""
11+
for cmd in ${cmds}; do
12+
if ! command -v "${cmd}" &> /dev/null; then
13+
missing_cmds+="${cmd} "
14+
fi
15+
done
16+
if [[ -n "${missing_cmds}" ]]; then
17+
echo "Missing required commands: ${missing_cmds}" >&2
18+
exit 1
19+
fi
20+
21+
# Set the demo back to its initial state
22+
git checkout HEAD images
23+
./postsubmit.sh "${ORGANIZATION}"
24+
25+
clear
26+
27+
banner "We have two images in the 'images/' directory. One for python, another for node."
28+
pe "ls -l images/"
29+
pe "cat images/custom-iac-demo-node.yaml"
30+
pe "cat images/custom-iac-demo-python.yaml"
31+
32+
banner "Let's try adding 'curl' to our custom node image. First, let's check it exists in the private APK repo."
33+
pe "curl -sSf -u \"_token:\$(chainctl auth token --audience apk.cgr.dev)\" \"https://apk.cgr.dev/${ORGANIZATION}/x86_64/APKINDEX.tar.gz\" | tar -xOz APKINDEX | grep '^P:curl$'"
34+
35+
banner "Next, let's add it to the file and run the presubmit checks."
36+
pe "yq -i '.contents.packages += [\"curl\"]' images/custom-iac-demo-node.yaml"
37+
pe "git diff images/custom-iac-demo-node.yaml"
38+
pe "./presubmit.sh ${ORGANIZATION}"
39+
40+
banner "If we add an invalid package to one of the files, the presubmit check should fail."
41+
pe "yq -i '.contents.packages += [\"foobar\"]' images/custom-iac-demo-python.yaml"
42+
pe "cat images/custom-iac-demo-python.yaml"
43+
pe "./presubmit.sh ${ORGANIZATION}"
44+
pe "git checkout HEAD images/custom-iac-demo-python.yaml"
45+
46+
banner "We'll also get an error if the repo doesn't exist."
47+
pe "cp images/custom-iac-demo-python.yaml images/custom-iac-demo-foobar.yaml"
48+
pe "ls -l images/"
49+
pe "./presubmit.sh ${ORGANIZATION}"
50+
pe "rm images/custom-iac-demo-foobar.yaml"
51+
52+
banner "Check that the presubmit checks are still passing."
53+
pe "./presubmit.sh ${ORGANIZATION}"
54+
55+
banner "Now we can apply the changes."
56+
pe "./postsubmit.sh ${ORGANIZATION}"
57+
58+
banner "Running the postsubmit again should be a no-op."
59+
pe "./postsubmit.sh ${ORGANIZATION}"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
contents:
2+
packages: []
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
contents:
2+
packages:
3+
- mariadb-connector-c
4+
- mariadb-connector-c-dev

custom-assembly/iac/postsubmit.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail
4+
5+
org_name="${1:?Must provide organization name as the first argument.}"
6+
7+
tmp_dir=$(mktemp -d)
8+
trap "rm -rf ${tmp_dir}" EXIT
9+
10+
# Apply each yaml file
11+
while read -r f; do
12+
repo="${f#images/}"
13+
repo="${repo%.yaml}"
14+
repo="${repo%.yml}"
15+
16+
echo "${f}: applying..." 2>/dev/null
17+
chainctl image repo build apply --yes \
18+
--parent="${org_name}" \
19+
--repo="${repo}" \
20+
-f "${f}"
21+
done < <(find images -type f -name '*.yaml' -o -name '*.yml')

custom-assembly/iac/presubmit.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail
4+
5+
org_name="${1:?Must provide organization name as the first argument.}"
6+
7+
tmp_dir=$(mktemp -d)
8+
trap "rm -rf ${tmp_dir}" EXIT
9+
10+
# Download the APKINDEX for the private repository. We use this to check if
11+
# packages in the yaml files exist in the private APK repository.
12+
apk_repo="https://apk.cgr.dev/${org_name}"
13+
apk_token=$(chainctl auth token --audience apk.cgr.dev)
14+
apk_index="${tmp_dir}/APKINDEX"
15+
for arch in x86_64 amd64; do
16+
echo "${apk_repo}: downloading the APKINDEX for ${arch}" >&2
17+
curl \
18+
-sSf \
19+
-u "_token:${apk_token}" \
20+
"${apk_repo}/${arch}/APKINDEX.tar.gz" \
21+
2>/dev/null \
22+
| tar -xOz \
23+
>> "${apk_index}"
24+
done
25+
26+
# Validate each yaml file
27+
while read -r f; do
28+
repo="${f#images/}"
29+
repo="${repo%.yaml}"
30+
repo="${repo%.yml}"
31+
32+
# Ensure the repository is configured for custom assembly
33+
echo "${repo}: checking the repository is enabled for custom assembly." >&2
34+
chainctl image repo list --parent=${org_name} -o json \
35+
| jq --arg repo "${repo}" -e \
36+
'.items[] | select(.name == $repo) | .sync_config.apkoOverlay != null' \
37+
>/dev/null \
38+
|| (echo "${repo}: ERROR: not a custom assembly enabled repository" >&2; exit 1)
39+
40+
# Check that every package defined in the yaml file exists in the private APK
41+
# repository
42+
echo "${repo}: validating package list" >&2
43+
while read -r package; do
44+
echo "${repo}: checking package '${package}' is in ${apk_repo}" >&2
45+
grep -q "^P:${package}$" "${apk_index}" || (echo "${repo}: ERROR: package '${package}' not found in ${apk_repo}" >&2; exit 1)
46+
done < <(yq '.contents.packages[]' "${f}")
47+
done < <(find images -type f -name '*.yaml' -o -name '*.yml')

0 commit comments

Comments
 (0)