@@ -46,13 +46,15 @@ use tracing_subscriber::prelude::*;
4646use crate :: isolation:: Platform ;
4747use crate :: isolation:: isolated;
4848use crate :: share:: VirtiofsShare ;
49+ use crate :: types:: CONSOLE_LOG ;
4950use crate :: types:: MachineOpts ;
5051use crate :: types:: MountPlatformDecision ;
5152use crate :: types:: VMArgs ;
5253use crate :: utils:: create_tpx_blobs;
5354use crate :: utils:: create_tpx_logs;
5455use crate :: utils:: env_names_to_kvpairs;
5556use crate :: utils:: log_command;
57+ use crate :: utils:: tpx_artifacts_dir;
5658use crate :: vm:: VM ;
5759use crate :: vm:: VMError ;
5860
@@ -119,15 +121,22 @@ fn run(args: &RunCmdArgs) -> Result<()> {
119121
120122 let mut vm_args = args. vm_args . clone ( ) ;
121123 if args. postmortem {
122- if args. vm_args . console_output_file . is_none ( ) {
123- bail ! ( "Console output file must be specified to run command after VM termination." ) ;
124+ if args. vm_args . logs_dir . is_none ( ) {
125+ bail ! ( "logs_dir must be specified to run command after VM termination." ) ;
124126 }
125127 if args. vm_args . mode . command . is_none ( ) {
126128 bail ! ( "Expected to run command after VM termination but no command specified." ) ;
127129 }
128- // Don't run the test command inside the VM. Hijack it with our stub so we shut it down as
129- // soon as default target is reached.
130- vm_args. mode . command = Some ( vec ! [ "sh" . into( ) , "-c" . into( ) , "exit" . into( ) ] ) ;
130+ // A postmortem test runs on the host, so it must never be handed to the
131+ // guest. Hijack the guest command with a stub that just returns, which
132+ // leaves the VM at its default target for us to tear down - unless the
133+ // test wants to observe a guest-initiated shutdown, in which case the
134+ // stub has to arm ACPI S5 instead. `--no-block` is what lets the ssh
135+ // session close cleanly before systemd starts shutting down.
136+ vm_args. mode . command = Some ( match vm_args. expect_vm_exit {
137+ Some ( _) => vec ! [ "systemctl" . into( ) , "poweroff" . into( ) , "--no-block" . into( ) ] ,
138+ None => vec ! [ "sh" . into( ) , "-c" . into( ) , "exit" . into( ) ] ,
139+ } ) ;
131140 }
132141
133142 let machine_opts = args. machine_spec . clone ( ) . into_inner ( ) ;
@@ -160,13 +169,15 @@ fn run(args: &RunCmdArgs) -> Result<()> {
160169 args. vm_args . command_envs . iter ( ) . for_each ( |pair| {
161170 cmd. env ( & pair. key , & pair. value ) ;
162171 } ) ;
163- cmd. env (
164- "CONSOLE_OUTPUT" ,
165- args. vm_args
166- . console_output_file
167- . as_ref ( )
168- . expect ( "No console output file" ) ,
169- ) ;
172+ let logs_dir = args
173+ . vm_args
174+ . logs_dir
175+ . as_ref ( )
176+ . expect ( "logs_dir is checked above for postmortem" ) ;
177+ // Tests find per-process logs by binary name, e.g.
178+ // $SIDECAR_LOGS_DIR/tgtd.log.
179+ cmd. env ( "CONSOLE_OUTPUT" , logs_dir. join ( CONSOLE_LOG ) ) ;
180+ cmd. env ( "SIDECAR_LOGS_DIR" , logs_dir) ;
170181 cmd_args. iter ( ) . skip ( 1 ) . for_each ( |arg| {
171182 cmd. arg ( arg) ;
172183 } ) ;
@@ -192,12 +203,12 @@ fn respawn(args: &IsolateCmdArgs) -> Result<()> {
192203 let envs = env_names_to_kvpairs ( args. passenv . clone ( ) ) ;
193204 vm_args. command_envs = envs. clone ( ) ;
194205
195- // Let's always capture console output unless it's console mode
196- let _console_dir ;
197- if !vm_args. mode . console && vm_args. console_output_file . is_none ( ) {
198- let dir = tempdir ( ) . context ( "Failed to create temp dir for console output " ) ?;
199- vm_args. console_output_file = Some ( dir. path ( ) . join ( "console.txt" ) ) ;
200- _console_dir = dir;
206+ // Let's always capture logs unless it's console mode.
207+ let _logs_dir ;
208+ if !vm_args. mode . console && vm_args. logs_dir . is_none ( ) {
209+ let dir = tempdir ( ) . context ( "Failed to create temp dir for logs " ) ?;
210+ vm_args. logs_dir = Some ( dir. path ( ) . to_path_buf ( ) ) ;
211+ _logs_dir = dir;
201212 }
202213
203214 antlir2_rootless:: unshare_new_userns ( ) ?;
@@ -314,24 +325,20 @@ fn get_test_vm_args(
314325 }
315326 vm_args. mode . command = Some ( test_args. test . into_inner_cmd ( ) ) ;
316327 vm_args. command_envs = envs;
317- // Only auto-route the console to tpx artifacts if the caller didn't
318- // already specify --console-output-file. This lets `buck2 run` consumers
319- // (e.g. skycastle workflows) persist guest console output to an arbitrary
320- // path without being silently overridden.
321- if vm_args. console_output_file . is_none ( ) {
322- vm_args. console_output_file = create_tpx_logs ( "console.txt" , "console logs" ) ?;
328+ // Collect logs straight into the tpx artifacts dir so that everything
329+ // written there is uploaded and inspectable on failure.
330+ if vm_args. logs_dir . is_none ( ) {
331+ create_tpx_logs ( CONSOLE_LOG , "console logs" ) ?;
332+ vm_args. logs_dir = tpx_artifacts_dir ( ) ;
323333 }
324334 if dump_eth0_traffic {
325335 vm_args. eth0_output_file = create_tpx_blobs ( "eth0.pcap" , "eth0 traffic" ) ?;
326336 }
327- if let Some ( console_output_dir) = vm_args
328- . console_output_file
329- . as_ref ( )
330- . and_then ( |f| f. parent ( ) )
331- {
332- vm_args
333- . output_dirs
334- . push ( console_output_dir. to_owned ( ) . canonicalize ( ) ?) ;
337+ if let Some ( logs_dir) = & vm_args. logs_dir {
338+ let canonical = logs_dir. canonicalize ( ) . unwrap_or_else ( |_| logs_dir. clone ( ) ) ;
339+ if !vm_args. output_dirs . contains ( & canonical) {
340+ vm_args. output_dirs . push ( canonical) ;
341+ }
335342 }
336343 Ok ( ValidatedVMArgs {
337344 inner : vm_args,
@@ -449,6 +456,13 @@ fn test(args: &IsolateCmdArgs) -> Result<()> {
449456 args. passenv . clone ( ) ,
450457 args. dump_eth0_traffic ,
451458 ) ?;
459+
460+ // Annotate every host-side log up front so tpx uploads it even if the
461+ // producing process never writes anything.
462+ for log in machine_spec. host_log_files ( ) {
463+ create_tpx_logs ( & log. name , & log. description ) ?;
464+ }
465+
452466 antlir2_rootless:: unshare_new_userns ( ) ?;
453467 antlir2_isolate:: unshare_and_privatize_mount_ns ( ) . context ( "while isolating mount ns" ) ?;
454468
0 commit comments