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