Skip to content

Commit 4b210c6

Browse files
committed
feat(cli): add openshell conformance subcommand skeleton
Adds a hidden `openshell conformance` subcommand behind a `conformance` Cargo feature flag. The subcommand connects to a registered gateway and runs a suite of driver-agnostic conformance scenarios, reporting pass/fail per scenario and exiting non-zero on any failure. Implements the `lifecycle` scenario (create → ready → delete) as the first concrete scenario. The remaining five scenarios are stubbed as "not yet implemented" placeholders. Sandbox names follow the `conformance-<scenario>-<run-id>` convention (Kubernetes RFC 1123 safe) to make orphaned sandboxes identifiable. Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent a727116 commit 4b210c6

3 files changed

Lines changed: 385 additions & 0 deletions

File tree

crates/openshell-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ workspace = true
8686

8787
[features]
8888
bundled-z3 = ["openshell-prover/bundled-z3"]
89+
conformance = []
8990

9091
[dev-dependencies]
9192
futures = { workspace = true }

crates/openshell-cli/src/main.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,27 @@ enum Commands {
554554
command: Option<DoctorCommands>,
555555
},
556556

557+
/// Validate that a gateway and its driver are functioning correctly.
558+
///
559+
/// Runs a suite of conformance scenarios against the configured gateway
560+
/// and reports pass/fail for each. Exits non-zero if any scenario fails.
561+
///
562+
/// All sandboxes created by this command use the naming convention
563+
/// `conformance-<scenario>-<run-id>` and are cleaned up on completion.
564+
/// If the command is interrupted, any remaining sandboxes can be
565+
/// identified by the `conformance-` prefix.
566+
///
567+
/// Examples:
568+
/// openshell conformance run
569+
/// openshell conformance run --filter lifecycle
570+
/// openshell conformance run --timeout 120
571+
#[cfg(feature = "conformance")]
572+
#[command(hide = true, help_template = SUBCOMMAND_HELP_TEMPLATE)]
573+
Conformance {
574+
#[command(subcommand)]
575+
command: ConformanceCommands,
576+
},
577+
557578
// ===================================================================
558579
// ADDITIONAL COMMANDS
559580
// ===================================================================
@@ -1186,6 +1207,47 @@ enum InferenceCommands {
11861207
},
11871208
}
11881209

1210+
// -----------------------------------------------------------------------
1211+
// Conformance commands
1212+
// -----------------------------------------------------------------------
1213+
1214+
#[cfg(feature = "conformance")]
1215+
#[derive(Subcommand, Debug)]
1216+
enum ConformanceCommands {
1217+
/// Run the conformance suite against the configured gateway.
1218+
///
1219+
/// Connects to the gateway using the registered credentials and runs
1220+
/// each conformance scenario in sequence. Reports pass/fail per
1221+
/// scenario and exits non-zero if any scenario fails.
1222+
///
1223+
/// Examples:
1224+
/// openshell conformance run
1225+
/// openshell conformance run --filter lifecycle
1226+
/// openshell conformance run --timeout 120
1227+
#[command(help_template = LEAF_HELP_TEMPLATE, next_help_heading = "FLAGS")]
1228+
Run {
1229+
/// Only run scenarios whose name contains this substring.
1230+
#[arg(long, short = 'f')]
1231+
filter: Option<String>,
1232+
1233+
/// Seconds to wait for each scenario to complete.
1234+
#[arg(long, default_value = "300")]
1235+
timeout: u64,
1236+
1237+
/// Output format.
1238+
#[arg(short = 'o', long = "output", value_enum, default_value_t = OutputFormat::Table)]
1239+
output: OutputFormat,
1240+
},
1241+
1242+
/// List available conformance scenarios without running them.
1243+
#[command(help_template = LEAF_HELP_TEMPLATE, next_help_heading = "FLAGS")]
1244+
List {
1245+
/// Output format.
1246+
#[arg(short = 'o', long = "output", value_enum, default_value_t = OutputFormat::Table)]
1247+
output: OutputFormat,
1248+
},
1249+
}
1250+
11891251
// -----------------------------------------------------------------------
11901252
// Doctor (diagnostic) commands
11911253
// -----------------------------------------------------------------------
@@ -2062,6 +2124,35 @@ async fn main() -> Result<()> {
20622124
}
20632125
},
20642126

2127+
// -----------------------------------------------------------
2128+
// Conformance commands
2129+
// -----------------------------------------------------------
2130+
#[cfg(feature = "conformance")]
2131+
Some(Commands::Conformance { command }) => {
2132+
let ctx = resolve_gateway(&cli.gateway, &cli.gateway_endpoint)?;
2133+
let mut tls = tls.with_gateway_name(&ctx.name);
2134+
apply_auth(&mut tls, &ctx.name);
2135+
match command {
2136+
ConformanceCommands::Run {
2137+
filter,
2138+
timeout,
2139+
output,
2140+
} => {
2141+
run::conformance_run(
2142+
&ctx.endpoint,
2143+
&tls,
2144+
filter.as_deref(),
2145+
timeout,
2146+
output.as_str(),
2147+
)
2148+
.await?;
2149+
}
2150+
ConformanceCommands::List { output } => {
2151+
run::conformance_list(output.as_str())?;
2152+
}
2153+
}
2154+
}
2155+
20652156
// -----------------------------------------------------------
20662157
// Doctor (diagnostic) commands
20672158
// -----------------------------------------------------------

0 commit comments

Comments
 (0)