Skip to content

Commit

Permalink
Human-readable date-based image suffixes
Browse files Browse the repository at this point in the history
Historically, the ID used to identify a set of images was taken from the
Cirrus-CI build ID.  This was done to make auditing easier, since one
can easily retrieve the build logs using the ID.  However, there are
many disadvantages to using the build id:

* It's not human-readable, making it difficult to ascertain exactly when
  the images were built.
* It's not guaranteed to be incremental, and therefore cannot be
  utilized as a "version".
* It doesn't convey helpful information like when it was produced, or
  which release of Fedora is included in the set.

For these and other reasons, switch to a simple date-based image suffix
encoded in a repository file.  Include a UTC-baesd timestamp to avoid
value collisions, and a string identifying what OS's releases were built.

Signed-off-by: Chris Evich <[email protected]>
  • Loading branch information
cevich committed Jan 20, 2023
1 parent 75ad2e8 commit baa870c
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 66 deletions.
11 changes: 11 additions & 0 deletions .cirrus.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
# Lang. ref: https://github.com/bazelbuild/starlark/blob/master/spec.md#contents
# Impl. ref: https://cirrus-ci.org/guide/programming-tasks/
load("cirrus", "fs")

def main():
return {
"env": {
"IMG_SFX": fs.read("IMG_SFX").strip()
},
}
11 changes: 3 additions & 8 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ env:
CIRRUS_CLONE_DEPTH: 50
# Version of packer to use when building images
PACKER_VERSION: &PACKER_VERSION "1.8.3"
# Unique suffix label to use for all images produced by _this_ run (build)
IMG_SFX: "${CIRRUS_BUILD_ID}"
#IMG_SFX = <See IMG_SFX file and .cirrus.star script>


gcp_credentials: ENCRYPTED[823fdbc2fee3c27fa054ba1e9cfca084829b5e71572f1703a28e0746b1a924ee5860193f931adce197d40bf89e7027fe]

Expand All @@ -29,12 +29,7 @@ validate_task:
PACKER_VERSION: *PACKER_VERSION
script:
- "ci/shellcheck.sh"
- "make clean"
- "make help"
- "make image_builder/gce.json"
- "make base_images/cloud.json"
- "make cache_images/cloud.json"
- "make win_images/win-server-wsl.json"
- "ci/validate.sh"


image_builder_task:
Expand Down
1 change: 1 addition & 0 deletions IMG_SFX
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20230120t152650z-f37f36u2204
57 changes: 32 additions & 25 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,8 @@ override _PACKER_URL := https://releases.hashicorp.com/packer/$(strip $(call err
# Align each line properly to the header
override _HLPFMT = "%-20s %s\n"

# Suffix used to identify images produce by _this_ execution
# N/B: There are length/character limitations in GCE for image names
IMG_SFX ?=
# Suffix value for any images built from this make execution
_IMG_SFX ?= $(file <IMG_SFX)

# Env. vars needed by packer
export CHECKPOINT_DISABLE = 1 # Disable hashicorp phone-home
Expand All @@ -115,6 +114,14 @@ help: ## Default target, parses special in-line comments as documentation.
@grep -E '^[[:print:]]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":(.*)?## "}; {printf $(_HLPFMT), $$1, $$2}'

# N/B: This will become part of the GCE image name and AWS Image name-tag.
# There are length/character limitations (a-z, 0-9, -) in GCE for image
# names and a max-length of 63.
.PHONY: IMG_SFX
IMG_SFX: ## Generate a new date-based image suffix, store in the file IMG_SFX
$(file >$@,$(shell date --utc +%Y%m%dt%H%M%Sz)-f$(FEDORA_RELEASE)f$(PRIOR_FEDORA_RELEASE)u$(subst .,,$(UBUNTU_RELEASE)))
@echo "$(file <IMG_SFX)"

