diff --git a/architecture/build.md b/architecture/build.md index b6c464bbe5..402ce78ab6 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -266,8 +266,29 @@ 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 `. +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. + +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 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..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; @@ -99,6 +105,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(())