diff --git a/.github/workflows/branch-e2e.yml b/.github/workflows/branch-e2e.yml index eb903a55fc..a33cbc8c3b 100644 --- a/.github/workflows/branch-e2e.yml +++ b/.github/workflows/branch-e2e.yml @@ -303,9 +303,9 @@ jobs: integration-inputs-artifact-id: ${{ needs.prepare-integration.outputs.integration_inputs_artifact_id }} test-matrix: >- [ - {"environment":"fedora-podman-rootful","installer":"binaries","testsuite":"driver-podman"}, - {"environment":"fedora-podman-rootless","installer":"binaries","testsuite":"e2e-podman"}, - {"environment":"fedora-podman-rootless","installer":"binaries","testsuite":"driver-podman"} + {"environment":"fedora-podman-rootful","installer":"rpm","testsuite":"driver-podman"}, + {"environment":"fedora-podman-rootless","installer":"rpm","testsuite":"e2e-podman"}, + {"environment":"fedora-podman-rootless","installer":"rpm","testsuite":"driver-podman"} ] docker-e2e: diff --git a/CI.md b/CI.md index c5a19e1f08..5bf9425ed8 100644 --- a/CI.md +++ b/CI.md @@ -77,9 +77,10 @@ runs without optional E2E labels. Core integration qualification builds and inst the DEB on Ubuntu with Docker and installs the CLI and gateway RPMs on Fedora with rootful and rootless Podman. These lanes run conformance using the matching runtime images. Release Dev and Release Tag use the same package installers. -Fedora provider-refresh tests also use RPMs. The Podman driver-specific suites -retain the binary installer because their fixtures configure its system service, -local HTTP gateway, and CLI path. The manual Integration Tests workflow defaults +Fedora provider-refresh tests also use RPMs. The Podman driver-specific branch +lanes use RPMs for rootful and rootless user-namespace comparisons and rootless +Podman E2E. Their fixtures use the installed gateway's registration, active +configuration, and service context. The manual Integration Tests workflow defaults to the package installers and downloads the packages selected by its matrix. Three opt-in labels enable the long-running E2E suites: diff --git a/TESTING.md b/TESTING.md index a1c2f9476e..bf94a3ebc0 100644 --- a/TESTING.md +++ b/TESTING.md @@ -290,10 +290,27 @@ Run the portable subset in a disposable rootless Podman guest: ```shell nix run .#build-artifacts -nix run .#tmachine -- test fedora-podman-rootless binaries e2e-podman -nix run .#tmachine -- test fedora-podman-rootless binaries driver-podman +nix run .#tmachine -- test fedora-podman-rootless rpm e2e-podman +nix run .#tmachine -- test fedora-podman-rootless rpm driver-podman ``` +The driver suites also support the `binaries` installer. The shared installer +roles save the active gateway configuration, registration name, service scope, +service owner, and network name in `/var/lib/openshell-test/gateway.yaml`. +Suites resolve `openshell` from PATH and use that registration, including the +packaged gateway's HTTPS client credentials. Namespace fixtures modify the +active qualification configuration and restart its system or user service. +Failure diagnostics select the matching journal unit and user ID. Missing +metadata or credentials fail the run; suites do not replace package setup with +an HTTP gateway. Ansible sources participate in tmachine's installation cache +hash, so older cached installations are rebuilt with this metadata. + +The `driver-podman` suite supports rootful and rootless Podman. The +`e2e-podman` archive requires rootless Podman for its host workload fixtures. +DEB and RPM installers share the gateway role; available environments pair +DEB with Ubuntu/Docker and RPM with Fedora/Podman. A DEB/Podman run requires an +Ubuntu Podman environment. + Print the exact tmachine archive selection as a shell `PODMAN_CI_TESTS` array: ```shell diff --git a/tests/ansible/playbooks/drivers/podman/default-userns-baseline.yaml b/tests/ansible/playbooks/drivers/podman/default-userns-baseline.yaml index 7356ae3c5e..2542741dd8 100644 --- a/tests/ansible/playbooks/drivers/podman/default-userns-baseline.yaml +++ b/tests/ansible/playbooks/drivers/podman/default-userns-baseline.yaml @@ -12,6 +12,10 @@ - name: Wait for SSH ansible.builtin.wait_for_connection: + - name: Resolve installed gateway context + ansible.builtin.include_role: + name: openshell_test_gateway + - name: Detect tmachine container runtime ansible.builtin.include_role: name: tmachine_container_runtime @@ -38,7 +42,7 @@ - name: Read OpenShell gateway configuration become: true ansible.builtin.slurp: - src: /etc/openshell/gateway.toml + src: "{{ openshell_test_gateway.config_path }}" register: openshell_gateway_config - name: Require unconfigured Podman user namespaces diff --git a/tests/ansible/playbooks/drivers/podman/e2e.yaml b/tests/ansible/playbooks/drivers/podman/e2e.yaml index e839a2cfff..e20d120d68 100644 --- a/tests/ansible/playbooks/drivers/podman/e2e.yaml +++ b/tests/ansible/playbooks/drivers/podman/e2e.yaml @@ -11,6 +11,21 @@ - name: Wait for SSH ansible.builtin.wait_for_connection: + - name: Resolve installed gateway context + ansible.builtin.include_role: + name: openshell_test_gateway + + - name: Detect tmachine container runtime + ansible.builtin.include_role: + name: tmachine_container_runtime + + - name: Require rootless Podman for the E2E workload fixtures + ansible.builtin.assert: + that: + - tmachine_container_runtime_name == 'podman' + - tmachine_container_runtime_is_rootless + fail_msg: The Podman E2E archive requires the rootless Podman environment + - name: Create Podman E2E test directory become: true ansible.builtin.file: @@ -46,17 +61,7 @@ - "{{ podman_e2e_test_root }}/openshell-e2e-python-dev.tar" environment: HOME: /home/tmachine - XDG_RUNTIME_DIR: /run/user/1000 - - - name: Remove any previous OpenShell gateway registration - ansible.builtin.command: - argv: [/usr/local/bin/openshell, gateway, remove, tmachine] - changed_when: false - failed_when: false - - - name: Register the configured OpenShell gateway - ansible.builtin.command: - argv: [/usr/local/bin/openshell, gateway, add, http://127.0.0.1:17670, --local, --name, tmachine] + XDG_RUNTIME_DIR: "/run/user/{{ tmachine_container_runtime_tmachine_uid.stdout }}" - name: Run Podman E2E archive tests ansible.builtin.command: @@ -73,16 +78,16 @@ - --no-fail-fast environment: CONTAINER_ENGINE: podman - CONTAINER_HOST: unix:///run/user/1000/podman/podman.sock + CONTAINER_HOST: "unix://{{ tmachine_container_runtime_socket }}" HOME: /home/tmachine - OPENSHELL_BIN: /usr/local/bin/openshell + OPENSHELL_BIN: "{{ openshell_test_cli.stdout }}" OPENSHELL_E2E_CONTAINER_ENGINE_UNSET_XDG_CONFIG_HOME: "1" OPENSHELL_E2E_DRIVER: podman - OPENSHELL_E2E_NETWORK_NAME: tmachine + OPENSHELL_E2E_NETWORK_NAME: "{{ openshell_test_gateway.network_name }}" OPENSHELL_E2E_SANDBOX_NAMESPACE: tmachine - OPENSHELL_GATEWAY: tmachine - OPENSHELL_PODMAN_SOCKET: /run/user/1000/podman/podman.sock - XDG_RUNTIME_DIR: /run/user/1000 + OPENSHELL_GATEWAY: "{{ openshell_test_gateway.name }}" + OPENSHELL_PODMAN_SOCKET: "{{ tmachine_container_runtime_socket }}" + XDG_RUNTIME_DIR: "/run/user/{{ tmachine_container_runtime_tmachine_uid.stdout }}" register: podman_e2e_test_result changed_when: false failed_when: false @@ -95,18 +100,10 @@ ansible.builtin.debug: var: podman_e2e_test_result.stdout_lines - - name: Capture OpenShell gateway journal after Podman E2E test failure - become: true - ansible.builtin.command: - argv: [journalctl, --unit, openshell-gateway.service, --no-pager, --lines, "200"] - changed_when: false - failed_when: false - when: podman_e2e_test_result.rc != 0 - register: podman_e2e_gateway_journal - - - name: Show OpenShell gateway journal after Podman E2E test failure - ansible.builtin.debug: - var: podman_e2e_gateway_journal.stdout_lines + - name: Collect installed gateway diagnostics after test failure + ansible.builtin.include_role: + name: openshell_test_gateway + tasks_from: journal.yaml when: podman_e2e_test_result.rc != 0 - name: Require Podman E2E archive success diff --git a/tests/ansible/playbooks/drivers/podman/tests.yaml b/tests/ansible/playbooks/drivers/podman/tests.yaml index abe2f6e553..11390fb57f 100644 --- a/tests/ansible/playbooks/drivers/podman/tests.yaml +++ b/tests/ansible/playbooks/drivers/podman/tests.yaml @@ -12,6 +12,10 @@ - name: Wait for SSH ansible.builtin.wait_for_connection: + - name: Resolve installed gateway context + ansible.builtin.include_role: + name: openshell_test_gateway + - name: Detect tmachine container runtime ansible.builtin.include_role: name: tmachine_container_runtime @@ -39,21 +43,12 @@ owner: tmachine group: tmachine - - name: Remove any previous OpenShell gateway registration - ansible.builtin.command: - argv: [/usr/local/bin/openshell, gateway, remove, tmachine] - changed_when: false - failed_when: false - - - name: Register the configured OpenShell gateway - ansible.builtin.command: - argv: [/usr/local/bin/openshell, gateway, add, http://127.0.0.1:17670, --local, --name, tmachine] - - name: Run Podman archive tests ansible.builtin.command: argv: [cargo-nextest, nextest, run, --archive-file, "{{ podman_test_root }}/tests.tar.zst", --workspace-remap, "{{ podman_test_root }}", --no-capture] environment: - OPENSHELL_BIN: /usr/local/bin/openshell + OPENSHELL_GATEWAY: "{{ openshell_test_gateway.name }}" + OPENSHELL_BIN: "{{ openshell_test_cli.stdout }}" OPENSHELL_TEST_INPUT_DIR: "{{ podman_test_input_dir }}" OPENSHELL_PODMAN_TEST_IMAGE: "{{ openshell_podman_test_image | default('') }}" register: podman_test_result @@ -68,24 +63,10 @@ ansible.builtin.debug: var: podman_test_result.stdout_lines - - name: Capture OpenShell gateway journal after Podman test failure - become: true - ansible.builtin.command: - argv: - - journalctl - - --unit - - openshell-gateway.service - - --no-pager - - --lines - - "200" - changed_when: false - failed_when: false - when: podman_test_result.rc != 0 - register: podman_gateway_journal - - - name: Show OpenShell gateway journal after Podman test failure - ansible.builtin.debug: - var: podman_gateway_journal.stdout_lines + - name: Collect installed gateway diagnostics after test failure + ansible.builtin.include_role: + name: openshell_test_gateway + tasks_from: journal.yaml when: podman_test_result.rc != 0 - name: Discover Podman containers after test failure diff --git a/tests/ansible/playbooks/drivers/podman/userns-profile.yaml b/tests/ansible/playbooks/drivers/podman/userns-profile.yaml index a3071c2833..64880a0aa4 100644 --- a/tests/ansible/playbooks/drivers/podman/userns-profile.yaml +++ b/tests/ansible/playbooks/drivers/podman/userns-profile.yaml @@ -12,6 +12,10 @@ - name: Wait for SSH ansible.builtin.wait_for_connection: + - name: Resolve installed gateway context + ansible.builtin.include_role: + name: openshell_test_gateway + - name: Detect tmachine container runtime ansible.builtin.include_role: name: tmachine_container_runtime @@ -38,21 +42,15 @@ - name: Apply the Podman user-namespace fixture become: true ansible.builtin.blockinfile: - path: /etc/openshell/gateway.toml + path: "{{ openshell_test_gateway.config_path }}" + insertafter: '^\[openshell\.drivers\.podman\]$' marker: "# {mark} OpenShell Podman userns test fixture" block: "{{ lookup('ansible.builtin.file', podman_userns_config) | trim }}" - - name: Restart OpenShell gateway with the Podman user-namespace fixture - become: true - ansible.builtin.systemd_service: - name: openshell-gateway.service - state: restarted - - - name: Wait for the configured OpenShell gateway - ansible.builtin.wait_for: - host: 127.0.0.1 - port: 17670 - timeout: 60 + - name: Restart installed gateway with the Podman user-namespace fixture + ansible.builtin.include_role: + name: openshell_test_gateway + tasks_from: restart.yaml - name: Capture direct Podman user-namespace mapping become: true diff --git a/tests/ansible/roles/openshell_client/tasks/main.yaml b/tests/ansible/roles/openshell_client/tasks/main.yaml index 6586f23ac2..8a60195fa1 100644 --- a/tests/ansible/roles/openshell_client/tasks/main.yaml +++ b/tests/ansible/roles/openshell_client/tasks/main.yaml @@ -12,3 +12,17 @@ - --local - --name - "{{ openshell_client_gateway_name | default('tmachine') }}" + +- name: Publish test gateway context + ansible.builtin.include_role: + name: openshell_test_gateway + tasks_from: publish.yaml + vars: + openshell_test_gateway_context: + name: "{{ openshell_client_gateway_name | default('tmachine') }}" + config_path: "{{ openshell_client_gateway_config_path | default('/etc/openshell/gateway.toml') }}" + service_scope: "{{ openshell_client_gateway_service_scope | default('system') }}" + service_user: "{{ openshell_gateway_user if openshell_client_gateway_service_scope | default('system') == 'user' else 'root' }}" + service_home: "{{ openshell_gateway_home if openshell_client_gateway_service_scope | default('system') == 'user' else '/root' }}" + service_uid: "{{ openshell_gateway_uid if openshell_client_gateway_service_scope | default('system') == 'user' else '0' }}" + network_name: "{{ openshell_client_gateway_network_name | default('tmachine') }}" diff --git a/tests/ansible/roles/openshell_packaged_gateway/tasks/main.yaml b/tests/ansible/roles/openshell_packaged_gateway/tasks/main.yaml index a3e04f52bf..a50e238150 100644 --- a/tests/ansible/roles/openshell_packaged_gateway/tasks/main.yaml +++ b/tests/ansible/roles/openshell_packaged_gateway/tasks/main.yaml @@ -121,3 +121,6 @@ vars: openshell_client_gateway_endpoint: https://127.0.0.1:17670 openshell_client_gateway_name: openshell + openshell_client_gateway_config_path: /var/lib/openshell-qualification/gateway.toml + openshell_client_gateway_service_scope: user + openshell_client_gateway_network_name: openshell diff --git a/tests/ansible/roles/openshell_test_gateway/tasks/journal.yaml b/tests/ansible/roles/openshell_test_gateway/tasks/journal.yaml new file mode 100644 index 0000000000..2e5d7874f9 --- /dev/null +++ b/tests/ansible/roles/openshell_test_gateway/tasks/journal.yaml @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +--- +# Root's packaged user unit can log to the system journal. Match journal +# fields instead of --user so both root and tmachine user units are captured. +- name: Capture installed gateway journal + become: true + ansible.builtin.command: + argv: >- + {{ ['journalctl'] + + (['_SYSTEMD_USER_UNIT=openshell-gateway.service', '_UID=' ~ openshell_test_gateway.service_uid] + if openshell_test_gateway.service_scope == 'user' + else ['--unit', 'openshell-gateway.service']) + + ['--no-pager', '--lines', '200'] }} + register: openshell_test_gateway_journal + changed_when: false + failed_when: false + +- name: Show installed gateway journal + ansible.builtin.debug: + var: openshell_test_gateway_journal.stdout_lines diff --git a/tests/ansible/roles/openshell_test_gateway/tasks/main.yaml b/tests/ansible/roles/openshell_test_gateway/tasks/main.yaml new file mode 100644 index 0000000000..55d18b310e --- /dev/null +++ b/tests/ansible/roles/openshell_test_gateway/tasks/main.yaml @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +--- +- name: Read installed gateway context + ansible.builtin.slurp: + src: /var/lib/openshell-test/gateway.yaml + register: openshell_test_gateway_metadata + +- name: Load installed gateway context + ansible.builtin.set_fact: + openshell_test_gateway: "{{ (openshell_test_gateway_metadata.content | b64decode | from_yaml).openshell_test_gateway }}" + +- name: Validate installed gateway context + ansible.builtin.assert: + that: + - openshell_test_gateway.service_scope in ['system', 'user'] + - openshell_test_gateway.config_path is match('^/') + - openshell_test_gateway.name | length > 0 + +- name: Resolve installed OpenShell CLI from PATH + ansible.builtin.command: + argv: [/bin/sh, -c, command -v openshell] + register: openshell_test_cli + changed_when: false + +- name: Check active gateway configuration + become: true + ansible.builtin.stat: + path: "{{ openshell_test_gateway.config_path }}" + register: openshell_test_gateway_config + +- name: Require active gateway configuration + ansible.builtin.assert: + that: [openshell_test_gateway_config.stat.isreg | default(false)] + +- name: Resolve gateway service environment + ansible.builtin.set_fact: + openshell_test_gateway_service_environment: + HOME: "{{ openshell_test_gateway.service_home }}" + XDG_RUNTIME_DIR: "/run/user/{{ openshell_test_gateway.service_uid }}" + DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ openshell_test_gateway.service_uid }}/bus" diff --git a/tests/ansible/roles/openshell_test_gateway/tasks/publish.yaml b/tests/ansible/roles/openshell_test_gateway/tasks/publish.yaml new file mode 100644 index 0000000000..1e04d266c2 --- /dev/null +++ b/tests/ansible/roles/openshell_test_gateway/tasks/publish.yaml @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +--- +# Installation and suite execution are separate Ansible processes. Persist only +# non-secret metadata; the CLI keeps registration and mTLS credentials itself. +- name: Create test gateway metadata directory + become: true + ansible.builtin.file: + path: /var/lib/openshell-test + state: directory + owner: root + group: root + mode: "0755" + +- name: Publish installed gateway context + become: true + ansible.builtin.copy: + dest: /var/lib/openshell-test/gateway.yaml + owner: root + group: root + mode: "0644" + content: "{{ {'openshell_test_gateway': openshell_test_gateway_context} | to_nice_yaml }}" diff --git a/tests/ansible/roles/openshell_test_gateway/tasks/restart.yaml b/tests/ansible/roles/openshell_test_gateway/tasks/restart.yaml new file mode 100644 index 0000000000..56b56123f9 --- /dev/null +++ b/tests/ansible/roles/openshell_test_gateway/tasks/restart.yaml @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +--- +- name: Restart installed gateway + block: + - name: Restart gateway service with the active configuration + become: true + become_user: "{{ openshell_test_gateway.service_user }}" + ansible.builtin.systemd_service: + name: openshell-gateway.service + scope: "{{ openshell_test_gateway.service_scope }}" + state: restarted + environment: "{{ openshell_test_gateway_service_environment }}" + + - name: Wait for authenticated gateway readiness + ansible.builtin.command: + argv: ["{{ openshell_test_cli.stdout }}", gateway, info, --output, json] + environment: + OPENSHELL_GATEWAY: "{{ openshell_test_gateway.name }}" + changed_when: false + register: openshell_test_gateway_ready + retries: 30 + delay: 2 + until: >- + openshell_test_gateway_ready.rc == 0 and + (openshell_test_gateway_ready.stdout | from_json).status == 'healthy' + + rescue: + - name: Collect gateway restart diagnostics + ansible.builtin.include_tasks: journal.yaml + + - name: Report gateway restart failure + ansible.builtin.fail: + msg: Installed gateway failed to restart with the driver fixture