From 31e6cc0cfd1cbd595675f21df01827e261605fae Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Fri, 4 Sep 2026 16:20:46 +0200 Subject: [PATCH 1/3] refactor(test-guest): isolate rootless Podman gateway lifecycle Signed-off-by: Evan Lezar --- .github/workflows/conformance.yml | 2 +- nix/test-guest/README.md | 10 +- .../configuration/podman-rootless.yml | 6 + .../configuration/tasks/podman-common.yml | 6 + nix/test-guest/default.nix | 2 +- .../defaults/main.yml | 1 + .../tasks/development-gateway.yml | 30 +++- .../roles/gateway-podman/tasks/lifecycle.yml | 135 ++++++++++++++++++ .../roles/gateway-podman/tasks/main.yml | 66 +++++++++ .../tasks/lifecycle.yml | 66 --------- .../gateway-rootless-podman/tasks/main.yml | 25 ---- .../openshell-development/defaults/main.yml | 1 - .../openshell-development/tasks/main.yml | 1 - nix/test-guest/run.sh | 14 +- 14 files changed, 258 insertions(+), 107 deletions(-) rename nix/test-guest/provisioners/roles/{gateway-rootless-podman => gateway-podman}/defaults/main.yml (77%) rename nix/test-guest/provisioners/roles/{gateway-rootless-podman => gateway-podman}/tasks/development-gateway.yml (72%) create mode 100644 nix/test-guest/provisioners/roles/gateway-podman/tasks/lifecycle.yml create mode 100644 nix/test-guest/provisioners/roles/gateway-podman/tasks/main.yml delete mode 100644 nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/lifecycle.yml delete mode 100644 nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/main.yml diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 775eb9f83e..757609a456 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -178,6 +178,6 @@ jobs: --copy conformance-input/openshell-conformance:/tmp/openshell-conformance \ --copy nix/test-guest/conformance-plans/gateway-upgrade-restart.toml:/tmp/conformance-plan.toml \ --provision openshell-rpm-latest-release \ - --provision gateway-rootless-podman \ + --provision gateway-podman \ --provision openshell-rpm-gateway-upgrade \ -- /tmp/openshell-conformance run --plan /tmp/conformance-plan.toml diff --git a/nix/test-guest/README.md b/nix/test-guest/README.md index 0006a331fb..4e359d9efe 100644 --- a/nix/test-guest/README.md +++ b/nix/test-guest/README.md @@ -50,7 +50,7 @@ nix/test-guest/ └── selinux.yml └── provisioners/ └── roles/ - ├── gateway-rootless-podman/ + ├── gateway-podman/ ├── openshell-development/ ├── openshell-rpm/ └── openshell-rpm-gateway-upgrade/ @@ -188,7 +188,7 @@ EOF - `/usr/local/bin/openshell-gateway` - `/usr/local/lib/openshell-sandbox.tar` -Compose it with `gateway-rootless-podman` to configure a rootless Podman +Compose it with `gateway-podman` to configure a rootless Podman gateway. For example, run conformance after the provisioners complete: ```shell @@ -199,7 +199,7 @@ nix run .#test-guest -- \ --copy ./openshell-gateway:/usr/local/bin/openshell-gateway \ --copy ./openshell-sandbox.tar:/usr/local/lib/openshell-sandbox.tar \ --provision openshell-development \ - --provision gateway-rootless-podman \ + --provision gateway-podman \ -- /usr/local/bin/openshell-conformance run --plan - <<'EOF' version = 1 @@ -217,12 +217,12 @@ EOF `openshell-rpm` expects OpenShell to have been installed with `--install`. It uses the RPM-owned `/usr/bin` binaries and `openshell-gateway` user service, without copied development artifacts or a supervisor archive. Compose it with -`gateway-rootless-podman` before an RPM action such as +`gateway-podman` before an RPM action such as `openshell-rpm-gateway-upgrade`. `openshell-rpm-latest-release` downloads and installs the latest stable OpenShell GitHub release for the guest architecture, then publishes the same -RPM installation contract. Compose it with `gateway-rootless-podman` and an +RPM installation contract. Compose it with `gateway-podman` and an RPM gateway action when testing an upgrade from the current release. Versioned plans under `nix/test-guest/conformance-plans/` bind conformance diff --git a/nix/test-guest/configuration/podman-rootless.yml b/nix/test-guest/configuration/podman-rootless.yml index 23fef05e5e..f9e0cf493b 100644 --- a/nix/test-guest/configuration/podman-rootless.yml +++ b/nix/test-guest/configuration/podman-rootless.yml @@ -34,3 +34,9 @@ - name: Configure shared rootless Podman settings ansible.builtin.import_tasks: tasks/podman-rootless/shared.yml + + - name: Record rootless Podman mode + ansible.builtin.copy: + dest: /etc/openshell-test-guest/podman-mode + content: "rootless\n" + mode: "0644" diff --git a/nix/test-guest/configuration/tasks/podman-common.yml b/nix/test-guest/configuration/tasks/podman-common.yml index 18a0f1304a..b5385daac8 100644 --- a/nix/test-guest/configuration/tasks/podman-common.yml +++ b/nix/test-guest/configuration/tasks/podman-common.yml @@ -11,3 +11,9 @@ ansible.builtin.package: name: podman state: present + +- name: Create test-guest state directory + ansible.builtin.file: + path: /etc/openshell-test-guest + state: directory + mode: "0755" diff --git a/nix/test-guest/default.nix b/nix/test-guest/default.nix index e3b19a5d83..e195f6da37 100644 --- a/nix/test-guest/default.nix +++ b/nix/test-guest/default.nix @@ -43,7 +43,7 @@ let "openshell-development" "openshell-rpm" "openshell-rpm-latest-release" - "gateway-rootless-podman" + "gateway-podman" "openshell-rpm-gateway-reinstall" "openshell-rpm-gateway-upgrade" ]; diff --git a/nix/test-guest/provisioners/roles/gateway-rootless-podman/defaults/main.yml b/nix/test-guest/provisioners/roles/gateway-podman/defaults/main.yml similarity index 77% rename from nix/test-guest/provisioners/roles/gateway-rootless-podman/defaults/main.yml rename to nix/test-guest/provisioners/roles/gateway-podman/defaults/main.yml index 3a50abfba1..ce0ec961dc 100644 --- a/nix/test-guest/provisioners/roles/gateway-rootless-podman/defaults/main.yml +++ b/nix/test-guest/provisioners/roles/gateway-podman/defaults/main.yml @@ -2,5 +2,6 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +openshell_gateway_service_command: /home/openshell/.local/bin/openshell-test-guest-as-gateway-user openshell_gateway_restart_command: /home/openshell/.local/bin/openshell-test-guest-gateway-restart openshell_gateway_diagnostics_command: /home/openshell/.local/bin/openshell-test-guest-diagnostics diff --git a/nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/development-gateway.yml b/nix/test-guest/provisioners/roles/gateway-podman/tasks/development-gateway.yml similarity index 72% rename from nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/development-gateway.yml rename to nix/test-guest/provisioners/roles/gateway-podman/tasks/development-gateway.yml index 4c89402dda..27d9547559 100644 --- a/nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/development-gateway.yml +++ b/nix/test-guest/provisioners/roles/gateway-podman/tasks/development-gateway.yml @@ -2,28 +2,40 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -- name: Create development gateway state directories +- name: Publish development gateway paths + ansible.builtin.set_fact: + openshell_gateway_state_root: "{{ openshell_gateway_service_home }}/.local/share/openshell-test-guest" + openshell_gateway_config_home: "{{ openshell_gateway_service_home }}/.config" + +- name: Create development gateway directories ansible.builtin.file: path: "{{ item }}" state: directory mode: "0700" + owner: "{{ openshell_gateway_service_user }}" + group: "{{ openshell_gateway_service_user }}" loop: - "{{ openshell_gateway_state_root }}" - "{{ openshell_gateway_state_root }}/xdg/config" - "{{ openshell_gateway_state_root }}/xdg/cache" - "{{ openshell_gateway_state_root }}/xdg/data" - "{{ openshell_gateway_state_root }}/xdg/state" - - /home/openshell/.config/systemd/user + - "{{ openshell_gateway_config_home }}/systemd/user" + become: true -- name: Generate development gateway certificates +- name: Generate development gateway credentials ansible.builtin.command: cmd: "{{ openshell_gateway_bin }} generate-certs --output-dir {{ openshell_gateway_state_root }}/pki" creates: "{{ openshell_gateway_state_root }}/pki/jwt/signing.pem" + become: true + become_user: "{{ openshell_gateway_service_user }}" -- name: Write rootless Podman gateway configuration +- name: Write development Podman gateway configuration ansible.builtin.copy: dest: "{{ openshell_gateway_state_root }}/gateway.toml" mode: "0600" + owner: "{{ openshell_gateway_service_user }}" + group: "{{ openshell_gateway_service_user }}" content: | [openshell] version = 1 @@ -50,6 +62,7 @@ image_pull_policy = "always" network_name = "openshell-test-guest" grpc_endpoint = "http://host.containers.internal:8080" + become: true - name: Check for the development supervisor image ansible.builtin.command: @@ -57,6 +70,8 @@ register: openshell_supervisor_image_exists changed_when: false failed_when: false + become: true + become_user: "{{ openshell_gateway_service_user }}" - name: Import the development supervisor image ansible.builtin.command: @@ -64,11 +79,15 @@ podman import --change 'ENTRYPOINT ["/openshell-sandbox"]' {{ openshell_supervisor_archive }} {{ openshell_supervisor_image }} when: openshell_supervisor_image_exists.rc != 0 + become: true + become_user: "{{ openshell_gateway_service_user }}" - name: Install the development gateway user service ansible.builtin.copy: - dest: "/home/openshell/.config/systemd/user/{{ openshell_gateway_service }}" + dest: "{{ openshell_gateway_config_home }}/systemd/user/{{ openshell_gateway_service }}" mode: "0600" + owner: "{{ openshell_gateway_service_user }}" + group: "{{ openshell_gateway_service_user }}" content: | [Unit] Description=OpenShell development test guest gateway @@ -84,3 +103,4 @@ [Install] WantedBy=default.target + become: true diff --git a/nix/test-guest/provisioners/roles/gateway-podman/tasks/lifecycle.yml b/nix/test-guest/provisioners/roles/gateway-podman/tasks/lifecycle.yml new file mode 100644 index 0000000000..ccd6615ff1 --- /dev/null +++ b/nix/test-guest/provisioners/roles/gateway-podman/tasks/lifecycle.yml @@ -0,0 +1,135 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- name: Publish gateway user-manager environment + ansible.builtin.set_fact: + openshell_gateway_user_environment: + HOME: "{{ openshell_gateway_service_home }}" + XDG_RUNTIME_DIR: "/run/user/{{ openshell_gateway_service_uid }}" + DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ openshell_gateway_service_uid }}/bus" + +- name: Reload gateway user services + ansible.builtin.systemd_service: + daemon_reload: true + scope: user + environment: "{{ openshell_gateway_user_environment }}" + become: true + become_user: "{{ openshell_gateway_service_user }}" + +- name: Start the gateway user service + ansible.builtin.systemd_service: + name: "{{ openshell_gateway_service }}" + enabled: true + state: started + scope: user + environment: "{{ openshell_gateway_user_environment }}" + become: true + become_user: "{{ openshell_gateway_service_user }}" + +- name: Register the provisioned gateway with its service account + ansible.builtin.command: + cmd: "{{ openshell_cli_bin }} gateway add {{ openshell_gateway_endpoint }} --local --name test-guest" + environment: "{{ openshell_gateway_user_environment }}" + become: true + become_user: "{{ openshell_gateway_service_user }}" + +- name: Wait for gateway CLI health + ansible.builtin.command: + cmd: "{{ openshell_cli_bin }} status" + environment: "{{ openshell_gateway_user_environment }}" + become: true + become_user: "{{ openshell_gateway_service_user }}" + register: gateway_health + changed_when: false + retries: 60 + delay: 1 + until: gateway_health.rc == 0 + ignore_errors: true + +- name: Collect failed gateway service status + ansible.builtin.command: + cmd: "systemctl --user status {{ openshell_gateway_service }} --no-pager" + environment: "{{ openshell_gateway_user_environment }}" + become: true + become_user: "{{ openshell_gateway_service_user }}" + register: gateway_service_status + changed_when: false + failed_when: false + when: gateway_health is failed + +- name: Collect failed gateway service journal + ansible.builtin.command: + cmd: "journalctl --user -u {{ openshell_gateway_service }} --no-pager -n 200" + environment: "{{ openshell_gateway_user_environment }}" + become: true + become_user: "{{ openshell_gateway_service_user }}" + register: gateway_service_journal + changed_when: false + failed_when: false + when: gateway_health is failed + +- name: Report failed gateway service diagnostics + ansible.builtin.debug: + msg: | + {{ gateway_service_status.stdout }} + {{ gateway_service_journal.stdout }} + when: gateway_health is failed + +- name: Require gateway CLI health + ansible.builtin.assert: + that: gateway_health is not failed + fail_msg: The provisioned gateway did not become healthy. + +- name: Create the target gateway-control command directory + ansible.builtin.file: + path: "{{ openshell_gateway_restart_command | dirname }}" + state: directory + mode: "0700" + +- name: Install the gateway service-account command wrapper + ansible.builtin.copy: + dest: "{{ openshell_gateway_service_command }}" + mode: "0700" + content: | + #!/usr/bin/env bash + set -Eeuo pipefail + exec sudo -H -u {{ openshell_gateway_service_user }} env \ + HOME={{ openshell_gateway_service_home }} \ + XDG_RUNTIME_DIR=/run/user/{{ openshell_gateway_service_uid }} \ + DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{{ openshell_gateway_service_uid }}/bus \ + "$@" + +- name: Install the target gateway restart command + ansible.builtin.copy: + dest: "{{ openshell_gateway_restart_command }}" + mode: "0700" + content: | + #!/usr/bin/env bash + set -Eeuo pipefail + gateway_user_command={{ openshell_gateway_service_command | quote }} + "${gateway_user_command}" systemctl --user restart {{ openshell_gateway_service }} + for _ in $(seq 1 60); do + if "${gateway_user_command}" {{ openshell_cli_bin }} status >/dev/null 2>&1; then + exit 0 + fi + sleep 1 + done + "${gateway_user_command}" systemctl --user status {{ openshell_gateway_service }} --no-pager >&2 || true + exit 1 + +- name: Install the target gateway diagnostics command + ansible.builtin.copy: + dest: "{{ openshell_gateway_diagnostics_command }}" + mode: "0700" + content: | + #!/usr/bin/env bash + set -u + gateway_user_command={{ openshell_gateway_service_command | quote }} + "${gateway_user_command}" systemctl --user status {{ openshell_gateway_service }} --no-pager || true + "${gateway_user_command}" journalctl --user -u {{ openshell_gateway_service }} --no-pager -n 200 || true + "${gateway_user_command}" podman info || true + "${gateway_user_command}" podman ps --all || true + if command -v getenforce >/dev/null 2>&1; then + getenforce || true + fi diff --git a/nix/test-guest/provisioners/roles/gateway-podman/tasks/main.yml b/nix/test-guest/provisioners/roles/gateway-podman/tasks/main.yml new file mode 100644 index 0000000000..9400a6d69c --- /dev/null +++ b/nix/test-guest/provisioners/roles/gateway-podman/tasks/main.yml @@ -0,0 +1,66 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- name: Read the configured Podman mode + ansible.builtin.command: + argv: + - cat + - /etc/openshell-test-guest/podman-mode + register: openshell_podman_mode_command + changed_when: false + +- name: Publish the configured Podman mode + ansible.builtin.set_fact: + openshell_podman_mode: "{{ openshell_podman_mode_command.stdout | trim }}" + +- name: Require an OpenShell installation + ansible.builtin.assert: + that: + - openshell_install_source is defined + - openshell_cli_bin is defined + - openshell_gateway_bin is defined + - openshell_gateway_service is defined + - openshell_gateway_endpoint is defined + - openshell_install_source in ['development', 'rpm'] + - openshell_podman_mode == 'rootless' + fail_msg: >- + gateway-podman requires an earlier OpenShell installation role and a + podman-rootless test-guest configuration. + +- name: Resolve the gateway service account + ansible.builtin.getent: + database: passwd + key: openshell + +- name: Publish the rootless gateway service account + ansible.builtin.set_fact: + openshell_gateway_service_user: openshell + openshell_gateway_service_uid: "{{ ansible_facts.getent_passwd.openshell[1] }}" + openshell_gateway_service_home: "{{ ansible_facts.getent_passwd.openshell[4] }}" + +- name: Configure a development gateway + ansible.builtin.include_tasks: development-gateway.yml + when: openshell_install_source == 'development' + +- name: Enable the gateway service account user manager + ansible.builtin.command: + argv: + - loginctl + - enable-linger + - "{{ openshell_gateway_service_user }}" + become: true + changed_when: false + +- name: Start the gateway service account user manager + ansible.builtin.systemd_service: + name: "user@{{ openshell_gateway_service_uid }}.service" + state: started + become: true + +- name: Run the gateway lifecycle + ansible.builtin.include_tasks: lifecycle.yml + +- name: Publish Podman gateway runtime + ansible.builtin.set_fact: + openshell_gateway_runtime: "{{ openshell_podman_mode }}-podman" diff --git a/nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/lifecycle.yml b/nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/lifecycle.yml deleted file mode 100644 index b90e66f26b..0000000000 --- a/nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/lifecycle.yml +++ /dev/null @@ -1,66 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -- name: Reload gateway user services - ansible.builtin.systemd_service: - daemon_reload: true - scope: user - -- name: Start the gateway user service - ansible.builtin.systemd_service: - name: "{{ openshell_gateway_service }}" - enabled: true - state: started - scope: user - -- name: Register the provisioned gateway with the CLI - ansible.builtin.command: - cmd: "{{ openshell_cli_bin }} gateway add {{ openshell_gateway_endpoint }} --local --name test-guest" - -- name: Wait for gateway CLI health - ansible.builtin.command: - cmd: "{{ openshell_cli_bin }} status" - register: gateway_health - changed_when: false - retries: 60 - delay: 1 - until: gateway_health.rc == 0 - -- name: Create the target gateway-control command directory - ansible.builtin.file: - path: "{{ openshell_gateway_restart_command | dirname }}" - state: directory - mode: "0700" - -- name: Install the target gateway restart command - ansible.builtin.copy: - dest: "{{ openshell_gateway_restart_command }}" - mode: "0700" - content: | - #!/usr/bin/env bash - set -Eeuo pipefail - systemctl --user restart {{ openshell_gateway_service }} - for _ in $(seq 1 60); do - if {{ openshell_cli_bin }} status >/dev/null 2>&1; then - exit 0 - fi - sleep 1 - done - systemctl --user status {{ openshell_gateway_service }} --no-pager >&2 || true - exit 1 - -- name: Install the target gateway diagnostics command - ansible.builtin.copy: - dest: "{{ openshell_gateway_diagnostics_command }}" - mode: "0700" - content: | - #!/usr/bin/env bash - set -u - systemctl --user status {{ openshell_gateway_service }} --no-pager || true - journalctl --user -u {{ openshell_gateway_service }} --no-pager -n 200 || true - podman info || true - podman ps --all || true - if command -v getenforce >/dev/null 2>&1; then - getenforce || true - fi diff --git a/nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/main.yml b/nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/main.yml deleted file mode 100644 index 5a23e28406..0000000000 --- a/nix/test-guest/provisioners/roles/gateway-rootless-podman/tasks/main.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -- name: Require an OpenShell installation - ansible.builtin.assert: - that: - - openshell_install_source is defined - - openshell_cli_bin is defined - - openshell_gateway_bin is defined - - openshell_gateway_service is defined - - openshell_gateway_endpoint is defined - fail_msg: >- - gateway-rootless-podman requires an earlier OpenShell source provisioner, - such as openshell-development or openshell-rpm. - -- name: Configure development gateway for rootless Podman - ansible.builtin.include_tasks: development-gateway.yml - when: openshell_install_source == 'development' - -- ansible.builtin.import_tasks: lifecycle.yml - -- name: Publish rootless Podman gateway runtime - ansible.builtin.set_fact: - openshell_gateway_runtime: rootless-podman diff --git a/nix/test-guest/provisioners/roles/openshell-development/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-development/defaults/main.yml index 8c2eb312cf..2aae28bbe8 100644 --- a/nix/test-guest/provisioners/roles/openshell-development/defaults/main.yml +++ b/nix/test-guest/provisioners/roles/openshell-development/defaults/main.yml @@ -7,5 +7,4 @@ openshell_development_gateway_bin: /usr/local/bin/openshell-gateway openshell_development_sandbox_archive: /usr/local/lib/openshell-sandbox.tar openshell_development_gateway_service: openshell-test-guest-gateway.service openshell_development_gateway_endpoint: http://127.0.0.1:8080 -openshell_development_state_root: /home/openshell/.local/share/openshell-test-guest openshell_development_supervisor_image: localhost/openshell/supervisor:test-guest diff --git a/nix/test-guest/provisioners/roles/openshell-development/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-development/tasks/main.yml index 902646116a..dee9747279 100644 --- a/nix/test-guest/provisioners/roles/openshell-development/tasks/main.yml +++ b/nix/test-guest/provisioners/roles/openshell-development/tasks/main.yml @@ -26,7 +26,6 @@ openshell_gateway_bin: "{{ openshell_development_gateway_bin }}" openshell_gateway_service: "{{ openshell_development_gateway_service }}" openshell_gateway_endpoint: "{{ openshell_development_gateway_endpoint }}" - openshell_gateway_state_root: "{{ openshell_development_state_root }}" openshell_supervisor_source: archive openshell_supervisor_archive: "{{ openshell_development_sandbox_archive }}" openshell_supervisor_image: "{{ openshell_development_supervisor_image }}" diff --git a/nix/test-guest/run.sh b/nix/test-guest/run.sh index 045f2f441f..4c38f3a598 100644 --- a/nix/test-guest/run.sh +++ b/nix/test-guest/run.sh @@ -176,12 +176,18 @@ fi # shellcheck disable=SC1090 . "${OPENSHELL_TEST_GUEST_DISTROS}/${distro}" - for item in "${configurations[@]}"; do - if [[ ! ${item} =~ ^[a-z0-9][a-z0-9-]*$ ]] || +podman_mode= +for item in "${configurations[@]}"; do + if [[ ! ${item} =~ ^[a-z0-9][a-z0-9-]*$ ]] || [ ! -f "${OPENSHELL_TEST_GUEST_CONFIGURATIONS}/${item}" ]; then echo "unknown configuration: ${item:-}" >&2 exit 2 fi + case "${item}" in + podman-rootless) + podman_mode=rootless + ;; + esac done for item in "${provisions[@]}"; do if [[ ! ${item} =~ ^[a-z0-9][a-z0-9-]*$ ]] || @@ -189,6 +195,10 @@ for item in "${provisions[@]}"; do echo "unknown provisioner: ${item:-}" >&2 exit 2 fi + if [ "${item}" = gateway-podman ] && [ "${podman_mode}" != rootless ]; then + echo "gateway-podman requires --with podman-rootless" >&2 + exit 2 + fi done if [ -n "${requested_ssh_port}" ] && { From 4cb52e00497a7911032a2810fc93917bb9f94586 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Fri, 4 Sep 2026 17:05:33 +0200 Subject: [PATCH 2/3] refactor(test-guest): model OpenShell artifact sources Signed-off-by: Evan Lezar --- .github/workflows/conformance.yml | 8 +- nix/test-guest/README.md | 89 ++++++++++++------- .../gateway-upgrade-restart.toml | 4 +- nix/test-guest/default.nix | 11 +-- .../roles/gateway-podman/tasks/main.yml | 12 +-- .../defaults/main.yml | 11 +++ .../tasks/main.yml | 32 +++++++ .../defaults/main.yml | 22 +++++ .../tasks/main.yml | 52 +++++++++++ .../defaults/main.yml | 7 ++ .../tasks/main.yml | 11 +++ .../openshell-development/defaults/main.yml | 10 --- .../openshell-development/tasks/main.yml | 31 ------- .../defaults/main.yml | 0 .../tasks/main.yml | 19 ++-- .../defaults/main.yml | 0 .../tasks/main.yml | 0 .../defaults/main.yml | 7 -- .../tasks/main.yml | 70 --------------- .../defaults/main.yml | 7 -- .../tasks/main.yml | 59 ------------ .../openshell-rpm-source/defaults/main.yml | 15 ++++ .../roles/openshell-rpm-source/tasks/main.yml | 64 +++++++++++++ 23 files changed, 300 insertions(+), 241 deletions(-) create mode 100644 nix/test-guest/provisioners/roles/openshell-binaries-contract/defaults/main.yml create mode 100644 nix/test-guest/provisioners/roles/openshell-binaries-contract/tasks/main.yml create mode 100644 nix/test-guest/provisioners/roles/openshell-candidate-binaries-source/defaults/main.yml create mode 100644 nix/test-guest/provisioners/roles/openshell-candidate-binaries-source/tasks/main.yml create mode 100644 nix/test-guest/provisioners/roles/openshell-candidate-rpm-source/defaults/main.yml create mode 100644 nix/test-guest/provisioners/roles/openshell-candidate-rpm-source/tasks/main.yml delete mode 100644 nix/test-guest/provisioners/roles/openshell-development/defaults/main.yml delete mode 100644 nix/test-guest/provisioners/roles/openshell-development/tasks/main.yml rename nix/test-guest/provisioners/roles/{openshell-rpm-latest-release => openshell-latest-release-rpm-source}/defaults/main.yml (100%) rename nix/test-guest/provisioners/roles/{openshell-rpm-latest-release => openshell-latest-release-rpm-source}/tasks/main.yml (85%) rename nix/test-guest/provisioners/roles/{openshell-rpm => openshell-rpm-contract}/defaults/main.yml (100%) rename nix/test-guest/provisioners/roles/{openshell-rpm => openshell-rpm-contract}/tasks/main.yml (100%) delete mode 100644 nix/test-guest/provisioners/roles/openshell-rpm-gateway-reinstall/defaults/main.yml delete mode 100644 nix/test-guest/provisioners/roles/openshell-rpm-gateway-reinstall/tasks/main.yml delete mode 100644 nix/test-guest/provisioners/roles/openshell-rpm-gateway-upgrade/defaults/main.yml delete mode 100644 nix/test-guest/provisioners/roles/openshell-rpm-gateway-upgrade/tasks/main.yml create mode 100644 nix/test-guest/provisioners/roles/openshell-rpm-source/defaults/main.yml create mode 100644 nix/test-guest/provisioners/roles/openshell-rpm-source/tasks/main.yml diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 757609a456..f8dca3ac54 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -173,11 +173,11 @@ jobs: --distro fedora \ --with podman-rootless \ --with selinux \ - --copy "${candidate_cli_package[0]}:/var/lib/openshell-conformance/candidate/openshell.rpm" \ - --copy "${candidate_gateway_package[0]}:/var/lib/openshell-conformance/candidate/openshell-gateway.rpm" \ + --copy "${candidate_cli_package[0]}:/var/lib/openshell-test-guest/artifacts/openshell.rpm" \ + --copy "${candidate_gateway_package[0]}:/var/lib/openshell-test-guest/artifacts/openshell-gateway.rpm" \ --copy conformance-input/openshell-conformance:/tmp/openshell-conformance \ --copy nix/test-guest/conformance-plans/gateway-upgrade-restart.toml:/tmp/conformance-plan.toml \ - --provision openshell-rpm-latest-release \ + --provision openshell-latest-release-rpm-source \ + --provision openshell-candidate-rpm-source \ --provision gateway-podman \ - --provision openshell-rpm-gateway-upgrade \ -- /tmp/openshell-conformance run --plan /tmp/conformance-plan.toml diff --git a/nix/test-guest/README.md b/nix/test-guest/README.md index 4e359d9efe..dc2bbde274 100644 --- a/nix/test-guest/README.md +++ b/nix/test-guest/README.md @@ -51,9 +51,12 @@ nix/test-guest/ └── provisioners/ └── roles/ ├── gateway-podman/ - ├── openshell-development/ - ├── openshell-rpm/ - └── openshell-rpm-gateway-upgrade/ + ├── openshell-candidate-binaries-source/ + ├── openshell-binaries-contract/ + ├── openshell-candidate-rpm-source/ + ├── openshell-rpm-contract/ + ├── openshell-latest-release-rpm-source/ + └── openshell-rpm-source/ ``` - `default.nix` assembles the guest and cache flake apps. It selects host architecture and acceleration, supplies the runtime tools, and exposes distro profiles and configuration playbooks as Nix-store catalogs. @@ -144,18 +147,25 @@ nix run .#test-guest -- \ Configurations are Ansible playbooks stored under `nix/test-guest/configuration/`. Ansible runs on the host using the VM's ephemeral SSH key and loopback port. The guest does not install Ansible. -Configurations run in the order provided on the command line. OpenShell packages and copied files are installed after all configurations succeed. - -`--install` packages and `--copy` files are applied by a dedicated per-run -Ansible playbook. `--copy` preserves each source file's ordinary permission -bits. They are not stored in prepared VM cache entries. +Configurations run in the order provided on the command line. Package and file +artifacts are applied after all configurations succeed. `--install` installs a +generic Debian or RPM package directly; `--copy` stages a file at a guest path +and preserves its ordinary permission bits. Neither is stored in prepared VM +cache entries. ## System provisioners -`--provision NAME` applies a target-specific system setup after packages and +`--provision NAME` applies target-specific system setup after packages and copied artifacts are present. Unlike `--with`, provisioners are not cached. -They can therefore install and start an OpenShell system without coupling the -prepared guest image to a particular build or driver configuration. +Stage OpenShell artifacts with `--copy`, then use an ordered source installer +provisioner to make OpenShell available without coupling the prepared guest +image to a particular build or driver configuration. + +OpenShell source provisioners run in command-line order. The first source +installs and publishes the initial OpenShell state; later sources only make +their packages and target-side apply commands available. This lets a scenario +prepare guest state before initial installation and gives lifecycle actions the +exact source artifacts they must install later. Provisioners that support gateway continuity install a target-control command: @@ -182,23 +192,26 @@ timeout_secs = 120 EOF ``` -`openshell-development` expects these copied guest paths: +`openshell-candidate-binaries-source` makes staged raw candidate artifacts available. +When it is the first OpenShell source provisioner, it installs them into the +candidate binary OpenShell state. Stage these guest paths with `--copy`: -- `/usr/local/bin/openshell` -- `/usr/local/bin/openshell-gateway` -- `/usr/local/lib/openshell-sandbox.tar` +- `/var/lib/openshell-test-guest/artifacts/openshell` +- `/var/lib/openshell-test-guest/artifacts/openshell-gateway` +- `/var/lib/openshell-test-guest/artifacts/openshell-sandbox.tar` -Compose it with `gateway-podman` to configure a rootless Podman -gateway. For example, run conformance after the provisioners complete: +Compose the binary candidate source with `gateway-podman` to configure +a rootless Podman gateway. For example, run conformance after the provisioners +complete: ```shell nix run .#test-guest -- \ --distro fedora --with podman-rootless --with selinux \ - --copy ./openshell:/usr/local/bin/openshell \ + --copy ./openshell:/var/lib/openshell-test-guest/artifacts/openshell \ --copy ./openshell-conformance:/usr/local/bin/openshell-conformance \ - --copy ./openshell-gateway:/usr/local/bin/openshell-gateway \ - --copy ./openshell-sandbox.tar:/usr/local/lib/openshell-sandbox.tar \ - --provision openshell-development \ + --copy ./openshell-gateway:/var/lib/openshell-test-guest/artifacts/openshell-gateway \ + --copy ./openshell-sandbox.tar:/var/lib/openshell-test-guest/artifacts/openshell-sandbox.tar \ + --provision openshell-candidate-binaries-source \ --provision gateway-podman \ -- /usr/local/bin/openshell-conformance run --plan - <<'EOF' version = 1 @@ -214,16 +227,32 @@ timeout_secs = 120 EOF ``` -`openshell-rpm` expects OpenShell to have been installed with `--install`. It -uses the RPM-owned `/usr/bin` binaries and `openshell-gateway` user service, -without copied development artifacts or a supervisor archive. Compose it with -`gateway-podman` before an RPM action such as -`openshell-rpm-gateway-upgrade`. +`openshell-candidate-rpm-source` makes staged candidate RPMs available and publishes a +target-side candidate apply command. Stage the CLI and gateway packages as +`/var/lib/openshell-test-guest/artifacts/openshell.rpm` and +`/var/lib/openshell-test-guest/artifacts/openshell-gateway.rpm`. + +`openshell-latest-release-rpm-source` downloads the latest stable OpenShell GitHub +release for the guest architecture, stores its versioned RPMs under +`/var/lib/openshell-conformance/baseline`, and publishes a target-side +latest-release apply command. + +For an upgrade test, order the latest-release source first so it initializes the +guest, then make the candidate source available for the lifecycle action: + +```shell +--provision openshell-latest-release-rpm-source \ +--provision openshell-candidate-rpm-source \ +--provision gateway-podman +``` + +The upgrade plan applies the candidate source and then uses the independent +gateway-restart action to verify continuity across a subsequent gateway restart. -`openshell-rpm-latest-release` downloads and installs the latest stable -OpenShell GitHub release for the guest architecture, then publishes the same -RPM installation contract. Compose it with `gateway-podman` and an -RPM gateway action when testing an upgrade from the current release. +`openshell-binaries-contract`, `openshell-rpm-contract`, and +`openshell-rpm-source` are internal composition roles used by the public source +provisioners. They are listed for the runner's role resolution but are not +normal `--provision` entry points. Versioned plans under `nix/test-guest/conformance-plans/` bind conformance scenarios to the stable action-command contracts installed by provisioners. diff --git a/nix/test-guest/conformance-plans/gateway-upgrade-restart.toml b/nix/test-guest/conformance-plans/gateway-upgrade-restart.toml index fde903b778..4a654d1daa 100644 --- a/nix/test-guest/conformance-plans/gateway-upgrade-restart.toml +++ b/nix/test-guest/conformance-plans/gateway-upgrade-restart.toml @@ -15,8 +15,8 @@ scenario = "sandbox-continuity" workload_expectation = "reconciled" [[runs.actions]] -name = "gateway-upgrade" -command = "/home/openshell/.local/bin/openshell-test-guest-gateway-upgrade" +name = "rpm-apply-candidate" +command = "/home/openshell/.local/bin/openshell-test-guest-rpm-apply-candidate" timeout_secs = 120 [[runs.actions]] diff --git a/nix/test-guest/default.nix b/nix/test-guest/default.nix index e195f6da37..483ab7b0c2 100644 --- a/nix/test-guest/default.nix +++ b/nix/test-guest/default.nix @@ -40,12 +40,13 @@ let ]; provisionerRoles = [ - "openshell-development" - "openshell-rpm" - "openshell-rpm-latest-release" + "openshell-candidate-binaries-source" + "openshell-binaries-contract" + "openshell-candidate-rpm-source" + "openshell-rpm-contract" + "openshell-latest-release-rpm-source" + "openshell-rpm-source" "gateway-podman" - "openshell-rpm-gateway-reinstall" - "openshell-rpm-gateway-upgrade" ]; mkDistroProfile = diff --git a/nix/test-guest/provisioners/roles/gateway-podman/tasks/main.yml b/nix/test-guest/provisioners/roles/gateway-podman/tasks/main.yml index 9400a6d69c..2e2b563d58 100644 --- a/nix/test-guest/provisioners/roles/gateway-podman/tasks/main.yml +++ b/nix/test-guest/provisioners/roles/gateway-podman/tasks/main.yml @@ -22,11 +22,13 @@ - openshell_gateway_bin is defined - openshell_gateway_service is defined - openshell_gateway_endpoint is defined - - openshell_install_source in ['development', 'rpm'] + - openshell_install_source in ['binaries', 'rpm'] - openshell_podman_mode == 'rootless' fail_msg: >- - gateway-podman requires an earlier OpenShell installation role and a - podman-rootless test-guest configuration. + gateway-podman requires an earlier OpenShell source provisioner, such as + openshell-candidate-binaries-source, openshell-candidate-rpm-source, or + openshell-latest-release-rpm-source, and a podman-rootless test-guest + configuration. - name: Resolve the gateway service account ansible.builtin.getent: @@ -39,9 +41,9 @@ openshell_gateway_service_uid: "{{ ansible_facts.getent_passwd.openshell[1] }}" openshell_gateway_service_home: "{{ ansible_facts.getent_passwd.openshell[4] }}" -- name: Configure a development gateway +- name: Configure a candidate binary gateway ansible.builtin.include_tasks: development-gateway.yml - when: openshell_install_source == 'development' + when: openshell_install_source == 'binaries' - name: Enable the gateway service account user manager ansible.builtin.command: diff --git a/nix/test-guest/provisioners/roles/openshell-binaries-contract/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-binaries-contract/defaults/main.yml new file mode 100644 index 0000000000..6dd2f200c7 --- /dev/null +++ b/nix/test-guest/provisioners/roles/openshell-binaries-contract/defaults/main.yml @@ -0,0 +1,11 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +openshell_binaries_contract_cli_bin: /usr/local/bin/openshell +openshell_binaries_contract_gateway_bin: /usr/local/bin/openshell-gateway +openshell_binaries_contract_sandbox_archive: /usr/local/lib/openshell-sandbox.tar +openshell_binaries_contract_gateway_service: openshell-test-guest-gateway.service +openshell_binaries_contract_gateway_endpoint: http://127.0.0.1:8080 +openshell_binaries_contract_state_root: /home/openshell/.local/share/openshell-test-guest +openshell_binaries_contract_supervisor_image: localhost/openshell/supervisor:test-guest diff --git a/nix/test-guest/provisioners/roles/openshell-binaries-contract/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-binaries-contract/tasks/main.yml new file mode 100644 index 0000000000..698e548932 --- /dev/null +++ b/nix/test-guest/provisioners/roles/openshell-binaries-contract/tasks/main.yml @@ -0,0 +1,32 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- name: Verify installed candidate binaries + ansible.builtin.stat: + path: "{{ item }}" + loop: + - "{{ openshell_binaries_contract_cli_bin }}" + - "{{ openshell_binaries_contract_gateway_bin }}" + - "{{ openshell_binaries_contract_sandbox_archive }}" + register: openshell_binaries_contract_artifacts + +- name: Require installed candidate binaries + ansible.builtin.assert: + that: item.stat.exists + fail_msg: "missing required candidate binary: {{ item.item }}" + loop: "{{ openshell_binaries_contract_artifacts.results }}" + loop_control: + label: "{{ item.item }}" + +- name: Publish candidate binary OpenShell installation + ansible.builtin.set_fact: + openshell_install_source: binaries + openshell_cli_bin: "{{ openshell_binaries_contract_cli_bin }}" + openshell_gateway_bin: "{{ openshell_binaries_contract_gateway_bin }}" + openshell_gateway_service: "{{ openshell_binaries_contract_gateway_service }}" + openshell_gateway_endpoint: "{{ openshell_binaries_contract_gateway_endpoint }}" + openshell_gateway_state_root: "{{ openshell_binaries_contract_state_root }}" + openshell_supervisor_source: archive + openshell_supervisor_archive: "{{ openshell_binaries_contract_sandbox_archive }}" + openshell_supervisor_image: "{{ openshell_binaries_contract_supervisor_image }}" diff --git a/nix/test-guest/provisioners/roles/openshell-candidate-binaries-source/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-candidate-binaries-source/defaults/main.yml new file mode 100644 index 0000000000..da9fe4d175 --- /dev/null +++ b/nix/test-guest/provisioners/roles/openshell-candidate-binaries-source/defaults/main.yml @@ -0,0 +1,22 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +openshell_binaries_candidate_staging_dir: /var/lib/openshell-test-guest/artifacts +openshell_binaries_candidate_cli_artifact: "{{ openshell_binaries_candidate_staging_dir }}/openshell" +openshell_binaries_candidate_gateway_artifact: "{{ openshell_binaries_candidate_staging_dir }}/openshell-gateway" +openshell_binaries_candidate_sandbox_artifact: "{{ openshell_binaries_candidate_staging_dir }}/openshell-sandbox.tar" +openshell_binaries_candidate_cli_bin: /usr/local/bin/openshell +openshell_binaries_candidate_gateway_bin: /usr/local/bin/openshell-gateway +openshell_binaries_candidate_sandbox_archive: /usr/local/lib/openshell-sandbox.tar +openshell_binaries_candidate_apply_command: /home/openshell/.local/bin/openshell-test-guest-binaries-apply-candidate +openshell_binaries_candidate_artifacts: + - source: "{{ openshell_binaries_candidate_cli_artifact }}" + destination: "{{ openshell_binaries_candidate_cli_bin }}" + mode: "0755" + - source: "{{ openshell_binaries_candidate_gateway_artifact }}" + destination: "{{ openshell_binaries_candidate_gateway_bin }}" + mode: "0755" + - source: "{{ openshell_binaries_candidate_sandbox_artifact }}" + destination: "{{ openshell_binaries_candidate_sandbox_archive }}" + mode: "0644" diff --git a/nix/test-guest/provisioners/roles/openshell-candidate-binaries-source/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-candidate-binaries-source/tasks/main.yml new file mode 100644 index 0000000000..9a591d9304 --- /dev/null +++ b/nix/test-guest/provisioners/roles/openshell-candidate-binaries-source/tasks/main.yml @@ -0,0 +1,52 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- name: Verify candidate OpenShell binaries + ansible.builtin.stat: + path: "{{ item.source }}" + loop: "{{ openshell_binaries_candidate_artifacts }}" + register: openshell_binaries_candidate_artifact_stats + +- name: Require candidate OpenShell binaries + ansible.builtin.assert: + that: item.stat.exists + fail_msg: "missing candidate OpenShell binary: {{ item.item.source }}" + loop: "{{ openshell_binaries_candidate_artifact_stats.results }}" + loop_control: + label: "{{ item.item.source }}" + +- name: Create the candidate binary apply command directory + ansible.builtin.file: + path: "{{ openshell_binaries_candidate_apply_command | dirname }}" + state: directory + mode: "0700" + +- name: Install the candidate binary apply command + ansible.builtin.copy: + dest: "{{ openshell_binaries_candidate_apply_command }}" + mode: "0700" + content: | + #!/usr/bin/env bash + set -Eeuo pipefail + {% for artifact in openshell_binaries_candidate_artifacts %} + sudo install -D -m {{ artifact.mode }} -- {{ artifact.source | quote }} {{ artifact.destination | quote }} + {% endfor %} + +- name: Determine whether the candidate binaries are the initial OpenShell source + ansible.builtin.set_fact: + openshell_binaries_candidate_is_initial: "{{ openshell_install_source is not defined }}" + +- name: Install candidate binaries as the initial OpenShell state + ansible.builtin.command: + cmd: "{{ openshell_binaries_candidate_apply_command }}" + when: openshell_binaries_candidate_is_initial + +- name: Publish the initial candidate binary OpenShell contract + ansible.builtin.include_role: + name: openshell-binaries-contract + vars: + openshell_binaries_contract_cli_bin: "{{ openshell_binaries_candidate_cli_bin }}" + openshell_binaries_contract_gateway_bin: "{{ openshell_binaries_candidate_gateway_bin }}" + openshell_binaries_contract_sandbox_archive: "{{ openshell_binaries_candidate_sandbox_archive }}" + when: openshell_binaries_candidate_is_initial diff --git a/nix/test-guest/provisioners/roles/openshell-candidate-rpm-source/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-candidate-rpm-source/defaults/main.yml new file mode 100644 index 0000000000..4bcbb0d15e --- /dev/null +++ b/nix/test-guest/provisioners/roles/openshell-candidate-rpm-source/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +openshell_rpm_candidate_staging_dir: /var/lib/openshell-test-guest/artifacts +openshell_rpm_candidate_cli_package: "{{ openshell_rpm_candidate_staging_dir }}/openshell.rpm" +openshell_rpm_candidate_gateway_package: "{{ openshell_rpm_candidate_staging_dir }}/openshell-gateway.rpm" diff --git a/nix/test-guest/provisioners/roles/openshell-candidate-rpm-source/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-candidate-rpm-source/tasks/main.yml new file mode 100644 index 0000000000..b2408e1782 --- /dev/null +++ b/nix/test-guest/provisioners/roles/openshell-candidate-rpm-source/tasks/main.yml @@ -0,0 +1,11 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- name: Prepare the candidate RPM source + ansible.builtin.include_role: + name: openshell-rpm-source + vars: + openshell_rpm_source_name: candidate + openshell_rpm_source_cli_package: "{{ openshell_rpm_candidate_cli_package }}" + openshell_rpm_source_gateway_package: "{{ openshell_rpm_candidate_gateway_package }}" diff --git a/nix/test-guest/provisioners/roles/openshell-development/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-development/defaults/main.yml deleted file mode 100644 index 2aae28bbe8..0000000000 --- a/nix/test-guest/provisioners/roles/openshell-development/defaults/main.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -openshell_development_cli_bin: /usr/local/bin/openshell -openshell_development_gateway_bin: /usr/local/bin/openshell-gateway -openshell_development_sandbox_archive: /usr/local/lib/openshell-sandbox.tar -openshell_development_gateway_service: openshell-test-guest-gateway.service -openshell_development_gateway_endpoint: http://127.0.0.1:8080 -openshell_development_supervisor_image: localhost/openshell/supervisor:test-guest diff --git a/nix/test-guest/provisioners/roles/openshell-development/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-development/tasks/main.yml deleted file mode 100644 index dee9747279..0000000000 --- a/nix/test-guest/provisioners/roles/openshell-development/tasks/main.yml +++ /dev/null @@ -1,31 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -- name: Verify copied development artifacts - ansible.builtin.stat: - path: "{{ item }}" - loop: - - "{{ openshell_development_cli_bin }}" - - "{{ openshell_development_gateway_bin }}" - - "{{ openshell_development_sandbox_archive }}" - register: openshell_development_artifacts - -- name: Require copied development artifacts - ansible.builtin.assert: - that: item.stat.exists - fail_msg: "missing required development artifact: {{ item.item }}" - loop: "{{ openshell_development_artifacts.results }}" - loop_control: - label: "{{ item.item }}" - -- name: Publish development OpenShell installation - ansible.builtin.set_fact: - openshell_install_source: development - openshell_cli_bin: "{{ openshell_development_cli_bin }}" - openshell_gateway_bin: "{{ openshell_development_gateway_bin }}" - openshell_gateway_service: "{{ openshell_development_gateway_service }}" - openshell_gateway_endpoint: "{{ openshell_development_gateway_endpoint }}" - openshell_supervisor_source: archive - openshell_supervisor_archive: "{{ openshell_development_sandbox_archive }}" - openshell_supervisor_image: "{{ openshell_development_supervisor_image }}" diff --git a/nix/test-guest/provisioners/roles/openshell-rpm-latest-release/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-latest-release-rpm-source/defaults/main.yml similarity index 100% rename from nix/test-guest/provisioners/roles/openshell-rpm-latest-release/defaults/main.yml rename to nix/test-guest/provisioners/roles/openshell-latest-release-rpm-source/defaults/main.yml diff --git a/nix/test-guest/provisioners/roles/openshell-rpm-latest-release/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-latest-release-rpm-source/tasks/main.yml similarity index 85% rename from nix/test-guest/provisioners/roles/openshell-rpm-latest-release/tasks/main.yml rename to nix/test-guest/provisioners/roles/openshell-latest-release-rpm-source/tasks/main.yml index f6063bde0c..fcf0c10852 100644 --- a/nix/test-guest/provisioners/roles/openshell-rpm-latest-release/tasks/main.yml +++ b/nix/test-guest/provisioners/roles/openshell-latest-release-rpm-source/tasks/main.yml @@ -65,15 +65,12 @@ label: "{{ item.name }}" become: true -- name: Install latest-release OpenShell RPMs - ansible.builtin.dnf: - name: - - "{{ openshell_rpm_latest_release_download_dir }}/{{ openshell_rpm_latest_release_cli_asset.name }}" - - "{{ openshell_rpm_latest_release_download_dir }}/{{ openshell_rpm_latest_release_gateway_asset.name }}" - state: present - disable_gpg_check: true - become: true - -- name: Publish latest-release RPM OpenShell installation +- name: Prepare the latest-release RPM source ansible.builtin.include_role: - name: openshell-rpm + name: openshell-rpm-source + vars: + openshell_rpm_source_name: latest-release + openshell_rpm_source_cli_package: >- + {{ openshell_rpm_latest_release_download_dir }}/{{ openshell_rpm_latest_release_cli_asset.name }} + openshell_rpm_source_gateway_package: >- + {{ openshell_rpm_latest_release_download_dir }}/{{ openshell_rpm_latest_release_gateway_asset.name }} diff --git a/nix/test-guest/provisioners/roles/openshell-rpm/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-rpm-contract/defaults/main.yml similarity index 100% rename from nix/test-guest/provisioners/roles/openshell-rpm/defaults/main.yml rename to nix/test-guest/provisioners/roles/openshell-rpm-contract/defaults/main.yml diff --git a/nix/test-guest/provisioners/roles/openshell-rpm/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-rpm-contract/tasks/main.yml similarity index 100% rename from nix/test-guest/provisioners/roles/openshell-rpm/tasks/main.yml rename to nix/test-guest/provisioners/roles/openshell-rpm-contract/tasks/main.yml diff --git a/nix/test-guest/provisioners/roles/openshell-rpm-gateway-reinstall/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-rpm-gateway-reinstall/defaults/main.yml deleted file mode 100644 index 95c448d300..0000000000 --- a/nix/test-guest/provisioners/roles/openshell-rpm-gateway-reinstall/defaults/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -openshell_rpm_reinstall_cli: /var/lib/openshell-conformance/candidate/openshell.rpm -openshell_rpm_reinstall_gateway: /var/lib/openshell-conformance/candidate/openshell-gateway.rpm -openshell_rpm_reinstall_command: /home/openshell/.local/bin/openshell-test-guest-gateway-reinstall diff --git a/nix/test-guest/provisioners/roles/openshell-rpm-gateway-reinstall/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-rpm-gateway-reinstall/tasks/main.yml deleted file mode 100644 index 36f1c6dce2..0000000000 --- a/nix/test-guest/provisioners/roles/openshell-rpm-gateway-reinstall/tasks/main.yml +++ /dev/null @@ -1,70 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -- name: Require RPM gateway provisioner dependencies - ansible.builtin.assert: - that: - - openshell_install_source | default('') == 'rpm' - - openshell_cli_bin is defined - - openshell_gateway_service is defined - - openshell_gateway_runtime | default('') == 'rootless-podman' - fail_msg: >- - openshell-rpm-gateway-reinstall requires openshell-rpm and a configured - gateway runtime. - -- name: Verify candidate RPMs for reinstall - ansible.builtin.stat: - path: "{{ item }}" - loop: - - "{{ openshell_rpm_reinstall_cli }}" - - "{{ openshell_rpm_reinstall_gateway }}" - register: openshell_rpm_reinstall_candidates - -- name: Require copied candidate RPMs - ansible.builtin.assert: - that: item.stat.exists - fail_msg: "missing candidate RPM: {{ item.item }}" - loop: "{{ openshell_rpm_reinstall_candidates.results }}" - loop_control: - label: "{{ item.item }}" - -- name: Create the target reinstall command directory - ansible.builtin.file: - path: "{{ openshell_rpm_reinstall_command | dirname }}" - state: directory - mode: "0700" - -- name: Install the target RPM reinstall command - ansible.builtin.copy: - dest: "{{ openshell_rpm_reinstall_command }}" - mode: "0700" - content: | - #!/usr/bin/env bash - set -Eeuo pipefail - - latest_transaction() { - sudo dnf history list --reverse --quiet | awk 'NR == 1 { print $1 }' - } - - before_transaction="$(latest_transaction)" - sudo dnf reinstall -y --nogpgcheck {{ openshell_rpm_reinstall_cli }} {{ openshell_rpm_reinstall_gateway }} - after_transaction="$(latest_transaction)" - if [ -z "${after_transaction}" ] || [ "${after_transaction}" = "${before_transaction}" ]; then - echo "RPM reinstall did not create a DNF transaction" >&2 - exit 1 - fi - transaction_info="$(sudo dnf history info "${after_transaction}")" - grep -Eq 'Reinstall[[:space:]]+openshell-' <<<"${transaction_info}" - grep -Eq 'Reinstall[[:space:]]+openshell-gateway-' <<<"${transaction_info}" - rpm -V openshell openshell-gateway - systemctl --user daemon-reload - systemctl --user restart {{ openshell_gateway_service }} - for _ in $(seq 1 60); do - if {{ openshell_cli_bin }} status >/dev/null 2>&1; then - exit 0 - fi - sleep 1 - done - systemctl --user status {{ openshell_gateway_service }} --no-pager >&2 || true - exit 1 diff --git a/nix/test-guest/provisioners/roles/openshell-rpm-gateway-upgrade/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-rpm-gateway-upgrade/defaults/main.yml deleted file mode 100644 index 1538cce109..0000000000 --- a/nix/test-guest/provisioners/roles/openshell-rpm-gateway-upgrade/defaults/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -openshell_rpm_upgrade_cli: /var/lib/openshell-conformance/candidate/openshell.rpm -openshell_rpm_upgrade_gateway: /var/lib/openshell-conformance/candidate/openshell-gateway.rpm -openshell_rpm_upgrade_command: /home/openshell/.local/bin/openshell-test-guest-gateway-upgrade diff --git a/nix/test-guest/provisioners/roles/openshell-rpm-gateway-upgrade/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-rpm-gateway-upgrade/tasks/main.yml deleted file mode 100644 index 1d51168714..0000000000 --- a/nix/test-guest/provisioners/roles/openshell-rpm-gateway-upgrade/tasks/main.yml +++ /dev/null @@ -1,59 +0,0 @@ ---- -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -- name: Require RPM gateway provisioner dependencies - ansible.builtin.assert: - that: - - openshell_install_source | default('') == 'rpm' - - openshell_cli_bin is defined - - openshell_gateway_service is defined - - openshell_gateway_runtime | default('') == 'rootless-podman' - fail_msg: >- - openshell-rpm-gateway-upgrade requires openshell-rpm and a configured - gateway runtime. - -- name: Verify candidate RPMs for upgrade - ansible.builtin.stat: - path: "{{ item }}" - loop: - - "{{ openshell_rpm_upgrade_cli }}" - - "{{ openshell_rpm_upgrade_gateway }}" - register: openshell_rpm_upgrade_candidates - -- name: Require copied candidate RPMs - ansible.builtin.assert: - that: item.stat.exists - fail_msg: "missing required RPM upgrade candidate: {{ item.item }}" - loop: "{{ openshell_rpm_upgrade_candidates.results }}" - -- name: Install the target RPM upgrade command - ansible.builtin.copy: - dest: "{{ openshell_rpm_upgrade_command }}" - mode: "0700" - content: | - #!/usr/bin/env bash - set -Eeuo pipefail - baseline_cli="$(rpm -q --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}' openshell)" - baseline_gateway="$(rpm -q --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}' openshell-gateway)" - candidate_cli="$(rpm -qp --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}' {{ openshell_rpm_upgrade_cli }})" - candidate_gateway="$(rpm -qp --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}' {{ openshell_rpm_upgrade_gateway }})" - if [[ "$candidate_cli" == "$baseline_cli" || "$candidate_gateway" == "$baseline_gateway" ]]; then - echo "candidate RPMs must differ from the installed baseline" >&2 - exit 1 - fi - sudo dnf install -y --allowerasing --nogpgcheck {{ openshell_rpm_upgrade_cli }} {{ openshell_rpm_upgrade_gateway }} - installed_cli="$(rpm -q --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}' openshell)" - installed_gateway="$(rpm -q --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}' openshell-gateway)" - if [[ "$installed_cli" != "$candidate_cli" || "$installed_gateway" != "$candidate_gateway" ]]; then - echo "installed RPMs do not match the candidate RPMs" >&2 - exit 1 - fi - rpm -V openshell openshell-gateway - systemctl --user daemon-reload - systemctl --user restart {{ openshell_gateway_service }} - for _ in $(seq 1 60); do - {{ openshell_cli_bin }} status >/dev/null 2>&1 && exit 0 - sleep 1 - done - exit 1 diff --git a/nix/test-guest/provisioners/roles/openshell-rpm-source/defaults/main.yml b/nix/test-guest/provisioners/roles/openshell-rpm-source/defaults/main.yml new file mode 100644 index 0000000000..68c620e7a2 --- /dev/null +++ b/nix/test-guest/provisioners/roles/openshell-rpm-source/defaults/main.yml @@ -0,0 +1,15 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +openshell_rpm_source_name: "" +openshell_rpm_source_cli_package: "" +openshell_rpm_source_gateway_package: "" +openshell_rpm_source_command_dir: /home/openshell/.local/bin +openshell_rpm_source_apply_command: >- + {{ openshell_rpm_source_command_dir }}/openshell-test-guest-rpm-apply-{{ openshell_rpm_source_name }} +openshell_rpm_source_packages: + - name: openshell + path: "{{ openshell_rpm_source_cli_package }}" + - name: openshell-gateway + path: "{{ openshell_rpm_source_gateway_package }}" diff --git a/nix/test-guest/provisioners/roles/openshell-rpm-source/tasks/main.yml b/nix/test-guest/provisioners/roles/openshell-rpm-source/tasks/main.yml new file mode 100644 index 0000000000..8e63f650d8 --- /dev/null +++ b/nix/test-guest/provisioners/roles/openshell-rpm-source/tasks/main.yml @@ -0,0 +1,64 @@ +--- +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +- name: Require an RPM source definition + ansible.builtin.assert: + that: + - openshell_rpm_source_name | length > 0 + - openshell_rpm_source_cli_package | length > 0 + - openshell_rpm_source_gateway_package | length > 0 + +- name: Verify RPM source packages + ansible.builtin.stat: + path: "{{ item.path }}" + loop: "{{ openshell_rpm_source_packages }}" + register: openshell_rpm_source_package_stats + +- name: Require RPM source packages + ansible.builtin.assert: + that: item.stat.exists + fail_msg: "missing {{ openshell_rpm_source_name }} RPM: {{ item.item.path }}" + loop: "{{ openshell_rpm_source_package_stats.results }}" + loop_control: + label: "{{ item.item.path }}" + +- name: Create the RPM source command directory + ansible.builtin.file: + path: "{{ openshell_rpm_source_command_dir }}" + state: directory + mode: "0700" + +- name: Install the RPM source apply command + ansible.builtin.copy: + dest: "{{ openshell_rpm_source_apply_command }}" + mode: "0700" + content: | + #!/usr/bin/env bash + set -Eeuo pipefail + case "${1:-}" in + "") + sudo dnf install -y --allowerasing --nogpgcheck {{ openshell_rpm_source_cli_package | quote }} {{ openshell_rpm_source_gateway_package | quote }} + ;; + --force) + sudo dnf reinstall -y --nogpgcheck {{ openshell_rpm_source_cli_package | quote }} {{ openshell_rpm_source_gateway_package | quote }} + ;; + *) + echo "usage: $0 [--force]" >&2 + exit 2 + ;; + esac + +- name: Determine whether this is the initial OpenShell source + ansible.builtin.set_fact: + openshell_rpm_source_is_initial: "{{ openshell_install_source is not defined }}" + +- name: Install the first RPM source as the initial OpenShell state + ansible.builtin.command: + cmd: "{{ openshell_rpm_source_apply_command }}" + when: openshell_rpm_source_is_initial + +- name: Publish the initial RPM OpenShell contract + ansible.builtin.include_role: + name: openshell-rpm-contract + when: openshell_rpm_source_is_initial From 2954dd4c5c1612bb5584685f966c51683f437df0 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Fri, 4 Sep 2026 17:31:35 +0200 Subject: [PATCH 3/3] test(conformance): focus RPM continuity on gateway restart Signed-off-by: Evan Lezar --- .github/workflows/conformance.yml | 3 +-- nix/test-guest/README.md | 9 ++++----- ...gateway-upgrade-restart.toml => gateway-restart.toml} | 8 -------- 3 files changed, 5 insertions(+), 15 deletions(-) rename nix/test-guest/conformance-plans/{gateway-upgrade-restart.toml => gateway-restart.toml} (72%) diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index f8dca3ac54..b4a24f84e3 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -176,8 +176,7 @@ jobs: --copy "${candidate_cli_package[0]}:/var/lib/openshell-test-guest/artifacts/openshell.rpm" \ --copy "${candidate_gateway_package[0]}:/var/lib/openshell-test-guest/artifacts/openshell-gateway.rpm" \ --copy conformance-input/openshell-conformance:/tmp/openshell-conformance \ - --copy nix/test-guest/conformance-plans/gateway-upgrade-restart.toml:/tmp/conformance-plan.toml \ - --provision openshell-latest-release-rpm-source \ + --copy nix/test-guest/conformance-plans/gateway-restart.toml:/tmp/conformance-plan.toml \ --provision openshell-candidate-rpm-source \ --provision gateway-podman \ -- /tmp/openshell-conformance run --plan /tmp/conformance-plan.toml diff --git a/nix/test-guest/README.md b/nix/test-guest/README.md index dc2bbde274..4021e03c5e 100644 --- a/nix/test-guest/README.md +++ b/nix/test-guest/README.md @@ -237,17 +237,16 @@ release for the guest architecture, stores its versioned RPMs under `/var/lib/openshell-conformance/baseline`, and publishes a target-side latest-release apply command. -For an upgrade test, order the latest-release source first so it initializes the -guest, then make the candidate source available for the lifecycle action: +For gateway restart continuity, provision the candidate source first so it +initializes the guest: ```shell ---provision openshell-latest-release-rpm-source \ --provision openshell-candidate-rpm-source \ --provision gateway-podman ``` -The upgrade plan applies the candidate source and then uses the independent -gateway-restart action to verify continuity across a subsequent gateway restart. +The gateway-restart plan runs one `gateway-restart` action to verify continuity +across the restart. `openshell-binaries-contract`, `openshell-rpm-contract`, and `openshell-rpm-source` are internal composition roles used by the public source diff --git a/nix/test-guest/conformance-plans/gateway-upgrade-restart.toml b/nix/test-guest/conformance-plans/gateway-restart.toml similarity index 72% rename from nix/test-guest/conformance-plans/gateway-upgrade-restart.toml rename to nix/test-guest/conformance-plans/gateway-restart.toml index 4a654d1daa..fefbbf471e 100644 --- a/nix/test-guest/conformance-plans/gateway-upgrade-restart.toml +++ b/nix/test-guest/conformance-plans/gateway-restart.toml @@ -7,18 +7,10 @@ version = 1 command = "/home/openshell/.local/bin/openshell-test-guest-diagnostics" timeout_secs = 60 -[[runs]] -scenario = "smoke" - [[runs]] scenario = "sandbox-continuity" workload_expectation = "reconciled" -[[runs.actions]] -name = "rpm-apply-candidate" -command = "/home/openshell/.local/bin/openshell-test-guest-rpm-apply-candidate" -timeout_secs = 120 - [[runs.actions]] name = "gateway-restart" command = "/home/openshell/.local/bin/openshell-test-guest-gateway-restart"