Skip to content

Commit c96f174

Browse files
authored
test(tmachine): add interactive shell testsuite (#3522)
* test(tmachine): add interactive shell testsuite Signed-off-by: Evan Lezar <elezar@nvidia.com> * test(tmachine): add no-install profile Signed-off-by: Evan Lezar <elezar@nvidia.com> * docs(tmachine): document interactive shell usage Signed-off-by: Evan Lezar <elezar@nvidia.com> --------- Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent cb6e88a commit c96f174

5 files changed

Lines changed: 90 additions & 3 deletions

File tree

‎architecture/build.md‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,29 @@ revocation and the gateway's reauthorization-required recovery state.
266266
Tmachine environments define the guest machine and runtime setup, while named
267267
installers define how OpenShell is installed. This keeps the runtime mode
268268
independent from binary or package installation and lets multiple installers
269-
reuse the same prepared setup disk. The test command is
270-
`tmachine test <environment> <installer> <testsuite>`.
269+
reuse the same prepared setup disk. The `none` installer skips OpenShell
270+
installation and boots the prepared environment directly.
271+
272+
### Interactive tmachine shell
273+
274+
The test command is `tmachine test <environment> <installer> <testsuite>`. The
275+
`shell` testsuite prepares the selected environment and installer, then opens an
276+
interactive SSH session in the disposable guest for manual debugging.
277+
278+
Start an Ubuntu Docker guest without installing OpenShell:
279+
280+
```shell
281+
nix run .#tmachine -- test ubuntu-docker-rootful none shell
282+
```
283+
284+
Replace `none` with `deb` to install the locally staged Debian package before
285+
opening the shell:
286+
287+
```shell
288+
nix run .#tmachine -- test ubuntu-docker-rootful deb shell
289+
```
290+
291+
Exit the SSH session to shut down and discard the disposable guest.
271292

272293
The `tests/tmachine` setup and install caches include a digest of the
273294
entire directory containing `ANSIBLE_CONFIG`, including local roles, task

‎tests/ansible/playbooks/shell.yaml‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
---
5+
- name: Prepare an interactive tmachine shell
6+
hosts: all
7+
gather_facts: false
8+
tasks:
9+
- name: Wait for SSH
10+
ansible.builtin.wait_for_connection:
11+
12+
- name: Set the tmachine SSH password
13+
become: true
14+
ansible.builtin.command:
15+
argv: [chpasswd]
16+
stdin: tmachine:tmachine
17+
no_log: true
18+
changed_when: false

‎tests/config.nix‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ let
7070
];
7171

7272
installers = [
73+
{
74+
name = "none";
75+
use_galaxy = false;
76+
playbooks = [ ];
77+
inputs = { };
78+
}
7379
{
7480
name = "binaries";
7581
use_galaxy = false;
@@ -99,6 +105,12 @@ let
99105
];
100106

101107
testsuites = [
108+
{
109+
name = "shell";
110+
playbooks = [ "ansible/playbooks/shell.yaml" ];
111+
inputs = { };
112+
interactive = true;
113+
}
102114
{
103115
name = "conformance";
104116
playbooks = [ "ansible/playbooks/conformance/cli.yaml" ];

‎tests/tmachine/src/config.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub struct Testsuite {
4747
pub name: String,
4848
pub playbooks: Vec<PathBuf>,
4949
pub inputs: BTreeMap<String, PathBuf>,
50+
#[serde(default)]
51+
pub interactive: bool,
5052
}
5153

5254
impl Config {

‎tests/tmachine/src/qemu/test.rs‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
use std::process::Stdio;
5+
6+
use anyhow::{Context, Result};
47
use tempfile::tempdir;
8+
use tokio::process::Command;
59

610
use crate::config::{Environment, Installer, Machine, Testsuite};
7-
use anyhow::Result;
811

912
use super::img::QemuImage;
1013
use super::install::install;
@@ -25,6 +28,37 @@ pub async fn test(
2528

2629
run_playbooks(&testsuite.playbooks, &testsuite.inputs).await?;
2730

31+
if testsuite.interactive {
32+
println!("Opening an SSH shell in the tmachine VM.");
33+
let ssh_status = Command::new("sshpass")
34+
.env("SSHPASS", "tmachine")
35+
.args([
36+
"-e",
37+
"ssh",
38+
"-tt",
39+
"-p",
40+
"2222",
41+
"-o",
42+
"StrictHostKeyChecking=no",
43+
"-o",
44+
"UserKnownHostsFile=/dev/null",
45+
"-o",
46+
"LogLevel=ERROR",
47+
"tmachine@127.0.0.1",
48+
])
49+
.stdin(Stdio::inherit())
50+
.stdout(Stdio::inherit())
51+
.stderr(Stdio::inherit())
52+
.status()
53+
.await
54+
.context("open SSH shell in tmachine VM")?;
55+
56+
vm.shutdown().await;
57+
vm.wait().await;
58+
anyhow::ensure!(ssh_status.success(), "SSH shell exited with {ssh_status}");
59+
return Ok(());
60+
}
61+
2862
vm.shutdown().await;
2963
vm.wait().await;
3064
Ok(())

0 commit comments

Comments
 (0)