.PHONY: ci_debug
ci_debug: $(_TEMPDIR)/ci_debug.tar ## Build and enter container for local development/debugging of container-based Cirrus-CI tasks
/usr/bin/podman run -it --rm \
Expand Down Expand Up @@ -208,7 +215,7 @@ define packer_build
$(PACKER_INSTALL_DIR)/packer build \
-force \
-var TEMPDIR="$(_TEMPDIR)" \
-var IMG_SFX="$(call err_if_empty,IMG_SFX)" \
-var IMG_SFX="$(call err_if_empty,_IMG_SFX)" \
$(if $(PACKER_BUILDS),-only=$(PACKER_BUILDS)) \
$(if $(DEBUG_NESTED_VM),-var TTYDEV=$(shell tty),-var TTYDEV=/dev/null) \
$(if $(PACKER_BUILD_ARGS),$(PACKER_BUILD_ARGS)) \
Expand All @@ -234,7 +241,7 @@ image_builder_debug: $(_TEMPDIR)/image_builder_debug.tar ## Build and enter cont
-v /dev/kvm:/dev/kvm \
-e PACKER_INSTALL_DIR=/usr/local/bin \
-e PACKER_VERSION=$(call err_if_empty,PACKER_VERSION) \
-e IMG_SFX=$(call err_if_empty,IMG_SFX) \
-e IMG_SFX=$(call err_if_empty,_IMG_SFX) \
-e GAC_FILEPATH=$(GAC_FILEPATH) \
-e AWS_SHARED_CREDENTIALS_FILE=$(AWS_SHARED_CREDENTIALS_FILE) \
docker-archive:$<
Expand All @@ -244,14 +251,14 @@ $(_TEMPDIR)/image_builder_debug.tar: $(_TEMPDIR)/.cache/centos $(wildcard image_

# Avoid re-downloading unnecessarily
# Ref: https://www.gnu.org/software/make/manual/html_node/Special-Targets.html#Special-Targets
.PRECIOUS: $(_TEMPDIR)/fedora-aws-$(IMG_SFX).$(IMPORT_FORMAT)
$(_TEMPDIR)/fedora-aws-$(IMG_SFX).$(IMPORT_FORMAT): $(_TEMPDIR)
.PRECIOUS: $(_TEMPDIR)/fedora-aws-$(_IMG_SFX).$(IMPORT_FORMAT)
$(_TEMPDIR)/fedora-aws-$(_IMG_SFX).$(IMPORT_FORMAT): $(_TEMPDIR)
bash import_images/handle_image.sh \
$@ \
$(call err_if_empty,FEDORA_IMAGE_URL) \
$(call err_if_empty,FEDORA_CSUM_URL)

$(_TEMPDIR)/fedora-aws-arm64-$(IMG_SFX).$(IMPORT_FORMAT): $(_TEMPDIR)
$(_TEMPDIR)/fedora-aws-arm64-$(_IMG_SFX).$(IMPORT_FORMAT): $(_TEMPDIR)
bash import_images/handle_image.sh \
$@ \
$(call err_if_empty,FEDORA_ARM64_IMAGE_URL) \
Expand Down Expand Up @@ -298,17 +305,17 @@ $(_TEMPDIR)/%.snapshot_id: $(_TEMPDIR)/%.import_task_id
define _register_sed
sed -r \
-e 's/@@@NAME@@@/$(1)/' \
-e 's/@@@IMG_SFX@@@/$(IMG_SFX)/' \
-e 's/@@@IMG_SFX@@@/$(_IMG_SFX)/' \
-e 's/@@@ARCH@@@/$(2)/' \
-e 's/@@@SNAPSHOT_ID@@@/$(3)/' \
import_images/register.json.in \
> $(4)
endef

$(_TEMPDIR)/fedora-aws-$(IMG_SFX).register.json: $(_TEMPDIR)/fedora-aws-$(IMG_SFX).snapshot_id import_images/register.json.in
$(_TEMPDIR)/fedora-aws-$(_IMG_SFX).register.json: $(_TEMPDIR)/fedora-aws-$(_IMG_SFX).snapshot_id import_images/register.json.in
$(call _register_sed,fedora-aws,x86_64,$(file <$<),$@)

$(_TEMPDIR)/fedora-aws-arm64-$(IMG_SFX).register.json: $(_TEMPDIR)/fedora-aws-arm64-$(IMG_SFX).snapshot_id import_images/register.json.in
$(_TEMPDIR)/fedora-aws-arm64-$(_IMG_SFX).register.json: $(_TEMPDIR)/fedora-aws-arm64-$(_IMG_SFX).snapshot_id import_images/register.json.in
$(call _register_sed,fedora-aws-arm64,arm64,$(file <$<),$@)

# Avoid multiple registrations for the same image
Expand All @@ -333,17 +340,17 @@ $(_TEMPDIR)/%.ami.json: $(_TEMPDIR)/%.ami.id $(_TEMPDIR)/%.ami.name
| tee $@

.PHONY: import_images
import_images: $(_TEMPDIR)/fedora-aws-$(IMG_SFX).ami.json $(_TEMPDIR)/fedora-aws-arm64-$(IMG_SFX).ami.json import_images/manifest.json.in ## Import generic Fedora cloud images into AWS EC2.
import_images: $(_TEMPDIR)/fedora-aws-$(_IMG_SFX).ami.json $(_TEMPDIR)/fedora-aws-arm64-$(_IMG_SFX).ami.json import_images/manifest.json.in ## Import generic Fedora cloud images into AWS EC2.
sed -r \
-e 's/@@@IMG_SFX@@@/$(IMG_SFX)/' \
-e 's/@@@IMG_SFX@@@/$(_IMG_SFX)/' \
-e 's/@@@CIRRUS_TASK_ID@@@/$(CIRRUS_TASK_ID)/' \
import_images/manifest.json.in \
> import_images/manifest.json
@echo "Image import(s) successful."
@echo "############################################################"
@echo "Please update Makefile value:"
@echo ""
@echo " FEDORA_IMPORT_IMG_SFX = $(IMG_SFX)"
@echo " FEDORA_IMPORT_IMG_SFX = $(_IMG_SFX)"
@echo "############################################################"

.PHONY: base_images
Expand Down Expand Up @@ -377,7 +384,7 @@ ubuntu_podman: ## Build Ubuntu podman development container
$(call build_podman_container,$@,$(UBUNTU_RELEASE))

$(_TEMPDIR)/%_podman.tar: podman/Containerfile podman/setup.sh $(wildcard base_images/*.sh) $(wildcard cache_images/*.sh) $(_TEMPDIR)/.cache/%
podman build -t $*_podman:$(call err_if_empty,IMG_SFX) \
podman build -t $*_podman:$(call err_if_empty,_IMG_SFX) \
--security-opt seccomp=unconfined \
--build-arg=BASE_NAME=$(subst prior-,,$*) \
--build-arg=BASE_TAG=$(call err_if_empty,BASE_TAG) \
Expand All @@ -386,40 +393,40 @@ $(_TEMPDIR)/%_podman.tar: podman/Containerfile podman/setup.sh $(wildcard base_i
-v $(_TEMPDIR)/.cache/$*:/var/cache/apt:Z \
-f podman/Containerfile .
rm -f $@
podman save --quiet -o $@ $*_podman:$(IMG_SFX)
podman save --quiet -o $@ $*_podman:$(_IMG_SFX)

.PHONY: skopeo_cidev
skopeo_cidev: $(_TEMPDIR)/skopeo_cidev.tar ## Build Skopeo development and CI container
$(_TEMPDIR)/skopeo_cidev.tar: $(wildcard skopeo_base/*) $(_TEMPDIR)/.cache/fedora
podman build -t skopeo_cidev:$(call err_if_empty,IMG_SFX) \
podman build -t skopeo_cidev:$(call err_if_empty,_IMG_SFX) \
--security-opt seccomp=unconfined \
--build-arg=BASE_TAG=$(FEDORA_RELEASE) \
-v $(_TEMPDIR)/.cache/fedora:/var/cache/dnf:Z \
skopeo_cidev
rm -f $@
podman save --quiet -o $@ skopeo_cidev:$(IMG_SFX)
podman save --quiet -o $@ skopeo_cidev:$(_IMG_SFX)

# TODO: Temporarily force F36 due to:
# https://github.com/aio-libs/aiohttp/issues/6600
.PHONY: ccia
ccia: $(_TEMPDIR)/ccia.tar ## Build the Cirrus-CI Artifacts container image
$(_TEMPDIR)/ccia.tar: ccia/Containerfile
podman build -t ccia:$(call err_if_empty,IMG_SFX) \
podman build -t ccia:$(call err_if_empty,_IMG_SFX) \
--security-opt seccomp=unconfined \
--build-arg=BASE_TAG=36 \
ccia
rm -f $@
podman save --quiet -o $@ ccia:$(IMG_SFX)
podman save --quiet -o $@ ccia:$(_IMG_SFX)

.PHONY: imgts
imgts: $(_TEMPDIR)/imgts.tar ## Build the VM image time-stamping container image
$(_TEMPDIR)/imgts.tar: imgts/Containerfile imgts/entrypoint.sh imgts/google-cloud-sdk.repo imgts/lib_entrypoint.sh $(_TEMPDIR)/.cache/centos
$(call podman_build,$@,imgts:$(call err_if_empty,IMG_SFX),imgts,centos)
$(call podman_build,$@,imgts:$(call err_if_empty,_IMG_SFX),imgts,centos)

define imgts_base_podman_build
podman load -i $(_TEMPDIR)/imgts.tar
podman tag imgts:$(call err_if_empty,IMG_SFX) imgts:latest
$(call podman_build,$@,$(1):$(call err_if_empty,IMG_SFX),$(1),centos)
podman tag imgts:$(call err_if_empty,_IMG_SFX) imgts:latest
$(call podman_build,$@,$(1):$(call err_if_empty,_IMG_SFX),$(1),centos)
endef

.PHONY: imgobsolete
Expand All @@ -445,9 +452,9 @@ $(_TEMPDIR)/orphanvms.tar: $(_TEMPDIR)/imgts.tar imgts/lib_entrypoint.sh orphanv
.PHONY: .get_ci_vm
get_ci_vm: $(_TEMPDIR)/get_ci_vm.tar ## Build the get_ci_vm container image
$(_TEMPDIR)/get_ci_vm.tar: lib.sh get_ci_vm/Containerfile get_ci_vm/entrypoint.sh get_ci_vm/setup.sh $(_TEMPDIR)
podman build -t get_ci_vm:$(call err_if_empty,IMG_SFX) -f get_ci_vm/Containerfile .
podman build -t get_ci_vm:$(call err_if_empty,_IMG_SFX) -f get_ci_vm/Containerfile .
rm -f $@
podman save --quiet -o $@ get_ci_vm:$(IMG_SFX)
podman save --quiet -o $@ get_ci_vm:$(_IMG_SFX)

.PHONY: clean
clean: ## Remove all generated files referenced in this Makefile
Expand Down
59 changes: 26 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,23 +145,25 @@ see step 4 below.

### Process: ###

1. After you make your script changes, push to a PR. They will be
validated and linted before VM image production begins.
1. Whether or not you made any script changes, before pushing to a PR run
`make IMG_SFX` and commit the change. This helps avoid overwriting
existing images - potentially already in use by CI. The 'validate'
CI task will asserts every PR makes changes to this file.

2. The name of all output GCE images will share a common suffix (*image ID*).
2. The name of all output images will share a common `IMG_SFX` value
Assuming a successful image-build, a
[github-action](.github/workflows/pr_image_id.yml)
will post the new *image ID* as a comment in the PR. If this automation
breaks, you may need to [figure the ID out the hard
will post the types and names of all built images as a comment in the PR.
If this automation breaks, you may need to [figure the ID out the hard
way](README.md#Looking-up-an-image-ID). For AWS EC2 images, every one
will have a unique AMI ID assigned. You'll need to
[look these up separately](README.md#Looking-up-an-image-ID)
until the github action is updated.
until the github action is fixed.

3. Go over to whatever other containers/repository needed the image update.
Open the `.cirrus.yml` file, and find the 'env' line referencing the *image
ID* and/or *AMI*. It will likely be named `IMAGE_SUFFIX:` or something
similar. Paste in the *image ID* or *AMI*.
similar. Paste in the *image ID* (it should begin with the letter `c`).

4. Open up a PR with this change, and push it. Once all tests pass and you're
satisfied with the image changes, ask somebody to review/approve both
Expand All @@ -173,30 +175,27 @@ see step 4 below.

### Looking up an image ID: ###

A GCE *image ID* is simply big number prefixed by the letter 'c'. You may
need to look it up in a PR for example, if
A *image ID* is simply the `IMG_SFX` value prefixed by a letter. The letter
`b` represents base-images, and 'c' represents cache-images. You may
need to look the value up if (for example),
[the automated comment posting github-action](.github/workflows/pr_image_id.yml)
fails. For AWS EC2 images, you'll need to look up the AMI ID (string) for each
cache-image produced.
failed.

1. In a PR, find and click the build task for the image you're interested in.
Near the top of the Cirrus-CI WebUI, will be a section labeled 'Artifacts'.
1. In any PR, find and click the *details* link for one of the build tasks.
Near the top of the *Cirrus-CI WebUI*, will be a section labeled 'Artifacts'.

2. Click the `manifest` artifact
2. Click the `manifest` artifact.

3. Click the `cache_images` folder
3. Click the nested folder name (if there is one).

4. Click the `manifest.json` file, it should open in your browser window.

5. For *GCE images* look at the `artifact_id` field. It will end in a
`c<big number` suffix. This is the *image ID* for all GCE images.

6. For *AWS EC2 images* look at the `artifact_id` field. It will contain
a region prefix, like `us-east-1`, ignore this. At the end of the value
will be the AMI ID, similar to `ami-<big number>`. This is the ID for
this one, specific image. **Every AWS image will have a unique AMI ID**
(unlike the shared ID for GCE images).
5. For *GCE images* look at the `artifact_id` field. It will contain the
full *image id* value in the form `c<IMG_SFX>`.

6. For *AWS EC2 images* look under the `custom_data` field for the `IMG_SFX`
value. If this was a base-image, simply prefix the value with a `b`, or
a `c` for a cache-image.

## The image-builder image (overview step 1)

Expand Down Expand Up @@ -404,26 +403,20 @@ credential files and ensure correct account configuration. Having these files
stored *in your home directory* on your laptop/workstation, the process of
producing images proceeds as follows:

1. Invent some unique identity suffix for your images. It may contain (***only***)
lowercase letters, numbers and dashes; nothing else. Some suggestions
of useful values would be your name and today's date. If you manage to screw
this up somehow, stern errors will be presented without causing any real harm.

2. Ensure you have podman installed, and lots of available network and CPU
1. Ensure you have podman installed, and lots of available network and CPU
resources (i.e. turn off YouTube, shut down background VMs and other hungry
tasks). Build the image-builder container image, by executing
```
make image_builder_debug GAC_FILEPATH=</home/path/to/gac.json> \
AWS_SHARED_CREDENTIALS_FILE=</path/to/credentials> \
IMG_SFX=<ID chosen in step 1>
AWS_SHARED_CREDENTIALS_FILE=</path/to/credentials>
```

3. You will be dropped into a debugging container, inside a volume-mount of
2. You will be dropped into a debugging container, inside a volume-mount of
the repository root. This container is practically identical to the VM
produced and used in *overview step 1*. If changes are made, the container
image should be re-built to reflect them.

4. If you wish to build only a subset of available images, list the names
3. If you wish to build only a subset of available images, list the names
you want as comma-separated values of the `PACKER_BUILDS` variable. Be
sure you *export* this variable so that `make` has access to it. For
example, `export PACKER_BUILDS=ubuntu,prior-fedora`.
Expand Down
51 changes: 51 additions & 0 deletions ci/validate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash

# This script is intended to be run by Cirrus-CI to validate PR
# content prior to building any images. It should not be run
# under any other context.

set -eo pipefail

SCRIPT_FILEPATH=$(realpath "${BASH_SOURCE[0]}")
SCRIPT_DIRPATH=$(dirname "$SCRIPT_FILEPATH")
REPO_DIRPATH=$(realpath "$SCRIPT_DIRPATH/../")

# shellcheck source=./lib.sh
source "$REPO_DIRPATH/lib.sh"

req_env_vars CIRRUS_PR CIRRUS_BASE_SHA

# die() will add a reference to this file and line number.
[[ "$CIRRUS_CI" == "true" ]] || \
die "This script is only/ever intended to be run by Cirrus-CI."

for target in image_builder/gce.json base_images/cloud.json \
cache_images/cloud.json win_images/win-server-wsl.json; do
if ! make $target; then
die "Running 'make $target' failed, please validate input YAML files."
fi
done

# Variable is defined by Cirrus-CI at runtime
# shellcheck disable=SC2154
if ! git diff --name-only ${CIRRUS_BASE_SHA}..HEAD | grep -q IMG_SFX; then
die "Every PR must include an updated IMG_SFX file.
Simply run 'make IMG_SFX', commit the result, and re-push."
fi

# Verify new IMG_SFX value always sorts later than previous value. This prevents
# screwups due to local timezone, bad, or unset clocks, etc.
cd $REPO_DIRPATH
# Automation requires the date and time components sort properly
# as if they were version-numbers regardless of distro-version component.
new_img_ver=$(awk -F '-' '{print $1"."$2}' ./IMG_SFX)
# TODO: Conditional checkout not needed after the PR which added IMG_SFX file.
if git checkout ${CIRRUS_BASE_SHA} IMG_SFX; then
old_img_ver=$(awk -F '-' '{print $1"."$2}' ./IMG_SFX)
latest_img_ver=$(echo -e "$old_img_ver\n$new_img_ver" | sort -V | tail -1)
[[ "$latest_img_ver" == "$new_img_ver" ]] || \
die "Date/time stamp appears to have gone backwards! Please commit
an 'IMG_SFX' change with a value later than '$(<IMG_SFX)'"
else
warn "Could not find previous version of IMG_SFX, ignoring."
fi

0 comments on commit baa870c

Please sign in to comment.