Skip to content

Commit 531138c

Browse files
refactor(test-runner): provide Keycloak from a tmachine environment
Declare Keycloak as part of a tmachine environment instead of a service the runner starts after boot. ubuntu-docker-rootful-keycloak's setup runs Keycloak with a restart policy and writes its OPENSHELL_TEST_* variables, so the image is cached with the disk. The runner no longer knows about services: it passes every /etc/openshell-test/env.d/*.env variable to the tests. Environment setup can now take inputs, for the realm file, and the Keycloak fixture waits for the realm to answer after boot. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
1 parent 3a75dcb commit 531138c

14 files changed

Lines changed: 241 additions & 235 deletions

File tree

‎tests/environments/docker.yaml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ environments:
2424
tmachine: { environment: ubuntu-docker-rootful, installer: deb }
2525
gateway: openshell
2626

27-
# The same VM with Keycloak started after boot, for `features` suites that
28-
# need an OIDC issuer. `services` adds `+<service>` to the ID; see
29-
# tests/runner/src/services.rs.
27+
# Ubuntu with Docker and Keycloak, for `features` suites that need an OIDC
28+
# issuer. The tmachine environment runs Keycloak and publishes how to reach
29+
# it; `services` only names it in the ID.
3030
- id: tmachine.ubuntu2404-amd64.docker-rootful.deb+keycloak
3131
backend: tmachine
3232
machine: { os: ubuntu, version: "24.04", arch: amd64 }
3333
runtime: docker-rootful
3434
installer: deb
35-
tmachine: { environment: ubuntu-docker-rootful, installer: deb }
35+
tmachine: { environment: ubuntu-docker-rootful-keycloak, installer: deb }
3636
gateway: openshell
3737
services: [keycloak]

‎tests/plans/README.md‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,24 @@ replaced by `-`: `tests/file_transfer.rs` is the suite `file-transfer`.
1919

2020
| Plan | Runs |
2121
|---|---|
22-
| `cpu-tests.yaml` | The `conformance` family on Ubuntu 24.04 amd64 with rootful Docker and the Debian package, and the `features` suites that need Keycloak on the same VM with Keycloak started |
22+
| `cpu-tests.yaml` | The `conformance` family on Ubuntu 24.04 amd64 with rootful Docker and the Debian package, and the `features` suites that need Keycloak on the same setup with Keycloak |
2323

2424
## Environment services
2525

2626
A suite that needs something besides the gateway, such as an OIDC issuer, runs
27-
in an environment that lists it under `services`. The environment ID ends in
28-
`+<service>` for each one, e.g. `tmachine.ubuntu2404-amd64.docker-rootful.deb+keycloak`.
29-
30-
After the VM boots, the runner applies
31-
`tests/tmachine/ansible/playbooks/services/<service>.yaml`. The playbook starts
32-
the service and writes `OPENSHELL_TEST_*` lines to
33-
`/etc/openshell-test/env.d/<service>.env`; the runner passes them to the tests.
34-
A suite-local fixture reads them, as `tests/suites/features/keycloak` does. To
27+
in an environment that provides it. tmachine declares that environment: its
28+
setup installs and starts the service (with a restart policy, since setup is
29+
cached as a disk image) and writes how to reach it as `OPENSHELL_TEST_*` lines
30+
in `/etc/openshell-test/env.d/<service>.env`. See the
31+
`ubuntu-docker-rootful-keycloak` environment in `tests/tmachine/config.nix`
32+
and `tests/tmachine/ansible/playbooks/keycloak.yaml`.
33+
34+
The runner passes every variable in those files to the tests, and a
35+
suite-local fixture reads them, as `tests/suites/features/keycloak` does. To
3536
run such a suite against your own deployment, set the same variables yourself.
3637

37-
To add a service, add it to `Service` in `tests/runner/src/services.rs`, write
38-
its playbook, and add an environment that lists it.
38+
In `tests/environments/`, list the service under `services`; the environment
39+
ID then ends in `+<service>`, e.g. `tmachine.ubuntu2404-amd64.docker-rootful.deb+keycloak`.
3940

4041
## How a plan runs on CI
4142

‎tests/runner/src/environments.rs‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ use std::path::Path;
1717
use anyhow::{Context, Result, bail, ensure};
1818
use serde::Deserialize;
1919

20-
use crate::services::Service;
21-
2220
/// All environments, keyed by ID.
2321
#[derive(Debug, Default)]
2422
pub struct Catalog {
@@ -47,10 +45,11 @@ pub struct Environment {
4745
/// use the CLI's active gateway.
4846
#[serde(default)]
4947
pub gateway: Option<String>,
50-
/// Services the runner starts after boot, such as Keycloak. See
51-
/// [`crate::services`].
48+
/// What the tmachine environment runs besides the runtime, such as
49+
/// `keycloak`. Each adds `+<service>` to the ID, so plans can pick an
50+
/// environment that has it. The environment, not the runner, starts them.
5251
#[serde(default)]
53-
pub services: Vec<Service>,
52+
pub services: Vec<String>,
5453
}
5554

5655
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
@@ -126,7 +125,7 @@ impl Environment {
126125
);
127126
for service in &self.services {
128127
id.push('+');
129-
id.push_str(&service.to_string());
128+
id.push_str(service);
130129
}
131130
id
132131
}
@@ -262,7 +261,7 @@ environments:
262261
let env = environments
263262
.get("tmachine.ubuntu2404-amd64.docker-rootful.deb+keycloak")
264263
.unwrap();
265-
assert_eq!(env.services, [Service::Keycloak]);
264+
assert_eq!(env.services, ["keycloak"]);
266265

