diff --git a/.github/workflows/integration_test_app.yaml b/.github/workflows/integration_test_app.yaml index c81933f3..4182b5a9 100644 --- a/.github/workflows/integration_test_app.yaml +++ b/.github/workflows/integration_test_app.yaml @@ -38,7 +38,7 @@ jobs: - uses: canonical/setup-lxd@v0.1.3 - uses: actions/setup-python@v6 with: - python-version: '3.14' + python-version: "3.14" - name: Install tox run: | sudo apt-get update diff --git a/app/pyproject.toml b/app/pyproject.toml index 01406142..9499d32f 100644 --- a/app/pyproject.toml +++ b/app/pyproject.toml @@ -3,7 +3,7 @@ [project] name = "github-runner-image-builder" -version = "0.14.0" +version = "0.14.1" authors = [ { name = "Canonical IS DevOps", email = "is-devops-team@canonical.com" }, ] diff --git a/app/src/github_runner_image_builder/openstack_builder.py b/app/src/github_runner_image_builder/openstack_builder.py index 36cc7f63..ff04cf16 100644 --- a/app/src/github_runner_image_builder/openstack_builder.py +++ b/app/src/github_runner_image_builder/openstack_builder.py @@ -344,6 +344,11 @@ def run( script_secrets=image_config.script_config.script_secrets, ssh_conn=ssh_conn, ) + logger.info("Cleaning cloud-init state before snapshot.") + ssh_conn.run( + "sudo cloud-init clean --logs --machine-id --seed --configs all", + timeout=EXTERNAL_SCRIPT_GENERAL_TIMEOUT, + ) _shutoff_server(conn=conn, server=builder) image = store.create_snapshot( cloud_name=cloud_config.cloud_name, diff --git a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 index 90647de0..f27f3e86 100644 --- a/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 +++ b/app/src/github_runner_image_builder/templates/cloud-init.sh.j2 @@ -166,6 +166,25 @@ function configure_system_users() { /usr/sbin/groupadd -f microk8s /usr/sbin/groupadd -f docker /usr/sbin/usermod --append --groups docker,microk8s,lxd,sudo ubuntu + + # Create runner user as alias to ubuntu for GARM compatibility. + # GARM expects a runner user with /home/runner/actions-runner path. + echo "Configuring runner user alias for GARM compatibility" + UBUNTU_UID=$(/usr/bin/id -u ubuntu) + UBUNTU_GID=$(/usr/bin/id -g ubuntu) + # --non-unique: allow reusing ubuntu's UID (duplicate UIDs are rejected by default). + # --uid/--gid: share ubuntu's UID/GID so both users have identical file permissions. + # --no-create-home: skip creating /home/runner; runner's home is set to /home/ubuntu instead. + # 2>/dev/null || true: ignore errors (e.g. runner user already exists) without failing the script. + /usr/sbin/useradd --non-unique --uid "$UBUNTU_UID" --gid "$UBUNTU_GID" --no-create-home --home-dir /home/ubuntu runner 2>/dev/null || true + /usr/sbin/usermod --append --groups docker,microk8s,lxd,sudo runner 2>/dev/null || true + # Symlink /home/runner -> /home/ubuntu so GARM's hardcoded /home/runner/actions-runner path resolves correctly. + # If /home/runner exists as a plain directory (not a symlink), remove it first; otherwise ln -sfnT would + # create the symlink inside the directory instead of replacing it. + # -T: treat destination as a file path, never as a directory target (prevents ln from creating the + # symlink inside /home/runner if it exists as a directory after rmdir fails). + [ -d /home/runner ] && ! [ -L /home/runner ] && rmdir /home/runner 2>/dev/null || true + /usr/bin/ln -sfnT /home/ubuntu /home/runner } diff --git a/app/tests/integration/conftest.py b/app/tests/integration/conftest.py index 36ffebcc..c5bf881d 100644 --- a/app/tests/integration/conftest.py +++ b/app/tests/integration/conftest.py @@ -152,10 +152,10 @@ def openstack_connection_fixture( with openstack.connect(cloud_name) as conn: yield conn - images = conn.list_images() - for image in images: - if str(image.name).startswith(test_id): - conn.delete_image(image) + # images = conn.list_images() + # for image in images: + # if str(image.name).startswith(test_id): + # conn.delete_image(image) @pytest.fixture(scope="module", name="dockerhub_mirror") diff --git a/app/tests/unit/test_openstack_builder.py b/app/tests/unit/test_openstack_builder.py index 26205fa3..3647bf2f 100644 --- a/app/tests/unit/test_openstack_builder.py +++ b/app/tests/unit/test_openstack_builder.py @@ -878,6 +878,25 @@ def test__generate_cloud_init_script( /usr/sbin/groupadd -f microk8s /usr/sbin/groupadd -f docker /usr/sbin/usermod --append --groups docker,microk8s,lxd,sudo ubuntu + + # Create runner user as alias to ubuntu for GARM compatibility. + # GARM expects a runner user with /home/runner/actions-runner path. + echo "Configuring runner user alias for GARM compatibility" + UBUNTU_UID=$(/usr/bin/id -u ubuntu) + UBUNTU_GID=$(/usr/bin/id -g ubuntu) + # --non-unique: allow reusing ubuntu's UID (duplicate UIDs are rejected by default). + # --uid/--gid: share ubuntu's UID/GID so both users have identical file permissions. + # --no-create-home: skip creating /home/runner; runner's home is set to /home/ubuntu instead. + # 2>/dev/null || true: ignore errors (e.g. runner user already exists) without failing the script. + /usr/sbin/useradd --non-unique --uid "$UBUNTU_UID" --gid "$UBUNTU_GID" --no-create-home --home-dir /home/ubuntu runner 2>/dev/null || true + /usr/sbin/usermod --append --groups docker,microk8s,lxd,sudo runner 2>/dev/null || true + # Symlink /home/runner -> /home/ubuntu so GARM's hardcoded /home/runner/actions-runner path resolves correctly. + # If /home/runner exists as a plain directory (not a symlink), remove it first; otherwise ln -sfnT would + # create the symlink inside the directory instead of replacing it. + # -T: treat destination as a file path, never as a directory target (prevents ln from creating the + # symlink inside /home/runner if it exists as a directory after rmdir fails). + [ -d /home/runner ] && ! [ -L /home/runner ] && rmdir /home/runner 2>/dev/null || true + /usr/bin/ln -sfnT /home/ubuntu /home/runner }} diff --git a/docs/changelog.md b/docs/changelog.md index a77c20cf..02573a62 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ +## [#223 Fix GARM image incompatibility](https://github.com/canonical/github-runner-image-builder-operator/pull/223) (2026-05-27) + +- Add `runner` user as an alias to the `ubuntu` user (same UID/GID, same home directory) so GARM can boot runners from images produced by this charm. + ## [#213 Fix proxy setup] - Fix proxy setup for image-relation joined hook. @@ -11,6 +15,7 @@ ## [#221 Add resource recommendation for charm deployment](https://github.com/canonical/github-runner-image-builder-operator/pull/221) + - Add 2 vCPUs, 8 GiB RAM, and 20 GiB disk OpenStack flavor recommendation.