From 1cbfcbe84b83232e5612e6c95d1a7d1b5a355e24 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Mon, 21 Sep 2026 20:15:37 +0200 Subject: [PATCH 1/3] test(tmachine): add interactive shell testsuite Signed-off-by: Evan Lezar --- architecture/build.md | 4 +++- tests/ansible/playbooks/shell.yaml | 18 +++++++++++++++ tests/config.nix | 6 +++++ tests/tmachine/src/config.rs | 2 ++ tests/tmachine/src/qemu/test.rs | 36 +++++++++++++++++++++++++++++- 5 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 tests/ansible/playbooks/shell.yaml diff --git a/architecture/build.md b/architecture/build.md index b6c464bbe5..d3ae398e11 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -267,7 +267,9 @@ Tmachine environments define the guest machine and runtime setup, while named installers define how OpenShell is installed. This keeps the runtime mode independent from binary or package installation and lets multiple installers reuse the same prepared setup disk. The test command is -`tmachine test `. +`tmachine test `. The `shell` testsuite +prepares the selected environment and installer, then opens an interactive SSH +session in the disposable guest for manual debugging. The `tests/tmachine` setup and install caches include a digest of the entire directory containing `ANSIBLE_CONFIG`, including local roles, task diff --git a/tests/ansible/playbooks/shell.yaml b/tests/ansible/playbooks/shell.yaml new file mode 100644 index 0000000000..cdacda8bba --- /dev/null +++ b/tests/ansible/playbooks/shell.yaml @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +--- +- name: Prepare an interactive tmachine shell + hosts: all + gather_facts: false + tasks: + - name: Wait for SSH + ansible.builtin.wait_for_connection: + + - name: Set the tmachine SSH password + become: true + ansible.builtin.command: + argv: [chpasswd] + stdin: tmachine:tmachine + no_log: true + changed_when: false diff --git a/tests/config.nix b/tests/config.nix index b542af6614..205bad83f2 100644 --- a/tests/config.nix +++ b/tests/config.nix @@ -99,6 +99,12 @@ let ]; testsuites = [ + { + name = "shell"; + playbooks = [ "ansible/playbooks/shell.yaml" ]; + inputs = { }; + interactive = true; + } { name = "conformance"; playbooks = [ "ansible/playbooks/conformance/cli.yaml" ]; diff --git a/tests/tmachine/src/config.rs b/tests/tmachine/src/config.rs index f9c1657b20..bc19cbbb1f 100644 --- a/tests/tmachine/src/config.rs +++ b/tests/tmachine/src/config.rs @@ -47,6 +47,8 @@ pub struct Testsuite { pub name: String, pub playbooks: Vec, pub inputs: BTreeMap, + #[serde(default)] + pub interactive: bool, } impl Config { diff --git a/tests/tmachine/src/qemu/test.rs b/tests/tmachine/src/qemu/test.rs index c103477ee0..ac8a8bdfc0 100644 --- a/tests/tmachine/src/qemu/test.rs +++ b/tests/tmachine/src/qemu/test.rs @@ -1,10 +1,13 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +use std::process::Stdio; + +use anyhow::{Context, Result}; use tempfile::tempdir; +use tokio::process::Command; use crate::config::{Environment, Installer, Machine, Testsuite}; -use anyhow::Result; use super::img::QemuImage; use super::install::install; @@ -25,6 +28,37 @@ pub async fn test( run_playbooks(&testsuite.playbooks, &testsuite.inputs).await?; + if testsuite.interactive { + println!("Opening an SSH shell in the tmachine VM."); + let ssh_status = Command::new("sshpass") + .env("SSHPASS", "tmachine") + .args([ + "-e", + "ssh", + "-tt", + "-p", + "2222", + "-o", + "StrictHostKeyChecking=no", + "-o", + "UserKnownHostsFile=/dev/null", + "-o", + "LogLevel=ERROR", + "tmachine@127.0.0.1", + ]) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .await + .context("open SSH shell in tmachine VM")?; + + vm.shutdown().await; + vm.wait().await; + anyhow::ensure!(ssh_status.success(), "SSH shell exited with {ssh_status}"); + return Ok(()); + } + vm.shutdown().await; vm.wait().await; Ok(()) From a7516a70786f22d1842a0b21912835c9bd18e901 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Mon, 21 Sep 2026 20:29:00 +0200 Subject: [PATCH 2/3] test(tmachine): add no-install profile Signed-off-by: Evan Lezar --- architecture/build.md | 15 +++++++++++---- tests/config.nix | 6 ++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/architecture/build.md b/architecture/build.md index d3ae398e11..1b5e81b6d1 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -266,10 +266,17 @@ revocation and the gateway's reauthorization-required recovery state. Tmachine environments define the guest machine and runtime setup, while named installers define how OpenShell is installed. This keeps the runtime mode independent from binary or package installation and lets multiple installers -reuse the same prepared setup disk. The test command is -`tmachine test `. The `shell` testsuite -prepares the selected environment and installer, then opens an interactive SSH -session in the disposable guest for manual debugging. +reuse the same prepared setup disk. The `none` installer skips OpenShell +installation and boots the prepared environment directly. + +The test command is `tmachine test `. The +`shell` testsuite prepares the selected environment and installer, then opens an +interactive SSH session in the disposable guest for manual debugging. For +example, start an Ubuntu Docker guest without installing OpenShell: + +```shell +nix run .#tmachine -- test ubuntu-docker-rootful none shell +``` The `tests/tmachine` setup and install caches include a digest of the entire directory containing `ANSIBLE_CONFIG`, including local roles, task diff --git a/tests/config.nix b/tests/config.nix index 205bad83f2..c8d80e6ebe 100644 --- a/tests/config.nix +++ b/tests/config.nix @@ -70,6 +70,12 @@ let ]; installers = [ + { + name = "none"; + use_galaxy = false; + playbooks = [ ]; + inputs = { }; + } { name = "binaries"; use_galaxy = false; From b036732da3553f5908360546e27dca33c0982c53 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Mon, 21 Sep 2026 20:36:04 +0200 Subject: [PATCH 3/3] docs(tmachine): document interactive shell usage Signed-off-by: Evan Lezar --- architecture/build.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/architecture/build.md b/architecture/build.md index 1b5e81b6d1..402ce78ab6 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -269,15 +269,27 @@ independent from binary or package installation and lets multiple installers reuse the same prepared setup disk. The `none` installer skips OpenShell installation and boots the prepared environment directly. +### Interactive tmachine shell + The test command is `tmachine test `. The `shell` testsuite prepares the selected environment and installer, then opens an -interactive SSH session in the disposable guest for manual debugging. For -example, start an Ubuntu Docker guest without installing OpenShell: +interactive SSH session in the disposable guest for manual debugging. + +Start an Ubuntu Docker guest without installing OpenShell: ```shell nix run .#tmachine -- test ubuntu-docker-rootful none shell ``` +Replace `none` with `deb` to install the locally staged Debian package before +opening the shell: + +```shell +nix run .#tmachine -- test ubuntu-docker-rootful deb shell +``` + +Exit the SSH session to shut down and discard the disposable guest. + The `tests/tmachine` setup and install caches include a digest of the entire directory containing `ANSIBLE_CONFIG`, including local roles, task includes, templates, inventory, and requirements. The digest uses sorted