267266
let without_suffix = yaml.replace("deb+keycloak\n", "deb\n");
268267
let err = catalog(&without_suffix).unwrap_err();

‎tests/runner/src/execute.rs‎

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
//!
77
//! Tests find the gateway the way the CLI does, so the runner points them at
88
//! the one the installer registered with `OPENSHELL_GATEWAY` and at the
9-
//! installed CLI with `OPENSHELL_BIN`. There is no target file.
9+
//! installed CLI with `OPENSHELL_BIN`. There is no target file. Anything else
10+
//! the environment provides, such as Keycloak, the tests learn from the
11+
//! variables it publishes (see [`crate::test_env`]).
1012
//!
1113
//! Adapted from the executor in Evan Lezar's NVIDIA/OpenShell#4629.
1214
@@ -22,7 +24,7 @@ use tmachine::config::Config;
2224
use crate::bundle::{Bundle, PROFILE, READY_BINARY};
2325
use crate::jobs::{FamilyJob, Job};
2426
use crate::report::{Outcome, Status, annotate_junit, verify_junit};
25-
use crate::services;
27+
use crate::test_env;
2628

2729
const GUEST_ROOT: &str = "/var/tmp/openshell-tests";
2830
/// How long the gateway has to become healthy after the environment is up.
@@ -81,32 +83,6 @@ pub async fn run(job: &Job, options: &Options<'_>) -> Vec<Outcome> {
8183
}
8284
};
8385

84-
let service_env = if job.environment.services.is_empty() {
85-
Ok(Vec::new())
86-
} else {
87-
eprintln!("==> {env_id}: starting services");
88-
services::start(&session, &job.environment.services).await
89-
};
90-
let service_env = match service_env {
91-
Ok(vars) => vars,
92-
Err(error) => {
93-
let message = format!("start services: {error:#}");
94-
eprintln!("==> {env_id}: {message}");
95-
if let Err(error) = session
96-
.collect_diagnostics(&options.results.join(env_id).join("diagnostics"))
97-
.await
98-
{
99-
eprintln!("==> {env_id}: collecting diagnostics failed: {error:#}");
100-
}
101-
session.down().await;
102-
return job
103-
.families
104-
.iter()
105-
.map(|family| outcome(family, Status::Error(message.clone())))
106-
.collect();
107-
}
108-
};
109-
11086
let mut outcomes = Vec::new();
11187
for family in &job.families {
11288
if !family.has_tests() {
@@ -119,7 +95,7 @@ pub async fn run(job: &Job, options: &Options<'_>) -> Vec<Outcome> {
11995
family.family,
12096
family.filterset()
12197
);
122-
let status = match run_family(&session, job, family, &service_env, options, &dir).await {
98+
let status = match run_family(&session, job, family, options, &dir).await {
12399
Ok(status) => status,
124100
Err(error) => Status::Error(format!("{error:#}")),
125101
};
@@ -176,14 +152,14 @@ async fn provision(job: &Job, config: &Config) -> Result<Session> {
176152

177153
/// The environment variables that point the tests at the system under test:
178154
/// the registered gateway (when the environment names one), the installed
179-
/// CLI, and whatever the environment's services published.
180-
pub fn guest_env(job: &Job, openshell_bin: &str, service_env: &[String]) -> Vec<String> {
155+
/// CLI, and what the environment publishes for tests (see [`test_env`]).
156+
pub fn guest_env(job: &Job, openshell_bin: &str, test_env: &[String]) -> Vec<String> {
181157
let mut vars = Vec::new();
182158
if let Some(gateway) = &job.environment.gateway {
183159
vars.push(format!("OPENSHELL_GATEWAY={gateway}"));
184160
}
185161
vars.push(format!("OPENSHELL_BIN={openshell_bin}"));
186-
vars.extend(service_env.iter().cloned());
162+
vars.extend(test_env.iter().cloned());
187163
vars
188164
}
189165

@@ -236,7 +212,6 @@ async fn run_family(
236212
session: &Session,
237213
job: &Job,
238214
family: &FamilyJob,
239-
service_env: &[String],
240215
options: &Options<'_>,
241216
dir: &Path,
242217
) -> Result<Status> {
@@ -258,7 +233,10 @@ async fn run_family(
258233
!openshell_bin.is_empty(),
259234
"the installer did not put openshell on the guest's PATH"
260235
);
261-
let env = guest_env(job, openshell_bin, service_env);
236+
let test_env = test_env::read(session)
237+
.await
238+
.context("read the variables the environment publishes for tests")?;
239+
let env = guest_env(job, openshell_bin, &test_env);
262240

263241
let guest = format!("{GUEST_ROOT}/{}", family.family);
264242
checked(

‎tests/runner/src/lib.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ pub mod github;
1313
pub mod jobs;
1414
pub mod plan;
1515
pub mod report;
16-
pub mod services;
16+
pub mod test_env;

‎tests/runner/src/services.rs‎

Lines changed: 0 additions & 158 deletions
This file was deleted.

0 commit comments

Comments
 (0)