From afd11af56ebdb7ad2ae7d8ac550c9508e5742c34 Mon Sep 17 00:00:00 2001 From: ana espinoza Date: Fri, 26 Jun 2026 10:22:10 -0600 Subject: [PATCH 1/8] Container env for running K8s image-builder --- image-builder/Dockerfile | 43 ++++++++++++++++++++++++++++++++++ image-builder/entrypoint.sh | 34 +++++++++++++++++++++++++++ image-builder/requirements.txt | 4 ++++ 3 files changed, 81 insertions(+) create mode 100644 image-builder/Dockerfile create mode 100644 image-builder/entrypoint.sh create mode 100644 image-builder/requirements.txt diff --git a/image-builder/Dockerfile b/image-builder/Dockerfile new file mode 100644 index 0000000..707aec2 --- /dev/null +++ b/image-builder/Dockerfile @@ -0,0 +1,43 @@ +FROM ubuntu:24.04 + +ARG GOSU_VERSION=1.19 + +ENV GOSU_URL="https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64" \ + HOME=/home/openstack \ + PATH=/home/openstack/bin:/home/openstack/.local/bin:/home/openstack/image-builder/images/capi/.local/bin:$PATH \ + IMAGE_BUILDER="https://github.com/kubernetes-sigs/image-builder" + +RUN mkdir -p /home/openstack/bin + +RUN apt update && apt upgrade -y && \ + apt install -y vim git curl unzip findutils diffutils less \ + python3 python-is-python3 python3-pip jq build-essential \ + ca-certificates gnupg \ + python3-openstackclient python3-magnumclient \ + python3-octaviaclient python3-designate && \ + apt clean && rm -rf /var/lib/apt/lists/* && \ + # gosu install start + curl -fsSL $GOSU_URL -o /usr/local/bin/gosu && \ + curl -fsSL $GOSU_URL.asc -o /tmp/gosu.asc && \ + export GNUPGHOME="$(mktemp -d)" && \ + # @tianon's, maintainer of gosu, public key, in case you are wondering + export KEY=B42F6819007F00F88E364FD4036A9C25BF357DD4 && \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys $KEY && \ + gpg --batch --verify /tmp/gosu.asc /usr/local/bin/gosu && \ + gpgconf --kill all && \ + rm -rf "$GNUPGHOME" /tmp/gosu.asc && \ + chmod +x /usr/local/bin/gosu + # gosu install end + +# Begin image-builder +WORKDIR /home/openstack +RUN git clone $IMAGE_BUILDER && \ + cd image-builder/images/capi/ && \ + make deps-openstack +# End image-builder + +COPY entrypoint.sh / +RUN chmod +x /entrypoint.sh + +# Start container +ENTRYPOINT ["/entrypoint.sh"] diff --git a/image-builder/entrypoint.sh b/image-builder/entrypoint.sh new file mode 100644 index 0000000..b38628a --- /dev/null +++ b/image-builder/entrypoint.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -euo pipefail + +USERNAME="openstack" +USER_ID=${OPENSTACK_USER_ID:-1001} +GROUP_ID=${OPENSTACK_GROUP_ID:-1001} + +case "$USER_ID" in + (''|*[!0-9]*) + echo "ERROR: OPENSTACK_USER_ID must be numeric, got '$USER_ID'" >&2; + exit 1;; +esac +case "$GROUP_ID" in + (''|*[!0-9]*) + echo "ERROR: OPENSTACK_GROUP_ID must be numeric, got '$GROUP_ID'" >&2; + exit 1;; +esac +if [ "$USER_ID" -eq 0 ] || [ "$GROUP_ID" -eq 0 ]; then + echo "ERROR: OPENSTACK_USER_ID and OPENSTACK_GROUP_ID must be non-root" >&2 + exit 1 +fi + +if ! getent group "$USERNAME" >/dev/null; then + groupadd -r "$USERNAME" -g "$GROUP_ID" +fi + +if ! id -u "$USERNAME" >/dev/null 2>&1; then + useradd -u "$USER_ID" -g "$USERNAME" -s /bin/bash -c "Openstack user" "$USERNAME" 2>/dev/null +fi + +HOME_DIR=$(getent passwd "$USERNAME" | cut -d: -f6) +chown -R "$USER_ID:$GROUP_ID" "$HOME_DIR" + +exec gosu "$USERNAME" "$@" diff --git a/image-builder/requirements.txt b/image-builder/requirements.txt new file mode 100644 index 0000000..409fa00 --- /dev/null +++ b/image-builder/requirements.txt @@ -0,0 +1,4 @@ +python-openstackclient==8.0.0 +python-magnumclient==4.8.1 +python-octaviaclient==3.11.0 +python-designateclient==6.3.0 From 12b96c3fb572f57462425224b0bd442c5826f36d Mon Sep 17 00:00:00 2001 From: ana espinoza Date: Thu, 16 Jul 2026 16:18:18 -0600 Subject: [PATCH 2/8] Drop down from root; set image-builder bin paths --- image-builder/Dockerfile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/image-builder/Dockerfile b/image-builder/Dockerfile index 707aec2..50d3167 100644 --- a/image-builder/Dockerfile +++ b/image-builder/Dockerfile @@ -29,11 +29,17 @@ RUN apt update && apt upgrade -y && \ chmod +x /usr/local/bin/gosu # gosu install end +USER openstack + # Begin image-builder WORKDIR /home/openstack RUN git clone $IMAGE_BUILDER && \ cd image-builder/images/capi/ && \ make deps-openstack + +# $HOME/.local/bin is the ansible install path +# $HOME/image-builder/images/capi/.local/bin is the packer install path +ENV PATH=$HOME/.local/bin:$HOME/image-builder/images/capi/.local/bin:$PATH # End image-builder COPY entrypoint.sh / From 7d4d0410cd4cb4721524723dc06bef609c414a40 Mon Sep 17 00:00:00 2001 From: ana espinoza Date: Fri, 31 Jul 2026 13:08:52 -0600 Subject: [PATCH 3/8] Scriptify running the containerized workflow --- image-builder/.gitignore | 1 + image-builder/Dockerfile | 2 + image-builder/README.md | 65 ++++++++++++++ image-builder/image-builder.sh | 84 +++++++++++++++++++ .../files/blacklist-act-pedit.conf | 1 + .../unidata-profile/files/dirtyfrag.conf | 3 + .../unidata-profile/files/disable-cifs.conf | 1 + .../roles/unidata-profile/tasks/main.yaml | 27 ++++++ 8 files changed, 184 insertions(+) create mode 100644 image-builder/.gitignore create mode 100644 image-builder/README.md create mode 100755 image-builder/image-builder.sh create mode 100644 image-builder/roles/unidata-profile/files/blacklist-act-pedit.conf create mode 100644 image-builder/roles/unidata-profile/files/dirtyfrag.conf create mode 100644 image-builder/roles/unidata-profile/files/disable-cifs.conf create mode 100644 image-builder/roles/unidata-profile/tasks/main.yaml diff --git a/image-builder/.gitignore b/image-builder/.gitignore new file mode 100644 index 0000000..a03b913 --- /dev/null +++ b/image-builder/.gitignore @@ -0,0 +1 @@ +./logs/ diff --git a/image-builder/Dockerfile b/image-builder/Dockerfile index 50d3167..08c7974 100644 --- a/image-builder/Dockerfile +++ b/image-builder/Dockerfile @@ -41,6 +41,8 @@ RUN git clone $IMAGE_BUILDER && \ # $HOME/image-builder/images/capi/.local/bin is the packer install path ENV PATH=$HOME/.local/bin:$HOME/image-builder/images/capi/.local/bin:$PATH # End image-builder + +WORKDIR /home/openstack/image-builder/images/capi COPY entrypoint.sh / RUN chmod +x /entrypoint.sh diff --git a/image-builder/README.md b/image-builder/README.md new file mode 100644 index 0000000..21cea03 --- /dev/null +++ b/image-builder/README.md @@ -0,0 +1,65 @@ +# How to image-builder + +[Image builder](https://github.com/kubernetes-sigs/image-builder) is a K8s special interest group project that creates "ClusterAPI compatible" images that something like Openstack Magnum can readily use to create cluster nodes. The project makes use of [Packer](https://developer.hashicorp.com/packer), a Hashicorp project that can be used to make machine images for a variety of platforms using a single configuration, and [Ansible](https://docs.ansible.com)--an "IT automation engine" used to configure and run tasks on hosts. + +We make use of it on Jetstream2 cloud to be capable of producing CAPI/K8s ready images that incorporate the most recent vulnerability patches and/or mitigations. It can also be used to further customize images as appropriate. For example, this work will also ensure that the `nsf-common` package is installed, allowing for Network File System mounts. + +## Unidata Quickstart + +Commands run on the `openstack-jetstream2` machine + +1) `cd usg-devops/image-builder` +2) Run `image-builder.sh` +3) If successful, set `kube_version` property on resulting image: + `openstack image set --property kube_version=v${VERSION} $IMAGE_NAME` +4) Edits vars in `create_cluster_template.sh` and run + +# WIP + +## Prerequisites + +You will need: + +- `docker` and `docker-compose` +- A valid `openrc.sh` file with credentials for Jetstream2 +- The `openstack` CLI +- An SSH key-pair; public key available on Jetstream2 + +### Creating security groups to allow SSH traffic + +The host that image-builder runs on needs to be able to SSH into the server that is created to build the image. To this end, we'll create two security groups: + +1) `image-builder`: Opens port 22 (SSH) to any openstack servers with the `image-builder-client` group; will be specified in `var_file.json` +2) `image-builder-client`: A "dummy" security group attached to the server that image-builder runs on; doesn't open any ports + +Create these security groups, and the necessary rule, as follows: + +```bash +openstack security group create --description "Allow machines with this SG to SSH into machines with the image-builder SG" image-builder-client +openstack security group create --description "Open port 22 (SSH) to any openstack servers with the 'image-builder-client' SG" image-builder +openstack security group rule create --protocol tcp --dst-port 22 --remote-group image-builder-client image-builder +``` + +Now add the `image-builder-client` to the Jetstream2 host where image-builder is running: + +```bash +openstack server add security group image-builder-client +``` + +>[!NOTE] +>At Unidata, we've already done these steps and attached the `image-builder-client` security group to the `openstack-jetstream2` machine. + +### Create an SSH keypair + +Image-builder uses `ansible` and thus `ssh` to configure our "source instance" that will be snapshot into the resulting image. On your image-builder client machine you must create an SSH keypair in the usual/preferred manner using `ssh-keygen`. + +Then, use the `openstack` CLI to upload the public key to Jetstream2. We will refer to this keypair resource in one of our `packer` configuration files. + +`openstack keypair create --public-key /path/to/public/key.pub` + +>[!IMPORTANT] +>The `openstack` application credentials (i.e. `openrc.sh` or `clouds.yaml`) used to run the above command must be the same that are supplied to the image-builder workflow; `openstack` is not aware of keypairs created by other users. + +## Running the workflow + +The workflow has been containerized. Thus, there is no need to install or build any additional dependencies other than docker and docker-compose. This workflow is intended to be ran on a Jetstream2 machine diff --git a/image-builder/image-builder.sh b/image-builder/image-builder.sh new file mode 100755 index 0000000..900564d --- /dev/null +++ b/image-builder/image-builder.sh @@ -0,0 +1,84 @@ +usage () { +cat < $LOG_DIR/var_file.json + +docker run -t \ + --name $IMAGE_NAME \ + -e IMAGE_NAME \ + -e PACKER_LOG=1 \ + -e PACKER_LOG_PATH=/image-builder-log/packer_debug.log \ + -e PACKER_VAR_FILES=/image-builder-log/var_file.json \ + --env-file $OPENRC_FILE \ + -v $LOG_DIR:/image-builder-log \ + -v $ROLES_DIR:/home/openstack/image-builder/images/capi/ansible/roles/$NODE_CUSTOM_ROLES_POST \ + -v $KEY_FILE:/home/openstack/.ssh/id_ed25519_packer \ + unidata/image-builder:$TAG make build-openstack-ubuntu-2204 diff --git a/image-builder/roles/unidata-profile/files/blacklist-act-pedit.conf b/image-builder/roles/unidata-profile/files/blacklist-act-pedit.conf new file mode 100644 index 0000000..99a1430 --- /dev/null +++ b/image-builder/roles/unidata-profile/files/blacklist-act-pedit.conf @@ -0,0 +1 @@ +blacklist act_pedit diff --git a/image-builder/roles/unidata-profile/files/dirtyfrag.conf b/image-builder/roles/unidata-profile/files/dirtyfrag.conf new file mode 100644 index 0000000..c8f2f43 --- /dev/null +++ b/image-builder/roles/unidata-profile/files/dirtyfrag.conf @@ -0,0 +1,3 @@ +install esp4 /bin/false +install esp6 /bin/false +install rxrpc /bin/false diff --git a/image-builder/roles/unidata-profile/files/disable-cifs.conf b/image-builder/roles/unidata-profile/files/disable-cifs.conf new file mode 100644 index 0000000..a8d0370 --- /dev/null +++ b/image-builder/roles/unidata-profile/files/disable-cifs.conf @@ -0,0 +1 @@ +blacklist cifs diff --git a/image-builder/roles/unidata-profile/tasks/main.yaml b/image-builder/roles/unidata-profile/tasks/main.yaml new file mode 100644 index 0000000..22d7093 --- /dev/null +++ b/image-builder/roles/unidata-profile/tasks/main.yaml @@ -0,0 +1,27 @@ +- name: Restrict ptrace_scope + ansible.posix.sysctl: + name: "kernel.yama.ptrace_scope" + value: 2 + sysctl_file: /etc/sysctl.d/10-ptrace.conf +- name: Unpack modprobe config files + ansible.builtin.unarchive: + src: ../files/security.tar + dest: /etc/modprobe.d + list_files: true + +- name: Check if nfs-common is needed + raw: which mount.nfs + register: need_nfs + failed_when: false + changed_when: false + # This command should always run, even in check mode + check_mode: false + tags: + - facts +- name: Install nfs-common + raw: + apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y nfs-common + become: true + when: + - need_nfs.rc != 0 From 89d954c1d190693bbc6213c3dd5fead98282ff8f Mon Sep 17 00:00:00 2001 From: ana espinoza Date: Fri, 31 Jul 2026 14:15:15 -0600 Subject: [PATCH 4/8] Some fixes --- image-builder/Dockerfile | 7 ++++--- image-builder/image-builder.sh | 28 ++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/image-builder/Dockerfile b/image-builder/Dockerfile index 08c7974..b64b64d 100644 --- a/image-builder/Dockerfile +++ b/image-builder/Dockerfile @@ -26,8 +26,10 @@ RUN apt update && apt upgrade -y && \ gpg --batch --verify /tmp/gosu.asc /usr/local/bin/gosu && \ gpgconf --kill all && \ rm -rf "$GNUPGHOME" /tmp/gosu.asc && \ - chmod +x /usr/local/bin/gosu + chmod +x /usr/local/bin/gosu && \ # gosu install end + useradd openstack && \ + chown -R openstack /home/openstack USER openstack @@ -44,8 +46,7 @@ ENV PATH=$HOME/.local/bin:$HOME/image-builder/images/capi/.local/bin:$PATH WORKDIR /home/openstack/image-builder/images/capi -COPY entrypoint.sh / -RUN chmod +x /entrypoint.sh +COPY --chmod=+x entrypoint.sh / # Start container ENTRYPOINT ["/entrypoint.sh"] diff --git a/image-builder/image-builder.sh b/image-builder/image-builder.sh index 900564d..53b5d4c 100755 --- a/image-builder/image-builder.sh +++ b/image-builder/image-builder.sh @@ -1,19 +1,31 @@ usage () { cat < Date: Wed, 26 Aug 2026 13:25:54 -0600 Subject: [PATCH 5/8] Run after setting openrc env vars --- image-builder/image-builder.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/image-builder/image-builder.sh b/image-builder/image-builder.sh index 53b5d4c..be6e275 100755 --- a/image-builder/image-builder.sh +++ b/image-builder/image-builder.sh @@ -83,13 +83,21 @@ VAR_FILE ) echo $VAR_FILE > $LOG_DIR/var_file.json +source $OPENRC_PATH + docker run -t \ --name $IMAGE_NAME \ -e IMAGE_NAME \ -e PACKER_LOG=1 \ -e PACKER_LOG_PATH=/image-builder-log/packer_debug.log \ -e PACKER_VAR_FILES=/image-builder-log/var_file.json \ - --env-file $OPENRC_PATH \ + -e OS_AUTH_TYPE \ + -e OS_AUTH_URL \ + -e OS_IDENTITY_API_VERSION \ + -e OS_REGION_NAME \ + -e OS_INTERFACE \ + -e OS_APPLICATION_CREDENTIAL_ID \ + -e OS_APPLICATION_CREDENTIAL_SECRET \ -v $LOG_DIR:/image-builder-log \ -v $ROLES_DIR:/home/openstack/image-builder/images/capi/ansible/roles/$NODE_CUSTOM_ROLES_POST \ -v $KEY_FILE:/home/openstack/.ssh/id_ed25519_packer \ From a2da2b66086dd7f1809532a21e45be3ed03fa31c Mon Sep 17 00:00:00 2001 From: ana espinoza Date: Thu, 27 Aug 2026 14:26:13 -0600 Subject: [PATCH 6/8] Split image-builder.sh into two parts (cont below) `image-builder.sh` is now a wrapper that sets environment variables and runs the docker container. `run.sh` will be COPY'ed into the Dockerfile and replace the current `entrypoint.sh` script. This script makes use of the environment variables set by `image-builder.sh` to create packer's `var_file.json`and run image-builder. Eventually, `run.sh` will also ensure that the resulting `kube_version` property is set in the resulting image, which is required by openstack Magnum, and will finally create a cluster template using the newly created image. --- image-builder/env.sh | 0 image-builder/image-builder.sh | 212 +++++++++++++++++++++------------ image-builder/run.sh | 67 +++++++++++ 3 files changed, 204 insertions(+), 75 deletions(-) create mode 100644 image-builder/env.sh create mode 100644 image-builder/run.sh diff --git a/image-builder/env.sh b/image-builder/env.sh new file mode 100644 index 0000000..e69de29 diff --git a/image-builder/image-builder.sh b/image-builder/image-builder.sh index be6e275..0fcb0da 100755 --- a/image-builder/image-builder.sh +++ b/image-builder/image-builder.sh @@ -1,20 +1,118 @@ usage () { cat < \ + --source-image \ + --network \ + [ --docker-image-tag ] \ + [ --source-image-flavor ] \ + [ --image-name-base ] \ + [ --image-name-suffix ] \ + [ --ssh-username ] \ + [ --ssh-keypair-name ] \ + [ --ssh-key-file ] \ + [ --node-custom-roles-post ] \ + [ --help ] +--------------------------------------------------------------------- +Usage: +Alternatively, create an env.sh setting the environment variables as in the example below: +``` +# Required arguments: +OPENRC_PATH= +SOURCE_IMAGE= +NETWORK= + +# Optional arguments: +DOCKER_IMAGE_TAG=latest +SOURCE_IMAGE_FLAVOR=m3.quad +IMAGE_NAME_BASE=unidata-ubuntu-magnum +IMAGE_NAME_SUFFIX=$(date +%Y%m%d_%H%M) +SSH_USERNAME=ubuntu +SSH_KEYPAIR_NAME=packer +SSH_KEY_FILE=~/.ssh/id_ed25519_packer +NODE_CUSTOM_ROLES_POST=unidata-profile +``` +Then: +[ IMAGE_BUILDER_ENV="/path/to/env.sh" ] ./$0 [ options ] + +In this case, options override values from env.sh USAGE } -TAG=${TAG:-latest} +# First source environment file +IMAGE_BUILDER_ENV=${IMAGE_BUILDER_ENV:-"./env.sh"} +ls -1 &> /dev/null $IMAGE_BUILDER_ENV || { echo "[ ERROR ] $IMAGE_BUILDER_ENV not found! Exiting ..."; exit 1; } +source $IMAGE_BUILDER_ENV -# SOURCE_IMAGE=$(openstack image show Featured-Minimal-Ubuntu22 -f value -c id) -# NETWORK=${NETWORK:-$(openstack network show auto_allocated_network -f value -c id)} +# Parse script args +# Override environment file vars with those parsed +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + --openrc-path) + OPENRC_PATH="$2" + shift 2 + ;; + --source-image) + SOURCE_IMAGE="$2" + shift 2 + ;; + --network) + NETWORK="$2" + shift 2 + ;; + --docker-image-tag) + DOCKER_IMAGE_TAG="$2" + shift 2 + ;; + --source-image-flavor) + SOURCE_IMAGE_FLAVOR="$2" + shift 2 + ;; + --image-name-base) + IMAGE_NAME_BASE="$2" + shift 2 + ;; + --image-name-suffix) + IMAGE_NAME_SUFFIX="$2" + shift 2 + ;; + --ssh-username) + SSH_USERNAME="$2" + shift 2 + ;; + --ssh-keypair-name) + SSH_KEYPAIR_NAME="$2" + shift 2 + ;; + --ssh-key-file) + SSH_KEY_FILE="$2" + shift 2 + ;; + --node-custom-roles-post) + NODE_CUSTOM_ROLES_POST="$2" + shift 2 + ;; + --help) + usage + exit 0 + ;; + *) + echo "[ ERROR ] Unknown option: $key" + usage + exit 1 + ;; + esac +done +# Ensure required variables are set and ensure everythiing else is defaulted +if [[ -z "$OPENRC_PATH" ]]; then + echo "!!! ERROR: Must provide an OPENRC_PATH" + usage + exit 1 +fi if [[ -z "$SOURCE_IMAGE" ]]; then echo "!!! ERROR: Must provide a SOURCE_IMAGE" usage @@ -25,80 +123,44 @@ if [[ -z "$NETWORK" ]]; then usage exit 1 fi +export OPENRC_PATH \ + SOURCE_IMAGE \ + NETWORK \ + DOCKER_IMAGE_TAG=${DOCKER_IMAGE_TAG:-"latest"} \ + SOURCE_IMAGE_FLAVOR=${SOURCE_IMAGE_FLAVOR:-"m3.quad"} \ + IMAGE_NAME_BASE=${IMAGE_NAME_BASE:-"unidata-ubuntu-magnum"} \ + IMAGE_NAME_SUFFIX=${IMAGE_NAME_SUFFIX:-"$(date +%Y%m%d_%H%M)"} \ + SSH_USERNAME=${SSH_USERNAME:-"ubuntu"} \ + SSH_KEYPAIR_NAME=${SSH_KEYPAIR_NAME:-"packer"} \ + SSH_KEY_FILE=${SSH_KEY_FILE:-"~/.ssh/id_ed25519_packer"} \ + NODE_CUSTOM_ROLES_POST=${NODE_CUSTOM_ROLES_POST:-"unidata-profile"} -FLAVOR=${FLAVOR:-m3.quad} - -IMAGE_NAME_BASE=${IMAGE_NAME_BASE:-unidata-ubuntu-magnum} -TIMESTAMP=$(date +%Y%m%d_%H%M) -IMAGE_NAME_SUFFIX=${IMAGE_NAME_SUFFIX:-$TIMESTAMP} -# export necessary for container to inherit with a `docker run -e IMAGE_NAME` -export IMAGE_NAME=${IMAGE_NAME:-$IMAGE_NAME_BASE-$IMAGE_NAME_SUFFIX} - -# If wanting to base off of something that isn't a featured ubuntu image, give ourselves a backdoor -SSH_USERNAME=${SSH_USERNAME:-ubuntu} - -# The keypair name to use, as recognized by JS2/openstack -SSH_KEYPAIR_NAME=${SSH_KEYPAIR_NAME:-packer} - -# Will be mounted via docker, so we need the full path -KEY_FILE=${KEY_FILE:-~/.ssh/id_ed25519_packer} -KEY_FILE=$(realpath $KEY_FILE) +# Export "derived" variables +# NOTE: If a NODE_CUSTOM_ROLES_POST isn't set, the resulting bind mount will mount an *empty or non-existent* $(pwd)/roles directory into the image-builder/images/capi/ansible/roles directory, breaking everything :) +# This must be fixed +export \ + IMAGE_NAME=${IMAGE_NAME:-"$IMAGE_NAME_BASE-$IMAGE_NAME_SUFFIX"} \ + ROLES_DIR=$(pwd)/roles/$NODE_CUSTOM_ROLES_POST + LOG_DIR=$(pwd)/logs/$IMAGE_NAME -# Ditto, but error out if one is not provided -if [[ -z "$OPENRC_PATH" ]]; then - echo "!!! ERROR: Must provide an OPENRC_PATH !!!" - usage - exit 1 -fi -OPENRC_PATH=$(realpath $OPENRC_FILE) - -NODE_CUSTOM_ROLES_POST=${NODE_CUSTOM_ROLES_POST:-unidata-profile} - -# Setup directories -LOG_DIR=$(pwd)/logs/$IMAGE_NAME -ROLES_DIR=$(pwd)/roles/$NODE_CUSTOM_ROLES_POST +# Ensure base log dir exists mkdir -p $LOG_DIR -# Construct a packer var_file.json -VAR_FILE=$(cat < $LOG_DIR/var_file.json - -source $OPENRC_PATH - +# Run docker image +# Bind mount log dir +# Bind mount ssh key file +# Bind mount roles dir +# Set env vars with -e option to docker run docker run -t \ --name $IMAGE_NAME \ - -e IMAGE_NAME \ -e PACKER_LOG=1 \ -e PACKER_LOG_PATH=/image-builder-log/packer_debug.log \ -e PACKER_VAR_FILES=/image-builder-log/var_file.json \ - -e OS_AUTH_TYPE \ - -e OS_AUTH_URL \ - -e OS_IDENTITY_API_VERSION \ - -e OS_REGION_NAME \ - -e OS_INTERFACE \ - -e OS_APPLICATION_CREDENTIAL_ID \ - -e OS_APPLICATION_CREDENTIAL_SECRET \ + -e SOURCE_IMAGE -e NETWORK -e SOURCE_IMAGE_FLAVOR \ + -e IMAGE_NAME -e SSH_USERNAME -e SSH_KEYPAIR_NAME \ + -e SSH_KEY_FILE \ -v $LOG_DIR:/image-builder-log \ -v $ROLES_DIR:/home/openstack/image-builder/images/capi/ansible/roles/$NODE_CUSTOM_ROLES_POST \ -v $KEY_FILE:/home/openstack/.ssh/id_ed25519_packer \ - unidata/image-builder:$TAG make build-openstack-ubuntu-2204 + -v $OPENRC_PATH:/home/openstack/openrc.sh \ + unidata/image-builder:${DOCKER_IMAGE_TAG} diff --git a/image-builder/run.sh b/image-builder/run.sh new file mode 100644 index 0000000..990ee24 --- /dev/null +++ b/image-builder/run.sh @@ -0,0 +1,67 @@ +# run.sh +# Run image builder +# Ensure kube_version property is set in resulting image +# Create a new cluster template + +set -e +set -o pipefail + +# log function +RUN_LOG_DIR="/image-builder-log" +log () { + tee -a $RUN_LOG_DIR/run.log +} + +# print date +info () { + echo "[ INFO ] $(date "+%Y-%m-%d %H:%M") -- $@" +} + +source /home/openstack/openrc.sh + +############### Run image builder ############### + +info "Running image builder" | log + +# Get UUIDs for the SOURCE_IMAGE and NETWORK +SOURCE_IMAGE_UUID=$(openstack image show $SOURCE_IMAGE -f value -c id) +NETWORK_UUID=$(openstack image show $NETWORK -f value -c id) +info "SOURCE_IMAGE_UUID=$SOURCE_IMAGE_UUID" | log +info "NETWORK_UUID=$NETWORK_UUID" | log + +# Ensure $FLAVOR and $KEYPAIR exist and are available to your openstack user +info "Ensuring flavor $FLAVOR is a valid openstack flavor" | log +openstack flavor show $FLAVOR -c name 2>&1 | log + +info "Ensuring keypair $SSH_KEYPAIR_NAME is a valid openstack keypair" | log +openstack keypair show $SSH_KEYPAIR_NAME 2>&1 | log + +# Construct a packer var_file.json +info "Creating $RUN_LOG_DIR/var_file.json" | log +VAR_FILE=$(cat < $RUN_LOG_DIR/var_file.json + +# Build the image +info "Building new CAPI image" +info "See $RUN_LOG_DIR/run.log" +make build-openstack-ubuntu-2204 | log From f4364e66f4f2def9b9d1ef1d9ec5e27fc4e14ab5 Mon Sep 17 00:00:00 2001 From: ana espinoza Date: Thu, 27 Aug 2026 14:41:55 -0600 Subject: [PATCH 7/8] Remove entrypoint.sh; add run.sh --- image-builder/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/image-builder/Dockerfile b/image-builder/Dockerfile index b64b64d..b757915 100644 --- a/image-builder/Dockerfile +++ b/image-builder/Dockerfile @@ -46,7 +46,7 @@ ENV PATH=$HOME/.local/bin:$HOME/image-builder/images/capi/.local/bin:$PATH WORKDIR /home/openstack/image-builder/images/capi -COPY --chmod=+x entrypoint.sh / +COPY --chmod=+x run.sh ./run.sh # Start container -ENTRYPOINT ["/entrypoint.sh"] +ENTRYPOINT ["run.sh"] From 4f5449c55f782887ddc7ab2136ff9e35ebe6c7e1 Mon Sep 17 00:00:00 2001 From: ana espinoza Date: Thu, 27 Aug 2026 15:05:41 -0600 Subject: [PATCH 8/8] Some fixes (cont) usage: - Property escape backslash characters - Remove backticks, since these spawn a subshell, whoops! KEY_FILE corrected to SSH_KEY_FILE Use `realpath` to ensure we refer to absolute paths in docker bind mounts: - OPENRC_PATH - SSH_KEY_FILE --- image-builder/image-builder.sh | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/image-builder/image-builder.sh b/image-builder/image-builder.sh index 0fcb0da..b263130 100755 --- a/image-builder/image-builder.sh +++ b/image-builder/image-builder.sh @@ -2,23 +2,23 @@ usage () { cat < \ - --source-image \ - --network \ - [ --docker-image-tag ] \ - [ --source-image-flavor ] \ - [ --image-name-base ] \ - [ --image-name-suffix ] \ - [ --ssh-username ] \ - [ --ssh-keypair-name ] \ - [ --ssh-key-file ] \ - [ --node-custom-roles-post ] \ +[ IMAGE_BUILDER_ENV="/path/to/env.sh" ] ./$0 \\ + --openrc-path \\ + --source-image \\ + --network \\ + [ --docker-image-tag ] \\ + [ --source-image-flavor ] \\ + [ --image-name-base ] \\ + [ --image-name-suffix ] \\ + [ --ssh-username ] \\ + [ --ssh-keypair-name ] \\ + [ --ssh-key-file ] \\ + [ --node-custom-roles-post ] \\ [ --help ] --------------------------------------------------------------------- Usage: Alternatively, create an env.sh setting the environment variables as in the example below: -``` +########### env.sh ########## # Required arguments: OPENRC_PATH= SOURCE_IMAGE= @@ -33,7 +33,7 @@ SSH_USERNAME=ubuntu SSH_KEYPAIR_NAME=packer SSH_KEY_FILE=~/.ssh/id_ed25519_packer NODE_CUSTOM_ROLES_POST=unidata-profile -``` +########### env.sh ########## Then: [ IMAGE_BUILDER_ENV="/path/to/env.sh" ] ./$0 [ options ] @@ -123,8 +123,7 @@ if [[ -z "$NETWORK" ]]; then usage exit 1 fi -export OPENRC_PATH \ - SOURCE_IMAGE \ +export SOURCE_IMAGE \ NETWORK \ DOCKER_IMAGE_TAG=${DOCKER_IMAGE_TAG:-"latest"} \ SOURCE_IMAGE_FLAVOR=${SOURCE_IMAGE_FLAVOR:-"m3.quad"} \ @@ -132,9 +131,12 @@ export OPENRC_PATH \ IMAGE_NAME_SUFFIX=${IMAGE_NAME_SUFFIX:-"$(date +%Y%m%d_%H%M)"} \ SSH_USERNAME=${SSH_USERNAME:-"ubuntu"} \ SSH_KEYPAIR_NAME=${SSH_KEYPAIR_NAME:-"packer"} \ - SSH_KEY_FILE=${SSH_KEY_FILE:-"~/.ssh/id_ed25519_packer"} \ NODE_CUSTOM_ROLES_POST=${NODE_CUSTOM_ROLES_POST:-"unidata-profile"} +# Ensure absolute paths are sent to the docker run command +export OPENRC_PATH=$(realpath $OPENRC_PATH) \ + SSH_KEY_FILE=$(realpath ${SSH_KEY_FILE:-"~/.ssh/id_ed25519_packer"}) \ + # Export "derived" variables # NOTE: If a NODE_CUSTOM_ROLES_POST isn't set, the resulting bind mount will mount an *empty or non-existent* $(pwd)/roles directory into the image-builder/images/capi/ansible/roles directory, breaking everything :) # This must be fixed @@ -161,6 +163,6 @@ docker run -t \ -e SSH_KEY_FILE \ -v $LOG_DIR:/image-builder-log \ -v $ROLES_DIR:/home/openstack/image-builder/images/capi/ansible/roles/$NODE_CUSTOM_ROLES_POST \ - -v $KEY_FILE:/home/openstack/.ssh/id_ed25519_packer \ + -v $SSH_KEY_FILE:/home/openstack/.ssh/id_ed25519_packer \ -v $OPENRC_PATH:/home/openstack/openrc.sh \ unidata/image-builder:${DOCKER_IMAGE_TAG}