Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-oci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
set -e
mkdir -p images-out
for flavour in bread bread-chisel-releases; do
for ver in 24.04 25.10 26.04 26.10; do
for ver in 22.04 24.04 25.10 26.04 26.10; do
tag="$flavour:$ver-${{ matrix.arch }}"
echo "==> saving $tag"
docker save "$tag" -o "images-out/$flavour-$ver-${{ matrix.arch }}.tar"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/push-ghcr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
run: |
set -e
for flavour in bread bread-chisel-releases; do
for ver in 24.04 25.10 26.04 26.10; do
for ver in 22.04 24.04 25.10 26.04 26.10; do
m="$flavour-$ver-manifest"
buildah manifest create "$m"
for arch in amd64 arm64 s390x ppc64le; do
Expand All @@ -71,7 +71,7 @@ jobs:
run: |
set -e
for flavour in bread bread-chisel-releases; do
for ver in 24.04 25.10 26.04 26.10; do
for ver in 22.04 24.04 25.10 26.04 26.10; do
cosign sign --yes \
"$REGISTRY/${{ github.repository }}/$flavour:$ver"
done
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ prebuilt docker images + ready-to-use spread yamls so you can drop Canonical [`s

## flavours

two image flavours, each for ubuntu 24.04, 25.10, 26.04, 26.10 x amd64 / arm64 / s390x / ppc64le, published as multiarch tags at `ghcr.io/lczyk/spread-bread/<flavour>:<ver>`. heads-up: s390x + ppc64le images build under qemu and ship untested (no native runners); amd64 + arm64 are tested on every release:
two image flavours, each for ubuntu 22.04, 24.04, 25.10, 26.04, 26.10 x amd64 / arm64 / s390x / ppc64le, published as multiarch tags at `ghcr.io/lczyk/spread-bread/<flavour>:<ver>`. heads-up: s390x + ppc64le images build under qemu and ship untested (no native runners); amd64 + arm64 are tested on every release:

- **`bread`** -- base: ubuntu + sshd. general-purpose spread system; the test suite installs whatever else it needs.
- **`bread-chisel-releases`** -- bread + `chisel` + the shell + container tooling typically needed by [chisel-releases](https://github.com/canonical/chisel-releases) spread tests (curl, wget, git, jq, file, sudo, tree, docker, skopeo). `chisel` and `docker` are built from source (canonical/chisel pinned by SHA, docker/cli pinned by version tag) so the bundled binaries are go 1.25+ and survive qemu emulation.
Expand All @@ -38,8 +38,8 @@ allocate inside the yaml `docker run`s the matching multiarch ghcr image; `--pla

available yamls in the release:

- `bread-{24.04,25.10,26.04,26.10}.yaml`
- `bread-chisel-releases-{24.04,25.10,26.04,26.10}.yaml`
- `bread-{22.04,24.04,25.10,26.04,26.10}.yaml`
- `bread-chisel-releases-{22.04,24.04,25.10,26.04,26.10}.yaml`

### networking (linux vs macOS)

Expand Down
37 changes: 37 additions & 0 deletions images/Dockerfile.bread-22.04
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Bread image: Ubuntu 22.04 + sshd only.

FROM docker.io/library/ubuntu:22.04@sha256:0e0a0fc6d18feda9db1590da249ac93e8d5abfea8f4c3c0c849ce512b5ef8982

# Skip man/doc/info install for every subsequent package -- dpkg's man-db
# trigger is a major cost under qemu emulation (s390x / ppc64le). Persists in
# the image fs, so the bread-chisel-releases install inherits the exclusion.
RUN printf 'path-exclude /usr/share/man/*\npath-exclude /usr/share/doc/*\npath-exclude /usr/share/info/*\n' \
> /etc/dpkg/dpkg.cfg.d/01-nodoc && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssh-server && \
mkdir /var/run/sshd || true && \
echo 'root:bread' | chpasswd && \
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config && \
echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config && \
apt-get clean && rm -rf /var/lib/apt/lists/*

# Replace /etc/pam.d/sshd with a pam_permit-only stack. These images are
# throwaway test containers; we don't care about password validity, and the
# default pam_unix stack pulls in unix_chkpwd which can fail to load
# libcrypt.so.1 inside privileged containers on some hosts.
RUN printf '%s\n' \
'auth required pam_permit.so' \
'account required pam_permit.so' \
'session required pam_permit.so' \
'password required pam_permit.so' \
> /etc/pam.d/sshd

# Insecure-image warning. Prints on interactive logins (ssh, `docker exec -it`).
# See hack/bread-warning.sh for design notes.
COPY hack/banner.txt /etc/bread-banner.txt
COPY hack/bread-warning.sh /etc/profile.d/00-bread-warning.sh
RUN chmod 0644 /etc/bread-banner.txt /etc/profile.d/00-bread-warning.sh && \
printf '\n# bread-warning\n[ -r /etc/profile.d/00-bread-warning.sh ] && . /etc/profile.d/00-bread-warning.sh\n' >> /root/.bashrc

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
30 changes: 30 additions & 0 deletions images/Dockerfile.bread-chisel-releases-22.04
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Bread-chisel-releases image: bread + chisel + tooling commonly
# expected by chisel-releases spread tests.
#
# chisel is built from source (see hack/build_binaries.sh) and dropped
# into ./cache/binaries/chisel-<arch> by the binaries stamp rule before
# this image is built.

ARG BASE_TAG=22.04-amd64
ARG BUILD_ARCH=amd64
FROM bread:${BASE_TAG}
ARG BUILD_ARCH

RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
curl wget git jq file sudo tree \
skopeo iproute2 && \
apt-get clean && rm -rf /var/lib/apt/lists/*

COPY cache/binaries/chisel-hacked-${BUILD_ARCH} /usr/local/bin/chisel-hacked
COPY cache/binaries/docker-${BUILD_ARCH} /usr/local/bin/docker
# NOTE: ship only the hacked chisel; `chisel` is a symlink to it so bare
# `chisel` invocations (e.g. the spread install-slices lib) pick up CHISEL_HACKS.
RUN chmod +x /usr/local/bin/chisel-hacked /usr/local/bin/docker && \
ln -s chisel-hacked /usr/local/bin/chisel

# Lazy-apt shim: auto-runs apt-get update on first non-update apt/apt-get call,
# then self-destructs so subsequent calls hit real apt directly.
# Needed because the image is built with apt lists wiped (apt-get clean above).
COPY hack/lazy-apt.sh /usr/local/bin/apt-get
RUN chmod +x /usr/local/bin/apt-get && cp /usr/local/bin/apt-get /usr/local/bin/apt
154 changes: 154 additions & 0 deletions inlined/bread-22.04.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
project: bread

path: /bread

environment:
PROJECT_PATH: /bread
SHARED_LIBRARIES: $PROJECT_PATH/tests/lib
PATH: $PATH:$SHARED_LIBRARIES

exclude:
- .git
- .github

backends:
docker:
type: adhoc
allocate: |
#spellchecker: ignore noninteractice

set -e

ver=$(echo "$SPREAD_SYSTEM" | cut -d- -f2) # e.g., ubuntu-24.04-amd64 -> 24.04
arch=$(echo "$SPREAD_SYSTEM" | cut -d- -f3) # e.g., ubuntu-24.04-amd64 -> amd64
echo "ver: $ver"
echo "arch: $arch"

image="ghcr.io/lczyk/spread-bread/bread:$ver"
echo "image: $image"

# Networking mode: how spread reaches the container's sshd.
# bridge -- connect to the container's docker bridge IP. Works where the host
# shares the docker bridge network (linux native), so 172.17.x.x is
# routable from the spread host.
# publish -- publish sshd to 127.0.0.1:<ephemeral host port> and connect there.
# Needed where the bridge IP is not host-routable, e.g. Docker
# Desktop on macOS (docker runs in a VM).
# Default is per-OS; override with BREAD_NET=bridge|publish.
mode="${BREAD_NET:-}"
if [ -z "$mode" ]; then
case "$(uname -s)" in
Darwin) mode=publish ;;
*) mode=bridge ;;
esac
fi
case "$mode" in
bridge|publish) ;;
*) echo "BREAD_NET must be 'bridge' or 'publish', got: '$mode'" >&2; exit 1 ;;
esac
echo "net mode: $mode"

if [ "$mode" = bridge ]; then
# Use a counter file to ensure unique container names.
# snippet thanks to @lengau
# https://github.com/canonical/charmcraft/blob/120a00a50f7ed3d0ae2fc2bea69e2e43b68b1594/spread.yaml#L72-L79
sleep 0.$RANDOM # Minimize chances of a race condition
export counter_file=".spread-worker-num"
instance_num=$(
flock -x $counter_file bash -c '
[ -s $counter_file ] || echo 0 > $counter_file
num=$(< $counter_file) && echo $num
echo $(( $num + 1 )) > $counter_file'
)
container_name="bread-${ver}-${arch}-${instance_num}"
publish_flag=""
else
# publish mode: no shared counter file (no flock dependency); a pid+RANDOM
# suffix is unique enough for a name. Docker picks the free host port.
container_name="bread-${ver}-${arch}-$$-${RANDOM}"
publish_flag="-p 127.0.0.1::22"
fi
echo "container_name: $container_name"

docker run \
--rm \
--platform "linux/$arch" \
-e DEBIAN_FRONTEND=noninteractice \
-e "usr=$SPREAD_SYSTEM_USERNAME" \
-e "pass=$SPREAD_SYSTEM_PASSWORD" \
$publish_flag \
--name "$container_name" \
-d "$image"

until docker exec "$container_name" pgrep sshd; do sleep 1; done

if [ "$mode" = publish ]; then
# The ephemeral host port docker mapped to the container's sshd.
port=$(docker port "$container_name" 22 | head -n1 | cut -d: -f2)
[ -n "$port" ] || { echo "could not find published sshd port for $container_name" >&2; exit 1; }
ADDRESS "127.0.0.1:$port"
else
ADDRESS "$(docker inspect "$container_name" --format '{{.NetworkSettings.Networks.bridge.IPAddress}}')"
fi
discard: |
#!/bin/bash

set -e

echo "Discarding container for system: $SPREAD_SYSTEM_ADDRESS"

# The address format tells us how the container was allocated (see the allocate
# script's BREAD_NET modes): "host:port" -> publish mode, bare IP -> bridge mode.
# Inferring from the address makes discard robust even if BREAD_NET is flipped
# between allocate and discard.
container_name=""
case "$SPREAD_SYSTEM_ADDRESS" in
*:*)
# publish mode: match the container by its published host port.
target_port="${SPREAD_SYSTEM_ADDRESS##*:}"
for cid in $(docker ps -a --format '{{.ID}}'); do
if docker port "$cid" 22 2>/dev/null | grep -q ":${target_port}\$"; then
container_name=$(docker inspect "$cid" --format '{{.Name}}' | sed 's#^/##')
break
fi
done
not_found="No container found with published port: $target_port"
;;
*)
# bridge mode: match the container by its bridge IP address.
for cid in $(docker ps -a --filter "network=bridge" --format '{{.ID}}'); do
cname=$(docker inspect "$cid" --format '{{.Name}}' | sed 's/^\/\(.*\)/\1/')
cip=$(docker inspect "$cid" --format '{{.NetworkSettings.Networks.bridge.IPAddress}}' || echo "")
if [ "$cip" == "$SPREAD_SYSTEM_ADDRESS" ]; then
container_name="$cname"
break
fi
done
not_found="No container found with IP address: $SPREAD_SYSTEM_ADDRESS"
;;
esac

if [ -n "$container_name" ]; then
echo "Removing container: $container_name"
docker rm -f "$container_name" 2>/dev/null || true
else
echo "$not_found"
exit 1
fi
systems:
- ubuntu-22.04-amd64:
password: bread
workers: 2
- ubuntu-22.04-arm64:
password: bread
workers: 2
- ubuntu-22.04-s390x:
password: bread
workers: 2
- ubuntu-22.04-ppc64le:
password: bread
workers: 2

suites:
tests/:
summary: Replace with your own suites
Loading