1use cm_types::{LongName, Name};
6use flex_client::ProxyHasDomain;
7use fuchsia_url::AbsoluteComponentUrl;
8use futures::future::BoxFuture;
9use futures::{FutureExt, StreamExt};
10use moniker::Moniker;
11use thiserror::Error;
12use {
13 flex_fuchsia_component as fcomponent, flex_fuchsia_component_decl as fdecl,
14 flex_fuchsia_sys2 as fsys,
15};
16
17#[derive(Error, Debug)]
19pub enum ActionError {
20 #[error("the instance could not be found")]
21 InstanceNotFound,
22 #[error("the instance has not been resolved")]
23 InstanceNotResolved,
24 #[error("component manager could not parse the moniker")]
25 BadMoniker,
26 #[error("component manager encountered an internal error")]
27 Internal,
28 #[error("component manager responded with an unknown error code")]
29 UnknownError,
30 #[error("unexpected FIDL error with LifecycleController: {0}")]
31 Fidl(#[from] fidl::Error),
32}
33
34#[derive(Error, Debug)]
35pub enum CreateError {
36 #[error("the instance already exists")]
37 InstanceAlreadyExists,
38 #[error("component manager could not parse the given child declaration")]
39 BadChildDecl,
40 #[error("the parent instance does not have a collection with the given name")]
41 CollectionNotFound,
42 #[error(transparent)]
43 ActionError(#[from] ActionError),
44}
45
46#[derive(Error, Debug)]
47pub enum DestroyError {
48 #[error("component manager could not parse the given child reference")]
49 BadChildRef,
50 #[error(transparent)]
51 ActionError(#[from] ActionError),
52}
53
54#[derive(Error, Debug)]
55pub enum StartError {
56 #[error("the package identified by the instance URL could not be found")]
57 PackageNotFound,
58 #[error("the manifest for the instance could not be found in its package")]
59 ManifestNotFound,
60 #[error(transparent)]
61 ActionError(#[from] ActionError),
62}
63
64#[derive(Error, Debug)]
65pub enum ResolveError {
66 #[error("the package identified by the instance URL could not be found")]
67 PackageNotFound,
68 #[error("the manifest for the instance could not be found in its package")]
69 ManifestNotFound,
70 #[error(transparent)]
71 ActionError(#[from] ActionError),
72}
73
74pub async fn create_instance_in_collection(
77 lifecycle_controller: &fsys::LifecycleControllerProxy,
78 parent: &Moniker,
79 collection: &Name,
80 child_name: &LongName,
81 url: &AbsoluteComponentUrl,
82 config_overrides: Vec<fdecl::ConfigOverride>,
83 child_args: Option<fcomponent::CreateChildArgs>,
84) -> Result<(), CreateError> {
85 let collection_ref = fdecl::CollectionRef { name: collection.to_string() };
86 let decl = fdecl::Child {
87 name: Some(child_name.to_string()),
88 url: Some(url.to_string()),
89 startup: Some(fdecl::StartupMode::Lazy),
90 environment: None,
91 config_overrides: Some(config_overrides),
92 ..Default::default()
93 };
94
95 lifecycle_controller
96 .create_instance(
97 &parent.to_string(),
98 &collection_ref,
99 &decl,
100 child_args.unwrap_or(fcomponent::CreateChildArgs::default()),
101 )
102 .await
103 .map_err(|e| ActionError::Fidl(e))?
104 .map_err(|e| match e {
105 fsys::CreateError::BadChildDecl => CreateError::BadChildDecl,
106 fsys::CreateError::CollectionNotFound => CreateError::CollectionNotFound,
107 fsys::CreateError::InstanceAlreadyExists => CreateError::InstanceAlreadyExists,
108 fsys::CreateError::Internal => ActionError::Internal.into(),
109 fsys::CreateError::BadMoniker => ActionError::BadMoniker.into(),
110 fsys::CreateError::InstanceNotFound => ActionError::InstanceNotFound.into(),
111 _ => ActionError::UnknownError.into(),
112 })?;
113
114 Ok(())
115}
116
117pub async fn destroy_instance_in_collection(
120 lifecycle_controller: &fsys::LifecycleControllerProxy,
121 parent: &Moniker,
122 collection: &Name,
123 child_name: &LongName,
124) -> Result<(), DestroyError> {
125 let child =
126 fdecl::ChildRef { name: child_name.to_string(), collection: Some(collection.to_string()) };
127
128 lifecycle_controller
129 .destroy_instance(&parent.to_string(), &child)
130 .await
131 .map_err(|e| ActionError::Fidl(e))?
132 .map_err(|e| match e {
133 fsys::DestroyError::BadChildRef => DestroyError::BadChildRef,
134 fsys::DestroyError::Internal => ActionError::Internal.into(),
135 fsys::DestroyError::BadMoniker => ActionError::BadMoniker.into(),
136 fsys::DestroyError::InstanceNotFound => ActionError::InstanceNotFound.into(),
137 fsys::DestroyError::InstanceNotResolved => ActionError::InstanceNotResolved.into(),
138 _ => ActionError::UnknownError.into(),
139 })?;
140 Ok(())
141}
142
143type StopFuture = BoxFuture<'static, Result<(), fidl::Error>>;
146
147pub async fn start_instance(
152 lifecycle_controller: &fsys::LifecycleControllerProxy,
153 moniker: &Moniker,
154) -> Result<StopFuture, StartError> {
155 let (client, server) = lifecycle_controller.domain().create_proxy::<fcomponent::BinderMarker>();
156 lifecycle_controller
157 .start_instance(&moniker.to_string(), server)
158 .await
159 .map_err(|e| ActionError::Fidl(e))?
160 .map_err(|e| match e {
161 fsys::StartError::PackageNotFound => StartError::PackageNotFound,
162 fsys::StartError::ManifestNotFound => StartError::ManifestNotFound,
163 fsys::StartError::Internal => ActionError::Internal.into(),
164 fsys::StartError::BadMoniker => ActionError::BadMoniker.into(),
165 fsys::StartError::InstanceNotFound => ActionError::InstanceNotFound.into(),
166 _ => ActionError::UnknownError.into(),
167 })?;
168 let stop_future = async move {
169 let mut event_stream = client.take_event_stream();
170 match event_stream.next().await {
171 Some(Err(e)) => return Err(e),
172 None => return Ok(()),
173 }
174 }
175 .boxed();
176 Ok(stop_future)
177}
178
179pub async fn start_instance_with_args(
184 lifecycle_controller: &fsys::LifecycleControllerProxy,
185 moniker: &Moniker,
186 arguments: fcomponent::StartChildArgs,
187) -> Result<StopFuture, StartError> {
188 let (client, server) = lifecycle_controller.domain().create_proxy::<fcomponent::BinderMarker>();
189 lifecycle_controller
190 .start_instance_with_args(&moniker.to_string(), server, arguments)
191 .await
192 .map_err(|e| ActionError::Fidl(e))?
193 .map_err(|e| match e {
194 fsys::StartError::PackageNotFound => StartError::PackageNotFound,
195 fsys::StartError::ManifestNotFound => StartError::ManifestNotFound,
196 fsys::StartError::Internal => ActionError::Internal.into(),
197 fsys::StartError::BadMoniker => ActionError::BadMoniker.into(),
198 fsys::StartError::InstanceNotFound => ActionError::InstanceNotFound.into(),
199 _ => ActionError::UnknownError.into(),
200 })?;
201 let stop_future = async move {
202 let mut event_stream = client.take_event_stream();
203 match event_stream.next().await {
204 Some(Err(e)) => return Err(e),
205 None => return Ok(()),
206 }
207 }
208 .boxed();
209 Ok(stop_future)
210}
211
212pub async fn stop_instance(
215 lifecycle_controller: &fsys::LifecycleControllerProxy,
216 moniker: &Moniker,
217) -> Result<(), ActionError> {
218 lifecycle_controller
219 .stop_instance(&moniker.to_string())
220 .await
221 .map_err(|e| ActionError::Fidl(e))?
222 .map_err(|e| match e {
223 fsys::StopError::Internal => ActionError::Internal,
224 fsys::StopError::BadMoniker => ActionError::BadMoniker,
225 fsys::StopError::InstanceNotFound => ActionError::InstanceNotFound,
226 _ => ActionError::UnknownError,
227 })?;
228 Ok(())
229}
230
231pub async fn resolve_instance(
234 lifecycle_controller: &fsys::LifecycleControllerProxy,
235 moniker: &Moniker,
236) -> Result<(), ResolveError> {
237 lifecycle_controller
238 .resolve_instance(&moniker.to_string())
239 .await
240 .map_err(|e| ActionError::Fidl(e))?
241 .map_err(|e| match e {
242 fsys::ResolveError::PackageNotFound => ResolveError::PackageNotFound,
243 fsys::ResolveError::ManifestNotFound => ResolveError::ManifestNotFound,
244 fsys::ResolveError::Internal => ActionError::Internal.into(),
245 fsys::ResolveError::BadMoniker => ActionError::BadMoniker.into(),
246 fsys::ResolveError::InstanceNotFound => ActionError::InstanceNotFound.into(),
247 _ => ActionError::UnknownError.into(),
248 })?;
249 Ok(())
250}
251
252pub async fn unresolve_instance(
255 lifecycle_controller: &fsys::LifecycleControllerProxy,
256 moniker: &Moniker,
257) -> Result<(), ActionError> {
258 lifecycle_controller
259 .unresolve_instance(&moniker.to_string())
260 .await
261 .map_err(|e| ActionError::Fidl(e))?
262 .map_err(|e| match e {
263 fsys::UnresolveError::Internal => ActionError::Internal,
264 fsys::UnresolveError::BadMoniker => ActionError::BadMoniker,
265 fsys::UnresolveError::InstanceNotFound => ActionError::InstanceNotFound,
266 _ => ActionError::UnknownError,
267 })?;
268 Ok(())
269}
270
271#[cfg(test)]
272mod test {
273 use super::*;
274 use assert_matches::assert_matches;
275 use fidl::endpoints::create_proxy_and_stream;
276 use fidl::HandleBased;
277 use flex_fuchsia_process as fprocess;
278 use futures::TryStreamExt;
279
280 fn lifecycle_create_instance(
281 expected_moniker: &'static str,
282 expected_collection: &'static str,
283 expected_name: &'static str,
284 expected_url: &'static str,
285 expected_numbered_handle_count: usize,
286 ) -> fsys::LifecycleControllerProxy {
287 let (lifecycle_controller, mut stream) =
288 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
289 fuchsia_async::Task::local(async move {
290 let req = stream.try_next().await.unwrap().unwrap();
291 match req {
292 fsys::LifecycleControllerRequest::CreateInstance {
293 parent_moniker,
294 collection,
295 decl,
296 args,
297 responder,
298 ..
299 } => {
300 assert_eq!(
301 Moniker::parse_str(expected_moniker),
302 Moniker::parse_str(&parent_moniker)
303 );
304 assert_eq!(expected_collection, collection.name);
305 assert_eq!(expected_name, decl.name.unwrap());
306 assert_eq!(expected_url, decl.url.unwrap());
307 assert_eq!(
308 expected_numbered_handle_count,
309 args.numbered_handles.unwrap_or(vec![]).len()
310 );
311 responder.send(Ok(())).unwrap();
312 }
313 _ => panic!("Unexpected Lifecycle Controller request"),
314 }
315 })
316 .detach();
317 lifecycle_controller
318 }
319
320 fn lifecycle_destroy_instance(
321 expected_moniker: &'static str,
322 expected_collection: &'static str,
323 expected_name: &'static str,
324 ) -> fsys::LifecycleControllerProxy {
325 let (lifecycle_controller, mut stream) =
326 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
327 fuchsia_async::Task::local(async move {
328 let req = stream.try_next().await.unwrap().unwrap();
329 match req {
330 fsys::LifecycleControllerRequest::DestroyInstance {
331 parent_moniker,
332 child,
333 responder,
334 ..
335 } => {
336 assert_eq!(
337 Moniker::parse_str(expected_moniker),
338 Moniker::parse_str(&parent_moniker)
339 );
340 assert_eq!(expected_name, child.name);
341 assert_eq!(expected_collection, child.collection.unwrap());
342 responder.send(Ok(())).unwrap();
343 }
344 _ => panic!("Unexpected Lifecycle Controller request"),
345 }
346 })
347 .detach();
348 lifecycle_controller
349 }
350
351 fn lifecycle_start(expected_moniker: &'static str) -> fsys::LifecycleControllerProxy {
352 let (lifecycle_controller, mut stream) =
353 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
354 fuchsia_async::Task::local(async move {
355 let req = stream.try_next().await.unwrap().unwrap();
356 match req {
357 fsys::LifecycleControllerRequest::StartInstanceWithArgs {
358 moniker,
359 responder,
360 ..
361 } => {
362 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
363 responder.send(Ok(())).unwrap();
364 }
365 _ => panic!("Unexpected Lifecycle Controller request"),
366 }
367 })
368 .detach();
369 lifecycle_controller
370 }
371
372 fn lifecycle_stop(expected_moniker: &'static str) -> fsys::LifecycleControllerProxy {
373 let (lifecycle_controller, mut stream) =
374 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
375 fuchsia_async::Task::local(async move {
376 let req = stream.try_next().await.unwrap().unwrap();
377 match req {
378 fsys::LifecycleControllerRequest::StopInstance { moniker, responder, .. } => {
379 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
380 responder.send(Ok(())).unwrap();
381 }
382 _ => panic!("Unexpected Lifecycle Controller request"),
383 }
384 })
385 .detach();
386 lifecycle_controller
387 }
388
389 fn lifecycle_resolve(expected_moniker: &'static str) -> fsys::LifecycleControllerProxy {
390 let (lifecycle_controller, mut stream) =
391 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
392 fuchsia_async::Task::local(async move {
393 let req = stream.try_next().await.unwrap().unwrap();
394 match req {
395 fsys::LifecycleControllerRequest::ResolveInstance {
396 moniker, responder, ..
397 } => {
398 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
399 responder.send(Ok(())).unwrap();
400 }
401 _ => panic!("Unexpected Lifecycle Controller request"),
402 }
403 })
404 .detach();
405 lifecycle_controller
406 }
407
408 fn lifecycle_unresolve(expected_moniker: &'static str) -> fsys::LifecycleControllerProxy {
409 let (lifecycle_controller, mut stream) =
410 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
411 fuchsia_async::Task::local(async move {
412 let req = stream.try_next().await.unwrap().unwrap();
413 match req {
414 fsys::LifecycleControllerRequest::UnresolveInstance {
415 moniker, responder, ..
416 } => {
417 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
418 responder.send(Ok(())).unwrap();
419 }
420 _ => panic!("Unexpected Lifecycle Controller request"),
421 }
422 })
423 .detach();
424 lifecycle_controller
425 }
426
427 fn lifecycle_create_fail(error: fsys::CreateError) -> 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::CreateInstance { responder, .. } => {
434 responder.send(Err(error)).unwrap();
435 }
436 _ => panic!("Unexpected Lifecycle Controller request"),
437 }
438 })
439 .detach();
440 lifecycle_controller
441 }
442
443 fn lifecycle_start_fail(error: fsys::StartError) -> fsys::LifecycleControllerProxy {
444 let (lifecycle_controller, mut stream) =
445 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
446 fuchsia_async::Task::local(async move {
447 let req = stream.try_next().await.unwrap().unwrap();
448 match req {
449 fsys::LifecycleControllerRequest::StartInstanceWithArgs { responder, .. } => {
450 responder.send(Err(error)).unwrap();
451 }
452 _ => panic!("Unexpected Lifecycle Controller request"),
453 }
454 })
455 .detach();
456 lifecycle_controller
457 }
458
459 #[fuchsia_async::run_singlethreaded(test)]
460 async fn test_create_child() {
461 let parent = Moniker::parse_str("core").unwrap();
462 let url =
463 AbsoluteComponentUrl::parse("fuchsia-pkg://fuchsia.com/test#meta/test.cm").unwrap();
464 let lc = lifecycle_create_instance(
465 "/core",
466 "foo",
467 "bar",
468 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
469 0,
470 );
471 create_instance_in_collection(
472 &lc,
473 &parent,
474 &"foo".parse().unwrap(),
475 &"bar".parse().unwrap(),
476 &url,
477 vec![],
478 None,
479 )
480 .await
481 .unwrap();
482 }
483
484 #[fuchsia_async::run_singlethreaded(test)]
485 async fn test_create_child_with_numbered_handles() {
486 let parent = Moniker::parse_str("core").unwrap();
487 let url =
488 AbsoluteComponentUrl::parse("fuchsia-pkg://fuchsia.com/test#meta/test.cm").unwrap();
489 let lc = lifecycle_create_instance(
490 "/core",
491 "foo",
492 "bar",
493 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
494 2,
495 );
496 let (left, right) = fidl::Socket::create_stream();
497 let child_args = fcomponent::CreateChildArgs {
498 numbered_handles: Some(vec![
499 fprocess::HandleInfo { handle: left.into_handle(), id: 0x10 },
500 fprocess::HandleInfo { handle: right.into_handle(), id: 0x11 },
501 ]),
502 ..Default::default()
503 };
504 create_instance_in_collection(
505 &lc,
506 &parent,
507 &"foo".parse().unwrap(),
508 &"bar".parse().unwrap(),
509 &url,
510 vec![],
511 Some(child_args),
512 )
513 .await
514 .unwrap();
515 }
516
517 #[fuchsia_async::run_singlethreaded(test)]
518 async fn test_create_already_exists() {
519 let parent = Moniker::parse_str("core").unwrap();
520 let url =
521 AbsoluteComponentUrl::parse("fuchsia-pkg://fuchsia.com/test#meta/test.cm").unwrap();
522 let lc = lifecycle_create_fail(fsys::CreateError::InstanceAlreadyExists);
523 let err = create_instance_in_collection(
524 &lc,
525 &parent,
526 &"foo".parse().unwrap(),
527 &"bar".parse().unwrap(),
528 &url,
529 vec![],
530 None,
531 )
532 .await
533 .unwrap_err();
534 assert_matches!(err, CreateError::InstanceAlreadyExists);
535 }
536
537 #[fuchsia_async::run_singlethreaded(test)]
538 async fn test_destroy_child() {
539 let parent = Moniker::parse_str("core").unwrap();
540 let lc = lifecycle_destroy_instance("core", "foo", "bar");
541 destroy_instance_in_collection(
542 &lc,
543 &parent,
544 &"foo".parse().unwrap(),
545 &"bar".parse().unwrap(),
546 )
547 .await
548 .unwrap();
549 }
550
551 #[fuchsia_async::run_singlethreaded(test)]
552 async fn test_start() {
553 let moniker = Moniker::parse_str("core/foo").unwrap();
554 let lc = lifecycle_start("core/foo");
555 let _ = start_instance_with_args(&lc, &moniker, fcomponent::StartChildArgs::default())
556 .await
557 .unwrap();
558 }
559
560 #[fuchsia_async::run_singlethreaded(test)]
561 async fn test_stop() {
562 let moniker = Moniker::parse_str("core/foo").unwrap();
563 let lc = lifecycle_stop("core/foo");
564 stop_instance(&lc, &moniker).await.unwrap();
565 }
566
567 #[fuchsia_async::run_singlethreaded(test)]
568 async fn test_resolve() {
569 let moniker = Moniker::parse_str("core/foo").unwrap();
570 let lc = lifecycle_resolve("core/foo");
571 resolve_instance(&lc, &moniker).await.unwrap();
572 }
573
574 #[fuchsia_async::run_singlethreaded(test)]
575 async fn test_unresolve() {
576 let moniker = Moniker::parse_str("core/foo").unwrap();
577 let lc = lifecycle_unresolve("core/foo");
578 unresolve_instance(&lc, &moniker).await.unwrap();
579 }
580
581 #[fuchsia_async::run_singlethreaded(test)]
582 async fn test_instance_not_found() {
583 let moniker = Moniker::parse_str("core/foo").unwrap();
584 let lc = lifecycle_start_fail(fsys::StartError::InstanceNotFound);
585 match start_instance_with_args(&lc, &moniker, fcomponent::StartChildArgs::default()).await {
586 Ok(_) => panic!("start shouldn't succeed"),
587 Err(StartError::ActionError(ActionError::InstanceNotFound)) => {}
588 Err(e) => panic!("start failed unexpectedly: {}", e),
589 }
590 }
591}