@@ -21,9 +21,6 @@ const BASE_IMAGE: &str = "nvcr.io/nvidia/base/ubuntu:24.04";
2121const ENGINE_ENV : & str = "OPENSHELL_TEST_CONTAINER_ENGINE" ;
2222const CREATE_TIMEOUT : Duration = Duration :: from_mins ( 10 ) ;
2323const 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" ;
2724/// A complete sandbox policy without a `process` section, so the sandbox
2825/// identity falls back to the image `USER`.
2926const IMAGE_IDENTITY_POLICY : & str = "version: 1
@@ -117,8 +114,9 @@ async fn default_workdir_uses_managed_workspace() {
117114}
118115
119116/// OpenShell rejects a custom `WORKDIR` that the image user cannot write
120- /// instead of granting the user new access to it, and reports that reason
121- /// rather than a generic startup failure.
117+ /// instead of granting the user new access to it. Drivers may surface the
118+ /// rejection through different startup diagnostics rather than one condition
119+ /// reason.
122120#[ tokio:: test]
123121async fn unwritable_custom_workdir_is_rejected ( ) {
124122 run ( "oci-image/unwritable-workdir" , async |runner| {
@@ -156,18 +154,58 @@ USER app
156154 if create. success ( ) || create. stdout ( ) . contains ( "should-not-run" ) {
157155 return Err ( create. failure_diagnostic ( "sandbox creation fails before the command runs" ) ) ;
158156 }
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- ) ) ) ;
157+ let diagnostic = format ! ( "{}\n {}" , create. stdout( ) , create. stderr( ) ) ;
158+ if !has_workspace_rejection_diagnostic ( & diagnostic) {
159+ return Err ( create. failure_diagnostic (
160+ "sandbox creation reports a workspace, permission, or workload startup rejection" ,
161+ ) ) ;
165162 }
166163 Ok ( ( ) )
167164 } )
168165 . await ;
169166}
170167
168+ fn has_workspace_rejection_diagnostic ( diagnostic : & str ) -> bool {
169+ let diagnostic = diagnostic. to_ascii_lowercase ( ) ;
170+ // The CLI may wrap the human message across lines with diagnostic gutters.
171+ // Match the existing startup reason and exit detail independently.
172+ if diagnostic. contains ( "containerexited" ) && diagnostic. contains ( "exited with code" ) {
173+ return true ;
174+ }
175+ [
176+ "workspace" ,
177+ "workingdir" ,
178+ "permission denied" ,
179+ // Some drivers expose the rejected workload launch through SSH rather
180+ // than propagating the runtime's workspace validation text.
181+ "subsystem request failed" ,
182+ ]
183+ . iter ( )
184+ . any ( |message| diagnostic. contains ( message) )
185+ }
186+
187+ #[ test]
188+ fn workspace_rejection_diagnostics_do_not_require_one_condition_reason ( ) {
189+ for diagnostic in [
190+ "image workspace validation failed" ,
191+ "WorkingDir /workspace/project is not writable" ,
192+ "Permission denied (os error 13)" ,
193+ "ContainerExited: Container exited with code 1" ,
194+ "Error: × sandbox entered error phase while provisioning: ContainerExited: Container\n │ exited with code 1" ,
195+ "subsystem request failed" ,
196+ ] {
197+ assert ! ( has_workspace_rejection_diagnostic( diagnostic) ) ;
198+ }
199+ for diagnostic in [
200+ "" ,
201+ "gateway connection refused" ,
202+ "image pull failed" ,
203+ "ContainerExited" ,
204+ ] {
205+ assert ! ( !has_workspace_rejection_diagnostic( diagnostic) ) ;
206+ }
207+ }
208+
171209async fn run ( scenario : & str , test : impl AsyncFnOnce ( & mut OpenShellRunner ) -> Result < ( ) , String > ) {
172210 let mut runner =
173211 OpenShellRunner :: from_env ( scenario) . expect ( "candidate openshell CLI is available" ) ;
0 commit comments