1use crate::cli::format::{
6 format_create_error, format_destroy_error, format_resolve_error, format_start_error,
7};
8use crate::lifecycle::{
9 ActionError, CreateError, DestroyError, StartError, create_instance_in_collection,
10 destroy_instance_in_collection, resolve_instance, start_instance, start_instance_with_args,
11};
12use anyhow::{Result, bail, format_err};
13#[allow(unused)]
14use flex_client::ProxyHasDomain;
15use flex_client::Socket;
16use flex_fuchsia_component as fcomponent;
17use flex_fuchsia_component_decl as fdecl;
18use flex_fuchsia_process as fprocess;
19use flex_fuchsia_sys2 as fsys;
20use fuchsia_url::fuchsia_pkg::AbsoluteComponentUrl;
21use futures::future::BoxFuture;
22#[allow(unused)]
23use futures::{AsyncReadExt, AsyncWriteExt};
24use moniker::Moniker;
25use std::io::Read;
26
27const TRANSFER_CHUNK_SIZE: usize = 8192;
31
32async fn copy<W: std::io::Write>(source: Socket, mut sink: W) -> Result<()> {
33 let mut source = flex_client::socket_to_async(source);
34 let mut buf = [0u8; TRANSFER_CHUNK_SIZE];
35 loop {
36 let bytes_read = source.read(&mut buf).await?;
37 if bytes_read == 0 {
38 return Ok(());
39 }
40 sink.write_all(&buf[..bytes_read])?;
41 sink.flush()?;
42 }
43}
44
45fn handle_id_for_fd(fd: u32) -> u32 {
49 const PA_FD: u32 = 0x30;
50 PA_FD | fd << 16
51}
52
53struct Stdio {
54 local_in: Socket,
55 local_out: Socket,
56 local_err: Socket,
57}
58
59impl Stdio {
60 #[cfg(not(feature = "fdomain"))]
61 fn new() -> (Self, Vec<fprocess::HandleInfo>) {
62 let (local_in, remote_in) = fidl::Socket::create_stream();
63 let (local_out, remote_out) = fidl::Socket::create_stream();
64 let (local_err, remote_err) = fidl::Socket::create_stream();
65
66 (
67 Self { local_in, local_out, local_err },
68 vec![
69 fprocess::HandleInfo { handle: remote_in.into_handle(), id: handle_id_for_fd(0) },
70 fprocess::HandleInfo { handle: remote_out.into_handle(), id: handle_id_for_fd(1) },
71 fprocess::HandleInfo { handle: remote_err.into_handle(), id: handle_id_for_fd(2) },
72 ],
73 )
74 }
75
76 #[cfg(feature = "fdomain")]
77 fn new(client: &std::sync::Arc<flex_client::Client>) -> (Self, Vec<fprocess::HandleInfo>) {
78 use flex_client::HandleBased;
79 let (local_in, remote_in) = client.create_stream_socket();
80 let (local_out, remote_out) = client.create_stream_socket();
81 let (local_err, remote_err) = client.create_stream_socket();
82
83 (
84 Self { local_in, local_out, local_err },
85 vec![
86 fprocess::HandleInfo { handle: remote_in.into_handle(), id: handle_id_for_fd(0) },
87 fprocess::HandleInfo { handle: remote_out.into_handle(), id: handle_id_for_fd(1) },
88 fprocess::HandleInfo { handle: remote_err.into_handle(), id: handle_id_for_fd(2) },
89 ],
90 )
91 }
92
93 async fn forward(self) {
94 let local_in = self.local_in;
95 let local_out = self.local_out;
96 let local_err = self.local_err;
97
98 let mut local_in = flex_client::socket_to_async(local_in);
99
100 std::thread::spawn(move || {
101 let mut term_in = std::io::stdin().lock();
102 let mut buf = [0u8; TRANSFER_CHUNK_SIZE];
103 let mut executor = fuchsia_async::LocalExecutorBuilder::new().build();
104 loop {
105 let bytes_read = term_in.read(&mut buf)?;
106 if bytes_read == 0 {
107 return Ok::<(), anyhow::Error>(());
108 }
109
110 executor.run_singlethreaded(local_in.write_all(&buf[..bytes_read]))?;
111 }
112 });
113
114 std::thread::spawn(move || {
115 let mut executor = fuchsia_async::LocalExecutorBuilder::new().build();
116 let _result: Result<()> = executor
117 .run_singlethreaded(async move { copy(local_err, std::io::stderr()).await });
118 });
119
120 std::thread::spawn(move || {
121 let mut executor = fuchsia_async::LocalExecutorBuilder::new().build();
122 let _result: Result<()> = executor
123 .run_singlethreaded(async move { copy(local_out, std::io::stdout().lock()).await });
124 std::process::exit(0);
125 });
126
127 let () = futures::future::pending().await;
130 }
131}
132
133pub async fn run_cmd<W: std::io::Write>(
134 moniker: Moniker,
135 url: AbsoluteComponentUrl,
136 recreate: bool,
137 connect_stdio: bool,
138 config_overrides: Vec<fdecl::ConfigOverride>,
139 lifecycle_controller_factory: impl Fn()
140 -> BoxFuture<'static, Result<fsys::LifecycleControllerProxy>>,
141 mut writer: W,
142) -> Result<()> {
143 let lifecycle_controller = lifecycle_controller_factory().await?;
144 let parent = moniker
145 .parent()
146 .ok_or_else(|| format_err!("Error: {} does not reference a dynamic instance", moniker))?;
147 let leaf = moniker
148 .leaf()
149 .ok_or_else(|| format_err!("Error: {} does not reference a dynamic instance", moniker))?;
150 let child_name = leaf.name();
151 let collection = leaf
152 .collection()
153 .ok_or_else(|| format_err!("Error: {} does not reference a dynamic instance", moniker))?;
154
155 if recreate {
156 match destroy_instance_in_collection(&lifecycle_controller, &parent, collection, child_name)
158 .await
159 {
160 Ok(()) => {
161 writeln!(writer, "Destroyed existing component instance at {}...", moniker)?;
162 }
163 Err(DestroyError::ActionError(ActionError::InstanceNotFound))
164 | Err(DestroyError::ActionError(ActionError::InstanceNotResolved)) => {
165 }
167 Err(e) => return Err(format_destroy_error(&moniker, e)),
168 }
169 }
170
171 writeln!(writer, "URL: {}", url)?;
172 writeln!(writer, "Moniker: {}", moniker)?;
173 writeln!(writer, "Creating component instance...")?;
174
175 let (mut maybe_stdio, numbered_handles) = if connect_stdio {
178 #[cfg(not(feature = "fdomain"))]
179 let (stdio, numbered_handles) = Stdio::new();
180 #[cfg(feature = "fdomain")]
181 let (stdio, numbered_handles) = Stdio::new(&lifecycle_controller.domain());
182 (Some(stdio), Some(numbered_handles))
183 } else {
184 (None, Some(vec![]))
185 };
186
187 let create_result = create_instance_in_collection(
188 &lifecycle_controller,
189 &parent,
190 collection,
191 child_name,
192 &url,
193 config_overrides.clone(),
194 None,
195 )
196 .await;
197
198 match create_result {
199 Err(CreateError::InstanceAlreadyExists) => {
200 bail!(
201 "\nError: {} already exists.\nUse --recreate to destroy and create a new instance, or provide a different moniker.\n",
202 moniker
203 )
204 }
205 Err(e) => {
206 return Err(format_create_error(&moniker, &parent, collection, e));
207 }
208 Ok(()) => {}
209 }
210
211 writeln!(writer, "Resolving component instance...")?;
212 if let Err(e) = resolve_instance(&lifecycle_controller, &moniker).await {
213 writeln!(writer, "Cleaning up component instance...")?;
214 if let Err(de) =
215 destroy_instance_in_collection(&lifecycle_controller, &parent, collection, child_name)
216 .await
217 {
218 writeln!(writer, "Warning: Failed to clean up component instance: {:?}", de)?;
219 }
220 return Err(format_resolve_error(&moniker, e));
221 }
222
223 writeln!(writer, "Starting component instance...")?;
224 let start_args = fcomponent::StartChildArgs { numbered_handles, ..Default::default() };
225 let res = start_instance_with_args(&lifecycle_controller, &moniker, start_args).await;
226 if let Err(StartError::ActionError(ActionError::Fidl(_e))) = &res {
227 let lifecycle_controller = lifecycle_controller_factory().await?;
234
235 if connect_stdio {
236 #[cfg(not(feature = "fdomain"))]
241 let (stdio, numbered_handles) = Stdio::new();
242 #[cfg(feature = "fdomain")]
243 let (stdio, numbered_handles) = Stdio::new(&lifecycle_controller.domain());
244 maybe_stdio = Some(stdio);
245 let create_args = fcomponent::CreateChildArgs {
246 numbered_handles: Some(numbered_handles),
247 ..Default::default()
248 };
249
250 destroy_instance_in_collection(&lifecycle_controller, &parent, collection, child_name)
251 .await?;
252 create_instance_in_collection(
253 &lifecycle_controller,
254 &parent,
255 collection,
256 child_name,
257 &url,
258 config_overrides,
259 Some(create_args),
260 )
261 .await?;
262 resolve_instance(&lifecycle_controller, &moniker)
263 .await
264 .map_err(|e| format_resolve_error(&moniker, e))?;
265 }
266
267 let _stop_future = start_instance(&lifecycle_controller, &moniker)
268 .await
269 .map_err(|e| format_start_error(&moniker, e))?;
270 } else {
271 let _stop_future = res.map_err(|e| format_start_error(&moniker, e))?;
272 }
273
274 if let Some(stdio) = maybe_stdio {
275 stdio.forward().await;
276 }
277
278 writeln!(writer, "Component instance is running!")?;
279
280 Ok(())
281}
282
283#[cfg(test)]
284mod test {
285 use super::*;
286 use fidl::endpoints::create_proxy_and_stream;
287 use flex_fuchsia_sys2 as fsys;
288 use futures::{FutureExt, TryStreamExt};
289
290 fn setup_fake_lifecycle_controller_ok(
291 expected_parent_moniker: &'static str,
292 expected_collection: &'static str,
293 expected_name: &'static str,
294 expected_url: &'static str,
295 expected_moniker: &'static str,
296 expect_destroy: bool,
297 ) -> fsys::LifecycleControllerProxy {
298 let (lifecycle_controller, mut stream) =
299 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
300 fuchsia_async::Task::local(async move {
301 if expect_destroy {
302 let req = stream.try_next().await.unwrap().unwrap();
303 match req {
304 fsys::LifecycleControllerRequest::DestroyInstance {
305 parent_moniker,
306 child,
307 responder,
308 } => {
309 assert_eq!(
310 Moniker::parse_str(expected_parent_moniker),
311 Moniker::parse_str(&parent_moniker)
312 );
313 assert_eq!(expected_name, child.name);
314 assert_eq!(expected_collection, child.collection.unwrap());
315 responder.send(Ok(())).unwrap();
316 }
317 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
318 }
319 }
320
321 let req = stream.try_next().await.unwrap().unwrap();
322 match req {
323 fsys::LifecycleControllerRequest::CreateInstance {
324 parent_moniker,
325 collection,
326 decl,
327 responder,
328 args: _,
329 } => {
330 assert_eq!(
331 Moniker::parse_str(expected_parent_moniker),
332 Moniker::parse_str(&parent_moniker)
333 );
334 assert_eq!(expected_collection, collection.name);
335 assert_eq!(expected_name, decl.name.unwrap());
336 assert_eq!(expected_url, decl.url.unwrap());
337 responder.send(Ok(())).unwrap();
338 }
339 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
340 }
341
342 let req = stream.try_next().await.unwrap().unwrap();
343 match req {
344 fsys::LifecycleControllerRequest::ResolveInstance { moniker, responder } => {
345 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
346 responder.send(Ok(())).unwrap();
347 }
348 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
349 }
350
351 let req = stream.try_next().await.unwrap().unwrap();
352 match req {
353 fsys::LifecycleControllerRequest::StartInstanceWithArgs {
354 moniker,
355 binder: _,
356 args: _,
357 responder,
358 } => {
359 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
360 responder.send(Ok(())).unwrap();
361 }
362 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
363 }
364 })
365 .detach();
366 lifecycle_controller
367 }
368
369 fn setup_fake_lifecycle_controller_fail(
370 expected_parent_moniker: &'static str,
371 expected_collection: &'static str,
372 expected_name: &'static str,
373 expected_url: &'static str,
374 ) -> fsys::LifecycleControllerProxy {
375 let (lifecycle_controller, mut stream) =
376 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
377 fuchsia_async::Task::local(async move {
378 let req = stream.try_next().await.unwrap().unwrap();
379 match req {
380 fsys::LifecycleControllerRequest::DestroyInstance {
381 parent_moniker,
382 child,
383 responder,
384 } => {
385 assert_eq!(
386 Moniker::parse_str(expected_parent_moniker),
387 Moniker::parse_str(&parent_moniker)
388 );
389 assert_eq!(expected_name, child.name);
390 assert_eq!(expected_collection, child.collection.unwrap());
391 responder.send(Ok(())).unwrap();
392 }
393 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
394 }
395
396 let req = stream.try_next().await.unwrap().unwrap();
397 match req {
398 fsys::LifecycleControllerRequest::CreateInstance {
399 parent_moniker,
400 collection,
401 decl,
402 responder,
403 args: _,
404 } => {
405 assert_eq!(
406 Moniker::parse_str(expected_parent_moniker),
407 Moniker::parse_str(&parent_moniker)
408 );
409 assert_eq!(expected_collection, collection.name);
410 assert_eq!(expected_name, decl.name.unwrap());
411 assert_eq!(expected_url, decl.url.unwrap());
412 responder.send(Err(fsys::CreateError::InstanceAlreadyExists)).unwrap();
413 }
414 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
415 }
416 })
417 .detach();
418 lifecycle_controller
419 }
420
421 fn setup_fake_lifecycle_controller_recreate(
422 expected_parent_moniker: &'static str,
423 expected_collection: &'static str,
424 expected_name: &'static str,
425 expected_url: &'static str,
426 expected_moniker: &'static str,
427 ) -> fsys::LifecycleControllerProxy {
428 let (lifecycle_controller, mut stream) =
429 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
430 fuchsia_async::Task::local(async move {
431 let req = stream.try_next().await.unwrap().unwrap();
432 match req {
433 fsys::LifecycleControllerRequest::DestroyInstance {
434 parent_moniker,
435 child,
436 responder,
437 } => {
438 assert_eq!(
439 Moniker::parse_str(expected_parent_moniker),
440 Moniker::parse_str(&parent_moniker)
441 );
442 assert_eq!(expected_name, child.name);
443 assert_eq!(expected_collection, child.collection.unwrap());
444 responder.send(Ok(())).unwrap();
445 }
446 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
447 }
448
449 let req = stream.try_next().await.unwrap().unwrap();
450 match req {
451 fsys::LifecycleControllerRequest::CreateInstance {
452 parent_moniker,
453 collection,
454 decl,
455 responder,
456 args: _,
457 } => {
458 assert_eq!(
459 Moniker::parse_str(expected_parent_moniker),
460 Moniker::parse_str(&parent_moniker)
461 );
462 assert_eq!(expected_collection, collection.name);
463 assert_eq!(expected_name, decl.name.unwrap());
464 assert_eq!(expected_url, decl.url.unwrap());
465 responder.send(Ok(())).unwrap();
466 }
467 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
468 }
469
470 let req = stream.try_next().await.unwrap().unwrap();
471 match req {
472 fsys::LifecycleControllerRequest::ResolveInstance { moniker, responder } => {
473 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
474 responder.send(Ok(())).unwrap();
475 }
476 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
477 }
478
479 let req = stream.try_next().await.unwrap().unwrap();
480 match req {
481 fsys::LifecycleControllerRequest::StartInstanceWithArgs {
482 moniker,
483 binder: _,
484 args: _,
485 responder,
486 } => {
487 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
488 responder.send(Ok(())).unwrap();
489 }
490 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
491 }
492 })
493 .detach();
494 lifecycle_controller
495 }
496
497 #[fuchsia::test]
498 async fn test_ok() -> Result<()> {
499 let mut output = Vec::new();
500 let lifecycle_controller = setup_fake_lifecycle_controller_ok(
501 "/some",
502 "collection",
503 "name",
504 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
505 "/some/collection:name",
506 true,
507 );
508 let response = run_cmd(
509 "/some/collection:name".try_into().unwrap(),
510 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
511 true,
512 false,
513 vec![],
514 move || {
515 let lifecycle_controller = lifecycle_controller.clone();
516 async move { Ok(lifecycle_controller) }.boxed()
517 },
518 &mut output,
519 )
520 .await;
521 response.unwrap();
522 Ok(())
523 }
524
525 #[fuchsia::test]
526 async fn test_name() -> Result<()> {
527 let mut output = Vec::new();
528 let lifecycle_controller = setup_fake_lifecycle_controller_ok(
529 "/core",
530 "ffx-laboratory",
531 "foobar",
532 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
533 "/core/ffx-laboratory:foobar",
534 false,
535 );
536 let response = run_cmd(
537 "/core/ffx-laboratory:foobar".try_into().unwrap(),
538 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
539 false,
540 false,
541 vec![],
542 move || {
543 let lifecycle_controller = lifecycle_controller.clone();
544 async move { Ok(lifecycle_controller) }.boxed()
545 },
546 &mut output,
547 )
548 .await;
549 response.unwrap();
550 Ok(())
551 }
552
553 #[fuchsia::test]
554 async fn test_fail() -> Result<()> {
555 let mut output = Vec::new();
556 let lifecycle_controller = setup_fake_lifecycle_controller_fail(
557 "/core",
558 "ffx-laboratory",
559 "test",
560 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
561 );
562 let response = run_cmd(
563 "/core/ffx-laboratory:test".try_into().unwrap(),
564 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
565 true,
566 false,
567 vec![],
568 move || {
569 let lifecycle_controller = lifecycle_controller.clone();
570 async move { Ok(lifecycle_controller) }.boxed()
571 },
572 &mut output,
573 )
574 .await;
575 response.unwrap_err();
576 Ok(())
577 }
578
579 #[fuchsia::test]
580 async fn test_recreate() -> Result<()> {
581 let mut output = Vec::new();
582 let lifecycle_controller = setup_fake_lifecycle_controller_recreate(
583 "/core",
584 "ffx-laboratory",
585 "test",
586 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
587 "/core/ffx-laboratory:test",
588 );
589 let response = run_cmd(
590 "/core/ffx-laboratory:test".try_into().unwrap(),
591 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
592 true,
593 false,
594 vec![],
595 move || {
596 let lifecycle_controller = lifecycle_controller.clone();
597 async move { Ok(lifecycle_controller) }.boxed()
598 },
599 &mut output,
600 )
601 .await;
602 response.unwrap();
603 Ok(())
604 }
605
606 fn setup_fake_lifecycle_controller_resolve_fail_cleanup(
607 expected_parent_moniker: &'static str,
608 expected_collection: &'static str,
609 expected_name: &'static str,
610 expected_url: &'static str,
611 expected_moniker: &'static str,
612 destroy_sender: futures::channel::oneshot::Sender<()>,
613 ) -> fsys::LifecycleControllerProxy {
614 let (lifecycle_controller, mut stream) =
615 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
616 fuchsia_async::Task::local(async move {
617 let req = stream.try_next().await.unwrap().unwrap();
618 match req {
619 fsys::LifecycleControllerRequest::CreateInstance {
620 parent_moniker,
621 collection,
622 decl,
623 responder,
624 ..
625 } => {
626 assert_eq!(
627 Moniker::parse_str(expected_parent_moniker),
628 Moniker::parse_str(&parent_moniker)
629 );
630 assert_eq!(expected_collection, collection.name);
631 assert_eq!(expected_name, decl.name.unwrap());
632 assert_eq!(expected_url, decl.url.unwrap());
633 responder.send(Ok(())).unwrap();
634 }
635 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
636 }
637
638 let req = stream.try_next().await.unwrap().unwrap();
639 match req {
640 fsys::LifecycleControllerRequest::ResolveInstance { moniker, responder } => {
641 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
642 responder.send(Err(fsys::ResolveError::PackageNotFound)).unwrap();
643 }
644 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
645 }
646
647 let req = stream.try_next().await.unwrap().unwrap();
648 match req {
649 fsys::LifecycleControllerRequest::DestroyInstance {
650 parent_moniker,
651 child,
652 responder,
653 } => {
654 assert_eq!(
655 Moniker::parse_str(expected_parent_moniker),
656 Moniker::parse_str(&parent_moniker)
657 );
658 assert_eq!(expected_name, child.name);
659 assert_eq!(expected_collection, child.collection.unwrap());
660 responder.send(Ok(())).unwrap();
661 let _ = destroy_sender.send(());
662 }
663 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
664 }
665 })
666 .detach();
667 lifecycle_controller
668 }
669
670 #[fuchsia::test]
671 async fn test_resolve_fail_cleanup() -> Result<()> {
672 let mut output = Vec::new();
673 let (destroy_sender, mut destroy_receiver) = futures::channel::oneshot::channel::<()>();
674
675 let lifecycle_controller = setup_fake_lifecycle_controller_resolve_fail_cleanup(
676 "/core",
677 "ffx-laboratory",
678 "test",
679 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
680 "/core/ffx-laboratory:test",
681 destroy_sender,
682 );
683 let response = run_cmd(
684 "/core/ffx-laboratory:test".try_into().unwrap(),
685 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
686 false,
687 false,
688 vec![],
689 move || {
690 let lifecycle_controller = lifecycle_controller.clone();
691 async move { Ok(lifecycle_controller) }.boxed()
692 },
693 &mut output,
694 )
695 .await;
696
697 assert!(response.is_err());
698 assert!(destroy_receiver.try_recv().unwrap().is_some());
700 Ok(())
701 }
702}