Skip to content

Commit 33f5e2d

Browse files
test(oci): require WorkspaceValidationFailed and cover image USER fallback
The unwritable WORKDIR scenario now requires the WorkspaceValidationFailed reason, so unrelated provisioning failures cannot pass it. The numeric USER scenario supplies a policy without a process section, so the image USER is the only source of the sandbox identity. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
1 parent aa74c33 commit 33f5e2d

1 file changed

Lines changed: 45 additions & 9 deletions

File tree

‎tests/suites/features/oci-image/tests/oci_image.rs‎

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,22 @@ const BASE_IMAGE: &str = "nvcr.io/nvidia/base/ubuntu:24.04";
2121
const ENGINE_ENV: &str = "OPENSHELL_TEST_CONTAINER_ENGINE";
2222
const CREATE_TIMEOUT: Duration = Duration::from_mins(10);
2323
const COMMAND_TIMEOUT: Duration = Duration::from_mins(2);
24+
/// Sandbox condition reason for an image `WORKDIR` the sandbox identity
25+
/// cannot use.
26+
const WORKSPACE_VALIDATION_FAILED: &str = "WorkspaceValidationFailed";
27+
/// A complete sandbox policy without a `process` section, so the sandbox
28+
/// identity falls back to the image `USER`.
29+
const IMAGE_IDENTITY_POLICY: &str = "version: 1
30+
31+
filesystem_policy:
32+
include_workdir: true
33+
read_only: [/usr, /lib, /lib64, /proc, /dev/urandom, /etc]
34+
read_write: [/sandbox, /tmp, /dev/null]
35+
landlock:
36+
compatibility: best_effort
37+
38+
network_policies: {}
39+
";
2440

2541
/// A named image user that owns its custom `WORKDIR`. Existing image content
2642
/// keeps its ownership.
@@ -42,14 +58,15 @@ USER app
4258
"{} test \"$(stat -c %u:%g .)\" = 1234:1235;",
4359
workspace_checks("1234:1235", "/workspace/project", true)
4460
);
45-
let sandbox = create_sandbox(runner, "named", "nu", &image, &checks).await?;
61+
let sandbox = create_sandbox(runner, "named", "nu", &image, None, &checks).await?;
4662
file_transfer_uses_workspace(runner, &sandbox).await
4763
})
4864
.await;
4965
}
5066

5167
/// A numeric image user without passwd entries can reach a custom `WORKDIR`
52-
/// whose parent directories are private to that user.
68+
/// whose parent directories are private to that user. The sandbox policy omits
69+
/// `process`, so the image `USER` is the only source of the sandbox identity.
5370
#[tokio::test]
5471
async fn custom_workdir_with_numeric_user_and_private_parents() {
5572
run("oci-image/custom-workdir-numeric-user", async |runner| {
@@ -66,8 +83,16 @@ USER 2345:2346
6683
"
6784
),
6885
)?;
86+
let policy_file = tempfile::NamedTempFile::new()
87+
.map_err(|error| format!("create policy file: {error}"))?;
88+
std::fs::write(policy_file.path(), IMAGE_IDENTITY_POLICY)
89+
.map_err(|error| format!("write policy file: {error}"))?;
90+
let policy = policy_file
91+
.path()
92+
.to_str()
93+
.ok_or("policy path is not UTF-8")?;
6994
let checks = workspace_checks("2345:2346", "/home/app/project", true);
70-
create_sandbox(runner, "numeric", "pu", &image, &checks)
95+
create_sandbox(runner, "numeric", "pu", &image, Some(policy), &checks)
7196
.await
7297
.map(drop)
7398
})
@@ -84,15 +109,16 @@ async fn default_workdir_uses_managed_workspace() {
84109
&format!("FROM {BASE_IMAGE}\nUSER 2345:2346\n"),
85110
)?;
86111
let checks = workspace_checks("2345:2346", "/sandbox", false);
87-
create_sandbox(runner, "default", "dw", &image, &checks)
112+
create_sandbox(runner, "default", "dw", &image, None, &checks)
88113
.await
89114
.map(drop)
90115
})
91116
.await;
92117
}
93118

94119
/// OpenShell rejects a custom `WORKDIR` that the image user cannot write
95-
/// instead of granting the user new access to it.
120+
/// instead of granting the user new access to it, and reports that reason
121+
/// rather than a generic startup failure.
96122
#[tokio::test]
97123
async fn unwritable_custom_workdir_is_rejected() {
98124
run("oci-image/unwritable-workdir", async |runner| {
@@ -130,6 +156,13 @@ USER app
130156
if create.success() || create.stdout().contains("should-not-run") {
131157
return Err(create.failure_diagnostic("sandbox creation fails before the command runs"));
132158
}
159+
if !create.stdout().contains(WORKSPACE_VALIDATION_FAILED)
160+
&& !create.stderr().contains(WORKSPACE_VALIDATION_FAILED)
161+
{
162+
return Err(create.failure_diagnostic(&format!(
163+
"sandbox creation fails with {WORKSPACE_VALIDATION_FAILED}"
164+
)));
165+
}
133166
Ok(())
134167
})
135168
.await;
@@ -173,6 +206,7 @@ async fn create_sandbox(
173206
suffix: &str,
174207
short: &str,
175208
image: &TestImage,
209+
policy: Option<&str>,
176210
checks: &str,
177211
) -> Result<String, String> {
178212
// Sandbox names are limited to 19 characters on some drivers.
@@ -182,14 +216,16 @@ async fn create_sandbox(
182216
"(set -eu; {checks} touch main-write) >/tmp/oci-main.log 2>&1; \
183217
echo $? >/tmp/oci-main.status; exec sleep infinity"
184218
);
219+
let mut args = vec!["sandbox", "create", "--name", &name, "--from", &image.tag];
220+
if let Some(policy) = policy {
221+
args.extend(["--policy", policy]);
222+
}
223+
args.extend(["--detach", "--", "sh", "-c", &main]);
185224
runner
186225
.step(format!("{suffix}/create"))
187226
.description("sandbox starts from the test image")
188227
.with_timeout(CREATE_TIMEOUT)
189-
.run(&[
190-
"sandbox", "create", "--name", &name, "--from", &image.tag, "--detach", "--", "sh",
191-
"-c", &main,
192-
])
228+
.run(&args)
193229
.await
194230
.map_err(|error| error.to_string())?
195231
.require_success()?;

0 commit comments

Comments
 (0)