Skip to content

Commit 9b069aa

Browse files
committed
fix(e2e): apply stable-placeholder supervisor image override
Use ManagedGateway's per-process supervisor image override so the fixture CA reaches the supervisor when runtime image environment variables take precedence over gateway TOML. Restore the wrapper's original image after each test, following the provider-readiness fixture. Signed-off-by: Shiju <shiju@nvidia.com>
1 parent 90916b9 commit 9b069aa

1 file changed

Lines changed: 28 additions & 96 deletions

File tree

‎e2e/rust/tests/stable_external_placeholders.rs‎

Lines changed: 28 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -247,16 +247,14 @@ impl FixtureImage {
247247

248248
// These tests run serially against a wrapper-owned gateway. The public fixture
249249
// CA must be removed and the original supervisor restored before the next case.
250-
struct GatewayTrustConfig {
251-
path: PathBuf,
252-
original: String,
253-
image_range: std::ops::Range<usize>,
250+
struct GatewayTrustFixture {
251+
directory: PathBuf,
254252
supervisor_image: String,
255253
health_port: u16,
256254
restore_required: bool,
257255
}
258256

259-
impl GatewayTrustConfig {
257+
impl GatewayTrustFixture {
260258
fn load() -> Result<Self, String> {
261259
if std::env::var_os("OPENSHELL_GATEWAY_ENDPOINT").is_some()
262260
|| std::env::var_os("OPENSHELL_E2E_GATEWAY_BIN").is_none()
@@ -268,6 +266,10 @@ impl GatewayTrustConfig {
268266
}
269267
let args_file = std::env::var_os("OPENSHELL_E2E_GATEWAY_ARGS_FILE")
270268
.ok_or("managed gateway argument metadata is missing")?;
269+
let directory = Path::new(&args_file)
270+
.parent()
271+
.ok_or("managed gateway arguments have no parent directory")?
272+
.to_path_buf();
271273
let raw =
272274
std::fs::read(args_file).map_err(|_| "could not read managed gateway arguments")?;
273275
let args = raw
@@ -284,112 +286,46 @@ impl GatewayTrustConfig {
284286
}
285287
Ok(value)
286288
};
287-
let path = PathBuf::from(argument("--config")?);
288289
let health_port = argument("--health-port")?
289290
.parse::<u16>()
290291
.map_err(|_| "managed gateway health port is invalid")?;
291-
let original = std::fs::read_to_string(&path)
292-
.map_err(|_| "could not read managed gateway configuration")?;
293-
let (image_range, supervisor_image) = docker_supervisor_image(&original)?;
292+
let supervisor_image = std::env::var("OPENSHELL_SUPERVISOR_IMAGE")
293+
.map_err(|_| "managed supervisor image is missing")?;
294294
Ok(Self {
295-
path,
296-
original,
297-
image_range,
295+
directory,
298296
supervisor_image,
299297
health_port,
300298
restore_required: false,
301299
})
302300
}
303301

304302
async fn apply(&mut self, image: &str) -> Result<(), String> {
305-
let mut updated = self.original.clone();
306-
updated.replace_range(self.image_range.clone(), image);
307-
// Set the guard before the write: a failed write or restart must still
308-
// flow through explicit restoration of the exact original bytes.
309303
self.restore_required = true;
310-
std::fs::write(&self.path, updated)
311-
.map_err(|_| "could not install fixture supervisor configuration")?;
312-
restart_fixture_gateway(self.health_port).await
304+
restart_fixture_gateway(self.health_port, Some(image)).await
313305
}
314306

315307
async fn restore(&mut self) -> Result<(), String> {
316308
if !self.restore_required {
317309
return Ok(());
318310
}
319-
std::fs::write(&self.path, &self.original)
320-
.map_err(|_| "could not restore original gateway configuration")?;
321-
restart_fixture_gateway(self.health_port)
311+
restart_fixture_gateway(self.health_port, None)
322312
.await
323-
.map_err(|_| "original gateway configuration was restored but restart failed")?;
313+
.map_err(|_| "could not restore original gateway runtime")?;
324314
self.restore_required = false;
325315
Ok(())
326316
}
327317
}
328318

329-
impl Drop for GatewayTrustConfig {
330-
fn drop(&mut self) {
331-
if self.restore_required {
332-
// Cancellation/panic fallback restores disk state only. Normal
333-
// Result paths explicitly restart and verify health; Drop never
334-
// launches a subprocess or hides a failed restart as success.
335-
let _ = std::fs::write(&self.path, &self.original);
336-
}
337-
}
338-
}
339-
340-
fn docker_supervisor_image(config: &str) -> Result<(std::ops::Range<usize>, String), String> {
341-
let mut in_docker = false;
342-
let mut offset = 0;
343-
let mut found = None;
344-
for line in config.split_inclusive('\n') {
345-
let trimmed = line.trim();
346-
if trimmed.starts_with('[') {
347-
in_docker = trimmed == "[openshell.drivers.docker]";
348-
} else if in_docker && let Some((key, value)) = trimmed.split_once('=') {
349-
if key.trim() == "socket_path" {
350-
return Err(
351-
"fixture cannot replace an external Docker driver configuration".to_string(),
352-
);
353-
}
354-
if key.trim() == "supervisor_image" {
355-
// Accept only the wrapper's single-line quoted OCI reference.
356-
// Reject escapes/comments instead of treating general TOML as
357-
// text and accidentally changing a different configuration key.
358-
let image = value
359-
.trim()
360-
.strip_prefix('"')
361-
.and_then(|value| value.strip_suffix('"'))
362-
.filter(|image| {
363-
!image.is_empty()
364-
&& image.bytes().all(|byte| {
365-
byte.is_ascii_alphanumeric() || b"/:@._-".contains(&byte)
366-
})
367-
})
368-
.ok_or("managed supervisor image is not a simple quoted OCI reference")?;
369-
let start = offset
370-
+ line
371-
.find('"')
372-
.ok_or("managed supervisor image is not quoted")?
373-
+ 1;
374-
if found
375-
.replace((start..start + image.len(), image.to_string()))
376-
.is_some()
377-
{
378-
return Err("managed Docker supervisor image is duplicated".to_string());
379-
}
380-
}
381-
}
382-
offset += line.len();
383-
}
384-
found.ok_or_else(|| "managed Docker supervisor image is missing".to_string())
385-
}
386-
387-
async fn restart_fixture_gateway(health_port: u16) -> Result<(), String> {
388-
let gateway = ManagedGateway::from_env()
319+
async fn restart_fixture_gateway(
320+
health_port: u16,
321+
supervisor_image: Option<&str>,
322+
) -> Result<(), String> {
323+
let mut gateway = ManagedGateway::from_env()
389324
.map_err(|_| "could not load managed gateway restart metadata")?
390325
.ok_or("managed gateway restart metadata disappeared")?;
391-
// ManagedGateway bounds graceful shutdown before force-kill. Keep it local:
392-
// its Drop can start a stopped gateway, but never owns configuration restore.
326+
if let Some(image) = supervisor_image {
327+
gateway.set_supervisor_image(image);
328+
}
393329
gateway
394330
.stop()
395331
.map_err(|_| "could not stop fixture gateway")?;
@@ -1103,17 +1039,13 @@ async fn ordinary_external_placeholder_keeps_revoked_assertion_after_rotation()
11031039
// Keep each lifecycle together so no phase can replace the retained client.
11041040
#[allow(clippy::too_many_lines)]
11051041
async fn external_caller_assertion_rotation(stable: bool) -> Result<(), String> {
1106-
let mut gateway_config = GatewayTrustConfig::load()?;
1042+
let mut gateway_fixture = GatewayTrustFixture::load()?;
11071043
let name = format!("e2e{:016x}", rand::random::<u64>());
11081044
let mut backend = BackendPair::new(&name)?;
11091045
// The wrapper's directory is shared with the host Docker daemon in CI;
11101046
// a job-container-local temporary path cannot back the TLS bind mount.
1111-
let fixture_parent = gateway_config
1112-
.path
1113-
.parent()
1114-
.ok_or("managed gateway configuration has no parent directory")?;
1115-
let directory =
1116-
TempDir::new_in(fixture_parent).map_err(|_| "could not allocate fixture directory")?;
1047+
let directory = TempDir::new_in(&gateway_fixture.directory)
1048+
.map_err(|_| "could not allocate fixture directory")?;
11171049
let context = directory.path().join("image");
11181050
std::fs::create_dir(&context).map_err(|_| "could not allocate public image context")?;
11191051
let backend_tls = directory.path().join("backend-tls");
@@ -1141,8 +1073,8 @@ async fn external_caller_assertion_rotation(stable: bool) -> Result<(), String>
11411073
// default user and its original public trust roots.
11421074
std::fs::write(&supervisor_dockerfile, format!(
11431075
"FROM {} AS supervisor\nFROM {base} AS trust-bundle\nUSER 0\nCOPY --from=supervisor /etc/ssl/certs/ca-certificates.crt /tmp/ca-certificates.crt\nCOPY fixture-ca.crt /tmp/stable-fixture-ca.crt\nRUN [\"/usr/bin/python3\", \"-c\", \"from pathlib import Path; bundle = Path('/tmp/ca-certificates.crt'); bundle.write_bytes(bundle.read_bytes() + Path('/tmp/stable-fixture-ca.crt').read_bytes())\"]\nFROM {}\nCOPY --from=trust-bundle /tmp/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt\n",
1144-
gateway_config.supervisor_image,
1145-
gateway_config.supervisor_image
1076+
gateway_fixture.supervisor_image,
1077+
gateway_fixture.supervisor_image
11461078
)).map_err(|_| "could not write fixture supervisor Dockerfile")?;
11471079
let image = FixtureImage::new()?;
11481080
let supervisor_image = FixtureImage::new()?;
@@ -1191,7 +1123,7 @@ async fn external_caller_assertion_rotation(stable: bool) -> Result<(), String>
11911123
supervisor_image
11921124
.build(&supervisor_dockerfile, &context)
11931125
.await?;
1194-
gateway_config.apply(supervisor_image.tag()).await?;
1126+
gateway_fixture.apply(supervisor_image.tag()).await?;
11951127
write_profile(&profile, &name, &host, port, python, stable)?;
11961128
write_policy(&policy, &host, &other_host, port, other_port, python)?;
11971129
let configuration = json!({"host": host, "other_host": other_host, "port": port,
@@ -1397,7 +1329,7 @@ async fn external_caller_assertion_rotation(stable: bool) -> Result<(), String>
13971329
// Restore the original runtime before removing its replacement. Retain the
13981330
// derived supervisor image if restoration fails, and report that failure
13991331
// even when a lifecycle assertion already failed.
1400-
let gateway_restore = gateway_config.restore().await;
1332+
let gateway_restore = gateway_fixture.restore().await;
14011333
let supervisor_cleanup = if gateway_restore.is_ok() {
14021334
supervisor_image.remove().await
14031335
} else {

0 commit comments

Comments
 (0)