diff --git a/.github/workflows/e2e-suites.yml b/.github/workflows/e2e-suites.yml index 04bd61ef37..791761befb 100644 --- a/.github/workflows/e2e-suites.yml +++ b/.github/workflows/e2e-suites.yml @@ -77,6 +77,11 @@ jobs: {"environment":"ubuntu-docker-rootful","installer":"deb","family":"features","package":"oci-image","gateway":"openshell","runner":"ubuntu-24.04"}, {"environment":"fedora-podman-rootful","installer":"rpm","family":"features","package":"oci-image","gateway":"openshell","runner":"ubuntu-24.04"}, {"environment":"fedora-podman-rootless","installer":"rpm","family":"features","package":"oci-image","gateway":"openshell","runner":"ubuntu-24.04"}, + {"environment":"ubuntu-docker-rootful","installer":"deb","family":"features","package":"host-gateway","gateway":"openshell","runner":"ubuntu-24.04"}, + {"environment":"fedora-podman-rootful","installer":"rpm","family":"features","package":"host-gateway","gateway":"openshell","runner":"ubuntu-24.04"}, + {"environment":"fedora-podman-rootless","installer":"rpm","family":"features","package":"host-gateway","gateway":"openshell","runner":"ubuntu-24.04"}, + {"environment":"ubuntu-k3s","installer":"k3s","family":"features","package":"host-gateway","gateway":"tmachine","runner":"ubuntu-24.04"}, + {"environment":"ubuntu-k3s","installer":"k3s-ha","family":"features","package":"host-gateway","gateway":"tmachine","runner":"ubuntu-24.04"}, {"environment":"fedora-podman-rootful-keycloak","installer":"rpm","family":"features","package":"keycloak-provider-refresh","gateway":"openshell","runner":"ubuntu-24.04"}, {"environment":"fedora-podman-rootless-keycloak","installer":"rpm","family":"features","package":"keycloak-provider-refresh","gateway":"openshell","runner":"ubuntu-24.04"}, {"environment":"fedora-podman-rootful-userns-default","installer":"binaries","family":"drivers/podman","gateway":"tmachine","runner":"ubuntu-24.04"}, diff --git a/TESTING.md b/TESTING.md index 12d81ee984..a3936ad649 100644 --- a/TESTING.md +++ b/TESTING.md @@ -586,6 +586,13 @@ mise run e2e:oci-image:podman Both tasks set `OPENSHELL_TEST_CONTAINER_ENGINE`, the command that builds test images into the gateway's image store. +The `host-gateway` feature suite (`tests/suites/features/host-gateway`) checks +that a sandbox from the Python test image reaches a server the test runs on the +host through `host.openshell.internal`. CI runs it on Docker rootful, Podman +rootful and rootless, and the `k3s` and `k3s-ha` installers. The tests run on +the K3s node, so the K3s installers set the chart's `server.hostGatewayIP` to +the node's address. + Run the VM-backed Rust CLI e2e suite: ```shell diff --git a/tests/suites/features/Cargo.lock b/tests/suites/features/Cargo.lock index c12c38f5ce..bb1a1427ee 100644 --- a/tests/suites/features/Cargo.lock +++ b/tests/suites/features/Cargo.lock @@ -1164,6 +1164,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "host-gateway" +version = "0.0.0" +dependencies = [ + "openshell-testkit", + "tempfile", + "tokio", +] + [[package]] name = "hostname" version = "0.4.2" diff --git a/tests/suites/features/Cargo.toml b/tests/suites/features/Cargo.toml index 5912e9176f..af907b4b15 100644 --- a/tests/suites/features/Cargo.toml +++ b/tests/suites/features/Cargo.toml @@ -3,4 +3,4 @@ [workspace] resolver = "2" -members = ["keycloak-provider-refresh", "kubernetes-updates", "oci-image"] +members = ["host-gateway", "keycloak-provider-refresh", "kubernetes-updates", "oci-image"] diff --git a/tests/suites/features/host-gateway/Cargo.toml b/tests/suites/features/host-gateway/Cargo.toml new file mode 100644 index 0000000000..d81b6cafb2 --- /dev/null +++ b/tests/suites/features/host-gateway/Cargo.toml @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "host-gateway" +description = "Features suite: sandboxes reach a host server through host.openshell.internal" +version = "0.0.0" +edition = "2024" +rust-version = "1.94" +license = "Apache-2.0" +repository = "https://github.com/NVIDIA/OpenShell" +publish = false + +[dependencies] +openshell-testkit = { path = "../../../testkit/testkit" } +tempfile = "3" +tokio = { version = "1.43", features = ["io-util", "net", "rt"] } diff --git a/tests/suites/features/host-gateway/tests/host_gateway.rs b/tests/suites/features/host-gateway/tests/host_gateway.rs new file mode 100644 index 0000000000..024b1cd84b --- /dev/null +++ b/tests/suites/features/host-gateway/tests/host_gateway.rs @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! `host.openshell.internal` names the host from inside a sandbox, on every +//! compute driver. +//! +//! The test starts an HTTP server where the tests run, which is the host the +//! gateway runs its sandboxes on, and runs a client in a sandbox from the +//! Python test image ([`testkit::python_image`]) that requests a page from that +//! server through the name. + +use testkit::{Context, Error, Result, ResultExt as _}; +use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::task::JoinHandle; + +/// The image's system Python, which needs nothing beyond its standard library. +/// The policy names its real path, `/usr/bin/python3.*`, because a rule matches +/// the executable behind a symlink. +const PYTHON: &str = "/usr/bin/python3"; + +/// Requests `/` from `host.openshell.internal` on the port in `argv[1]` and +/// prints the response. A failed connection is retried for 20 seconds, so a +/// slow start doesn't fail the test, and reported on stderr. +const CLIENT: &str = r#" +import socket, sys, time + +host, port = "host.openshell.internal", int(sys.argv[1]) +deadline = time.monotonic() + 20 +while True: + try: + conn = socket.create_connection((host, port), timeout=10) + break + except OSError as error: + print(f"connect {host}:{port}: {error}", file=sys.stderr, flush=True) + if time.monotonic() > deadline: + sys.exit(1) + time.sleep(1) +with conn: + conn.sendall(f"GET / HTTP/1.1\r\nHost: {host}:{port}\r\nConnection: close\r\n\r\n".encode()) + response = b"" + while chunk := conn.recv(4096): + response += chunk +print(response.decode(errors="replace")) +"#; + +/// A sandbox from the Python test image resolves `host.openshell.internal` +/// and gets a response from a server the test runs on the host. The default +/// policy denies all network access, so the test passes one that lets only the +/// image's Python reach the server's port. +#[testkit::test] +async fn python_sandbox_reaches_host_server(cx: &mut Context) -> testkit::Result { + let marker = format!("host-gateway-{}", cx.run_id()); + let server = HostServer::start(marker.clone()).await?; + let policy = PolicyFile::new(&policy(server.port))?; + let port = server.port.to_string(); + let image = testkit::python_image(); + + // The client runs through `sandbox exec` in a Ready sandbox, not as the + // main process. A gateway with several replicas (k3s-ha) can refuse to + // attach to a main process that already finished with "sandbox is not + // ready" when the request reaches a replica that doesn't hold the + // sandbox's session. + let name = cx.name("hg"); + cx.track(&name); + cx.cli([ + "sandbox", + "create", + "--name", + &name, + "--from", + &image, + "--policy", + policy.path()?, + "--detach", + "--no-tty", + "--", + "sh", + "-c", + "exec sleep infinity", + ]) + .await? + .success() + .context(format!("create a sandbox from {image}"))?; + cx.adopt(&name).await?.wait_ready().await?; + + let client = cx + .cli([ + "sandbox", "exec", "--name", &name, "--no-tty", "--", PYTHON, "-c", CLIENT, &port, + ]) + .await? + .success() + .context("run the client in the sandbox")?; + testkit::ensure!( + client.stdout.contains(&marker), + "the client did not get the host server's response:\n{}{}", + client.stdout, + client.stderr + ); + Ok(()) +} + +/// A sandbox policy that allows the image's system Python ([`PYTHON`]) to +/// connect to `host.openshell.internal` on `port`. The endpoint names only the +/// host and port, as the policy docs describe, so the driver's address for the +/// name is what makes it reachable, not an `allowed_ips` range. +fn policy(port: u16) -> String { + format!( + r#"version: 1 + +filesystem_policy: + include_workdir: true + read_only: [/usr, /lib, /lib64, /proc, /dev/urandom, /etc] + read_write: [/sandbox, /tmp, /dev/null] +landlock: + compatibility: best_effort + +process: + run_as_user: sandbox + run_as_group: sandbox + +network_policies: + host_server: + name: host_server + endpoints: + - host: host.openshell.internal + port: {port} + binaries: + - path: /usr/bin/python3.* +"# + ) +} + +/// An HTTP server on the host that answers every request with `body`. It +/// stops when dropped. +struct HostServer { + port: u16, + task: JoinHandle<()>, +} + +impl HostServer { + /// Listen on every host address, since drivers map + /// `host.openshell.internal` to different ones. + async fn start(body: String) -> Result { + let listener = TcpListener::bind(("0.0.0.0", 0)) + .await + .map_err(|error| Error::from_error(&error)) + .context("bind the host server")?; + let port = listener + .local_addr() + .map_err(|error| Error::from_error(&error)) + .context("read the host server's address")? + .port(); + let task = tokio::spawn(async move { + while let Ok((stream, _)) = listener.accept().await { + tokio::spawn(respond(stream, body.clone())); + } + }); + Ok(Self { port, task }) + } +} + +impl Drop for HostServer { + fn drop(&mut self) { + self.task.abort(); + } +} + +/// Read one request's headers from `stream` and answer with `body`. +async fn respond(mut stream: TcpStream, body: String) { + let _ = stream.set_nodelay(true); + let mut request = Vec::new(); + let mut buf = [0_u8; 1024]; + while !request.windows(4).any(|window| window == b"\r\n\r\n") { + match stream.read(&mut buf).await { + Ok(read) if read > 0 && request.len() < 64 * 1024 => { + request.extend_from_slice(&buf[..read]); + } + _ => return, + } + } + let response = format!( + "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", + body.len() + ); + let _ = stream.write_all(response.as_bytes()).await; + let _ = stream.shutdown().await; +} + +/// A sandbox policy written to a temporary file for `sandbox create --policy`. +struct PolicyFile(tempfile::NamedTempFile); + +impl PolicyFile { + fn new(contents: &str) -> Result { + let file = tempfile::NamedTempFile::new() + .map_err(|error| Error::from_error(&error)) + .context("create policy file")?; + std::fs::write(file.path(), contents) + .map_err(|error| Error::from_error(&error)) + .context(format!("write {}", file.path().display()))?; + Ok(Self(file)) + } + + fn path(&self) -> Result<&str> { + let path = self.0.path(); + path.to_str() + .ok_or_else(|| Error::msg(format!("{} is not UTF-8", path.display()))) + } +} diff --git a/tests/testkit/README.md b/tests/testkit/README.md index 3f786fe65d..c02ad01c25 100644 --- a/tests/testkit/README.md +++ b/tests/testkit/README.md @@ -75,6 +75,10 @@ fails. A killed test process skips cleanup, so names are unique to the run. compute driver. Driver suites call it first. - `testkit::base_image()` for the image to create sandboxes from or build on: `OPENSHELL_TEST_BASE_IMAGE`, or OpenShell's default sandbox image. +- `testkit::python_image()` for sandboxes that run `python3`, curl, git, or + openssl: `OPENSHELL_TEST_PYTHON_IMAGE`, or `openshell/e2e-python:dev` + (`e2e/python/Dockerfile.workload`). The tmachine installers load it into + every environment; `mise run e2e:workload:build` builds it locally. - `cx.cli([...])` to run the installed CLI against the same gateway, with `success()`, `failure()`, and `json()` on the output. `cx.cli_handle()` returns that CLI for code that can't borrow `cx`, such as a cleanup hook. diff --git a/tests/testkit/testkit/src/cli.rs b/tests/testkit/testkit/src/cli.rs index abba6065c3..5ac54e4108 100644 --- a/tests/testkit/testkit/src/cli.rs +++ b/tests/testkit/testkit/src/cli.rs @@ -21,7 +21,7 @@ use crate::gateway::{ENDPOINT_ENV, GATEWAY_ENV, Gateway, WORKSPACE_ENV}; /// How long one CLI invocation may take. pub const CLI_TIMEOUT: Duration = Duration::from_mins(5); /// The CLI binary to run. Defaults to `openshell` on PATH. -pub const BIN_ENV: &str = "OPENSHELL_BIN"; +pub(crate) const BIN_ENV: &str = "OPENSHELL_BIN"; /// Runs the CLI against the gateway and workspace the tests use. #[derive(Clone, Debug)] diff --git a/tests/testkit/testkit/src/gateway.rs b/tests/testkit/testkit/src/gateway.rs index ee3e333500..6513fcfc4e 100644 --- a/tests/testkit/testkit/src/gateway.rs +++ b/tests/testkit/testkit/src/gateway.rs @@ -21,9 +21,9 @@ use openshell_sdk::{ClientConfig, ClientIdentity, Health, OpenShellClient, Servi use crate::error::{Error, Result, ResultExt}; -pub const ENDPOINT_ENV: &str = "OPENSHELL_GATEWAY_ENDPOINT"; -pub const GATEWAY_ENV: &str = "OPENSHELL_GATEWAY"; -pub const WORKSPACE_ENV: &str = "OPENSHELL_WORKSPACE"; +pub(crate) const ENDPOINT_ENV: &str = "OPENSHELL_GATEWAY_ENDPOINT"; +pub(crate) const GATEWAY_ENV: &str = "OPENSHELL_GATEWAY"; +pub(crate) const WORKSPACE_ENV: &str = "OPENSHELL_WORKSPACE"; const DEFAULT_WORKSPACE: &str = "default"; // Mirrors the SDK's endpoint settings for channels testkit builds itself. diff --git a/tests/testkit/testkit/src/image.rs b/tests/testkit/testkit/src/image.rs index 4e398800b1..26bb2278fe 100644 --- a/tests/testkit/testkit/src/image.rs +++ b/tests/testkit/testkit/src/image.rs @@ -1,18 +1,35 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//! The base image tests create sandboxes from or build their images on. +//! The images tests create sandboxes from or build their images on. /// Overrides [`DEFAULT_BASE_IMAGE`], e.g. with a mirror. -pub const BASE_IMAGE_ENV: &str = "OPENSHELL_TEST_BASE_IMAGE"; +const BASE_IMAGE_ENV: &str = "OPENSHELL_TEST_BASE_IMAGE"; /// OpenShell's built-in default sandbox image. Pulling it needs access to /// nvcr.io. -pub const DEFAULT_BASE_IMAGE: &str = "nvcr.io/nvidia/base/ubuntu:24.04"; +const DEFAULT_BASE_IMAGE: &str = "nvcr.io/nvidia/base/ubuntu:24.04"; + +/// Overrides [`DEFAULT_PYTHON_IMAGE`]. +const PYTHON_IMAGE_ENV: &str = "OPENSHELL_TEST_PYTHON_IMAGE"; +/// The workload image for tests that run `python3`, curl, git or openssl in a +/// sandbox, built from `e2e/python/Dockerfile.workload`. It isn't published: +/// the tmachine installers load it into every environment, and +/// `mise run e2e:workload:build` builds it locally. +const DEFAULT_PYTHON_IMAGE: &str = "openshell/e2e-python:dev"; /// The image named by [`BASE_IMAGE_ENV`], or [`DEFAULT_BASE_IMAGE`]. pub fn base_image() -> String { - std::env::var(BASE_IMAGE_ENV) + image_from_env(BASE_IMAGE_ENV, DEFAULT_BASE_IMAGE) +} + +/// The image named by [`PYTHON_IMAGE_ENV`], or [`DEFAULT_PYTHON_IMAGE`]. +pub fn python_image() -> String { + image_from_env(PYTHON_IMAGE_ENV, DEFAULT_PYTHON_IMAGE) +} + +fn image_from_env(var: &str, default: &str) -> String { + std::env::var(var) .ok() .filter(|image| !image.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_BASE_IMAGE.to_string()) + .unwrap_or_else(|| default.to_string()) } diff --git a/tests/testkit/testkit/src/lib.rs b/tests/testkit/testkit/src/lib.rs index dee03cac53..834f28c1e7 100644 --- a/tests/testkit/testkit/src/lib.rs +++ b/tests/testkit/testkit/src/lib.rs @@ -39,14 +39,14 @@ mod registry; mod run; pub use cleanup::CLEANUP_TIMEOUT; -pub use cli::{BIN_ENV, CLI_TIMEOUT, Cli, CliOutput}; +pub use cli::{CLI_TIMEOUT, Cli, CliOutput}; pub use context::{ Context, DELETE_TIMEOUT, EXEC_TIMEOUT, ExecOutput, READY_TIMEOUT, STOP_TIMEOUT, Sandbox, SandboxBuilder, }; pub use error::{Error, Result, ResultExt}; pub use gateway::Gateway; -pub use image::{BASE_IMAGE_ENV, DEFAULT_BASE_IMAGE, base_image}; +pub use image::{base_image, python_image}; pub use openshell_sdk as sdk; pub use poll::{Poll, poll_until}; pub use program::main; diff --git a/tests/tmachine/ansible/playbooks/openshell-k3s.yaml b/tests/tmachine/ansible/playbooks/openshell-k3s.yaml index 2f03f9d7a8..5b68bc0067 100644 --- a/tests/tmachine/ansible/playbooks/openshell-k3s.yaml +++ b/tests/tmachine/ansible/playbooks/openshell-k3s.yaml @@ -34,6 +34,9 @@ src: "{{ openshell_sandbox_image }}" - name: supervisor src: "{{ openshell_supervisor_image }}" + # The testkit's python_image() sandbox workload. + - name: e2e-python + src: "{{ openshell_e2e_python_image }}" - name: Import OpenShell images into K3s ansible.builtin.command: @@ -49,6 +52,7 @@ - gateway - sandbox - supervisor + - e2e-python - name: Copy OpenShell Helm chart ansible.builtin.copy: @@ -56,6 +60,41 @@ dest: /var/lib/openshell/artifacts/helm-chart.tgz mode: "0600" + # Tests run in the guest, so host.openshell.internal must resolve to the + # node itself. A tmachine environment is one VM, so the cluster has one + # node; the check below fails if that stops being true, because then the + # address must be the one of the node the tests run on. QEMU user + # networking gives every boot the same guest address (10.0.2.15), so the + # value stays valid when the disk is reused. + # The node can also list an IPv6 address after it, so take the IPv4 one. + - name: Discover the K3s node address + ansible.builtin.command: + argv: + - /usr/local/bin/k3s + - kubectl + - --request-timeout=10s + - get + - nodes + - --output=jsonpath={.items[0].status.addresses[?(@.type=="InternalIP")].address} + register: k3s_node_address + changed_when: false + until: >- + k3s_node_address.rc == 0 + and k3s_node_address.stdout.split() | select('match', '^[0-9.]+$') | list | length > 0 + retries: 30 + delay: 2 + + - name: Check the K3s cluster has a single node + ansible.builtin.command: + argv: [/usr/local/bin/k3s, kubectl, --request-timeout=10s, get, nodes, --output=name] + register: k3s_nodes + changed_when: false + failed_when: k3s_nodes.rc != 0 or k3s_nodes.stdout_lines | length != 1 + + - name: Select the K3s node IPv4 address + ansible.builtin.set_fact: + k3s_node_ipv4: "{{ k3s_node_address.stdout.split() | select('match', '^[0-9.]+$') | first }}" + - name: Write OpenShell Helm values ansible.builtin.copy: dest: /var/lib/openshell/artifacts/values.yaml @@ -85,6 +124,7 @@ auth: allowUnauthenticatedUsers: true disableTls: true + hostGatewayIP: "{{ k3s_node_ipv4 }}" telemetryEnabled: false - name: Write OpenShell Helm values overlay diff --git a/tests/tmachine/ansible/roles/openshell_gateway/tasks/docker.yaml b/tests/tmachine/ansible/roles/openshell_gateway/tasks/docker.yaml index 3585c1d33f..11b033972a 100644 --- a/tests/tmachine/ansible/roles/openshell_gateway/tasks/docker.yaml +++ b/tests/tmachine/ansible/roles/openshell_gateway/tasks/docker.yaml @@ -2,7 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 --- -- name: Copy OpenShell runtime images +# Tests run sandboxes from the e2e-python workload image (the testkit's +# python_image()), so load it next to the runtime images. +- name: Copy OpenShell runtime and test workload images become: true ansible.builtin.copy: src: "{{ item.src }}" @@ -15,8 +17,10 @@ src: "{{ openshell_sandbox_image }}" - name: supervisor src: "{{ openshell_supervisor_image }}" + - name: e2e-python + src: "{{ openshell_e2e_python_image }}" -- name: Load OpenShell runtime images +- name: Load OpenShell runtime and test workload images become: true ansible.builtin.command: argv: @@ -27,3 +31,4 @@ loop: - sandbox - supervisor + - e2e-python diff --git a/tests/tmachine/ansible/roles/openshell_gateway/tasks/podman.yaml b/tests/tmachine/ansible/roles/openshell_gateway/tasks/podman.yaml index 784c56f6cb..b85361b769 100644 --- a/tests/tmachine/ansible/roles/openshell_gateway/tasks/podman.yaml +++ b/tests/tmachine/ansible/roles/openshell_gateway/tasks/podman.yaml @@ -2,7 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 --- -- name: Copy OpenShell runtime images +# Tests run sandboxes from the e2e-python workload image (the testkit's +# python_image()), so load it next to the runtime images. +- name: Copy OpenShell runtime and test workload images become: true ansible.builtin.copy: src: "{{ item.src }}" @@ -15,8 +17,10 @@ src: "{{ openshell_sandbox_image }}" - name: supervisor src: "{{ openshell_supervisor_image }}" + - name: e2e-python + src: "{{ openshell_e2e_python_image }}" -- name: Load OpenShell runtime images +- name: Load OpenShell runtime and test workload images become: true become_user: "{{ openshell_gateway_user }}" ansible.builtin.command: @@ -28,6 +32,7 @@ loop: - sandbox - supervisor + - e2e-python environment: HOME: "{{ openshell_gateway_home }}" XDG_RUNTIME_DIR: "/run/user/{{ openshell_gateway_uid }}" diff --git a/tests/tmachine/config.nix b/tests/tmachine/config.nix index ac1aa28d6d..24206097f9 100644 --- a/tests/tmachine/config.nix +++ b/tests/tmachine/config.nix @@ -23,9 +23,12 @@ let OVMF = firmwarePkgs.OVMF; }; qemu = qemuPkgs.qemu.override { hostCpuOnly = true; }; + # Every installer loads the sandbox workload image tests run python3 in. + e2ePythonImage = "../../artifacts/images/openshell-e2e-python-dev.tar"; k3sInputs = { agent_sandbox_version = "0.5.0"; openshell_cli_binary = "../../artifacts/binaries/${muslTarget}/openshell"; + openshell_e2e_python_image = e2ePythonImage; openshell_gateway_image = "../../artifacts/images/openshell-gateway-tmachine.tar"; openshell_helm_chart = "../../artifacts/helm/helm-chart-0.0.0.tgz"; openshell_sandbox_image = "../../artifacts/images/openshell-sandbox-tmachine.tar"; @@ -204,6 +207,7 @@ let inputs = { openshell_cli_binary = "../../artifacts/binaries/${muslTarget}/openshell"; openshell_gateway_binary = "../../artifacts/binaries/${gnuTarget}/openshell-gateway"; + openshell_e2e_python_image = e2ePythonImage; openshell_supervisor_image = "../../artifacts/images/openshell-supervisor-tmachine.tar"; openshell_sandbox_image = "../../artifacts/images/openshell-sandbox-tmachine.tar"; }; @@ -216,6 +220,7 @@ let ]; inputs = { openshell_deb = "../../artifacts/packages/openshell.deb"; + openshell_e2e_python_image = e2ePythonImage; openshell_supervisor_image = "../../artifacts/images/openshell-supervisor-tmachine.tar"; openshell_sandbox_image = "../../artifacts/images/openshell-sandbox-tmachine.tar"; }; @@ -227,6 +232,7 @@ let inputs = { openshell_rpm = "../../artifacts/packages/rpm/openshell.rpm"; openshell_gateway_rpm = "../../artifacts/packages/rpm/openshell-gateway.rpm"; + openshell_e2e_python_image = e2ePythonImage; openshell_supervisor_image = "../../artifacts/images/openshell-supervisor-tmachine.tar"; openshell_sandbox_image = "../../artifacts/images/openshell-sandbox-tmachine.tar"; }; @@ -245,7 +251,7 @@ let playbooks = [ "ansible/playbooks/drivers/podman/e2e.yaml" ]; inputs = { openshell_podman_e2e_test_bundle = "../../artifacts/test-archives/${muslTarget}/openshell-podman-e2e-tests.tar"; - openshell_podman_e2e_workload_image = "../../artifacts/images/openshell-e2e-python-dev.tar"; + openshell_podman_e2e_workload_image = e2ePythonImage; }; } ];