1use cm_types::{BorrowedLongName, BorrowedName};
6use flex_client::ProxyHasDomain;
7use flex_fuchsia_component as fcomponent;
8use flex_fuchsia_component_decl as fdecl;
9use flex_fuchsia_sys2 as fsys;
10use fuchsia_url::fuchsia_pkg::AbsoluteComponentUrl;
11use futures::future::BoxFuture;
12use futures::{FutureExt, StreamExt};
13use moniker::Moniker;
14use thiserror::Error;
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: &BorrowedName,
79 child_name: &BorrowedLongName,
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.as_ref(),
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: &BorrowedName,
122 child_name: &BorrowedLongName,
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.as_ref(), &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) = lifecycle_controller.domain().create_proxy::<fcomponent::BinderMarker>();
155 lifecycle_controller
156 .start_instance(moniker.as_ref(), 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) = lifecycle_controller.domain().create_proxy::<fcomponent::BinderMarker>();
188 lifecycle_controller
189 .start_instance_with_args(moniker.as_ref(), 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.as_ref())
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.as_ref())
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.as_ref())
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 flex_fuchsia_process as fprocess;
276 use futures::TryStreamExt;
277
278 fn lifecycle_create_instance(
279 expected_moniker: &'static str,
280 expected_collection: &'static str,
281 expected_name: &'static str,
282 expected_url: &'static str,
283 expected_numbered_handle_count: usize,
284 ) -> fsys::LifecycleControllerProxy {
285 let (lifecycle_controller, mut stream) =
286 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
287 fuchsia_async::Task::local(async move {
288 let req = stream.try_next().await.unwrap().unwrap();
289 match req {
290 fsys::LifecycleControllerRequest::CreateInstance {
291 parent_moniker,
292 collection,
293 decl,
294 args,
295 responder,
296 ..
297 } => {
298 assert_eq!(
299 Moniker::parse_str(expected_moniker),
300 Moniker::parse_str(&parent_moniker)
301 );
302 assert_eq!(expected_collection, collection.name);
303 assert_eq!(expected_name, decl.name.unwrap());
304 assert_eq!(expected_url, decl.url.unwrap());
305 assert_eq!(
306 expected_numbered_handle_count,
307 args.numbered_handles.unwrap_or(vec![]).len()
308 );
309 responder.send(Ok(())).unwrap();
310 }
311 _ => panic!("Unexpected Lifecycle Controller request"),
312 }
313 })
314 .detach();
315 lifecycle_controller
316 }
317
318 fn lifecycle_destroy_instance(
319 expected_moniker: &'static str,
320 expected_collection: &'static str,
321 expected_name: &'static str,
322 ) -> fsys::LifecycleControllerProxy {
323 let (lifecycle_controller, mut stream) =
324 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
325 fuchsia_async::Task::local(async move {
326 let req = stream.try_next().await.unwrap().unwrap();
327 match req {
328 fsys::LifecycleControllerRequest::DestroyInstance {
329 parent_moniker,
330 child,
331 responder,
332 ..
333 } => {
334 assert_eq!(
335 Moniker::parse_str(expected_moniker),
336 Moniker::parse_str(&parent_moniker)
337 );
338 assert_eq!(expected_name, child.name);
339 assert_eq!(expected_collection, child.collection.unwrap());
340 responder.send(Ok(())).unwrap();
341 }
342 _ => panic!("Unexpected Lifecycle Controller request"),
343 }
344 })
345 .detach();
346 lifecycle_controller
347 }
348
349 fn lifecycle_start(expected_moniker: &'static str) -> fsys::LifecycleControllerProxy {
350 let (lifecycle_controller, mut stream) =
351 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
352 fuchsia_async::Task::local(async move {
353 let req = stream.try_next().await.unwrap().unwrap();
354 match req {
355 fsys::LifecycleControllerRequest::StartInstanceWithArgs {
356 moniker,
357 responder,
358 ..
359 } => {
360 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
361 responder.send(Ok(())).unwrap();
362 }
363 _ => panic!("Unexpected Lifecycle Controller request"),
364 }
365 })
366 .detach();
367 lifecycle_controller
368 }
369
370 fn lifecycle_stop(expected_moniker: &'static str) -> fsys::LifecycleControllerProxy {
371 let (lifecycle_controller, mut stream) =
372 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
373 fuchsia_async::Task::local(async move {
374 let req = stream.try_next().await.unwrap().unwrap();
375 match req {
376 fsys::LifecycleControllerRequest::StopInstance { moniker, responder, .. } => {
377 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
378 responder.send(Ok(())).unwrap();
379 }
380 _ => panic!("Unexpected Lifecycle Controller request"),
381 }
382 })
383 .detach();
384 lifecycle_controller
385 }
386
387 fn lifecycle_resolve(expected_moniker: &'static str) -> fsys::LifecycleControllerProxy {
388 let (lifecycle_controller, mut stream) =
389 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
390 fuchsia_async::Task::local(async move {
391 let req = stream.try_next().await.unwrap().unwrap();
392 match req {
393 fsys::LifecycleControllerRequest::ResolveInstance {
394 moniker, responder, ..
395 } => {
396 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
397 responder.send(Ok(())).unwrap();
398 }
399 _ => panic!("Unexpected Lifecycle Controller request"),
400 }
401 })
402 .detach();
403 lifecycle_controller
404 }
405
406 fn lifecycle_unresolve(expected_moniker: &'static str) -> fsys::LifecycleControllerProxy {
407 let (lifecycle_controller, mut stream) =
408 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
409 fuchsia_async::Task::local(async move {
410 let req = stream.try_next().await.unwrap().unwrap();
411 match req {
412 fsys::LifecycleControllerRequest::UnresolveInstance {
413 moniker, responder, ..
414 } => {
415 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
416 responder.send(Ok(())).unwrap();
417 }
418 _ => panic!("Unexpected Lifecycle Controller request"),
419 }
420 })
421 .detach();
422 lifecycle_controller
423 }
424
425 fn lifecycle_create_fail(error: fsys::CreateError) -> fsys::LifecycleControllerProxy {
426 let (lifecycle_controller, mut stream) =
427 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
428 fuchsia_async::Task::local(async move {
429 let req = stream.try_next().await.unwrap().unwrap();
430 match req {
431 fsys::LifecycleControllerRequest::CreateInstance { responder, .. } => {
432 responder.send(Err(error)).unwrap();
433 }
434 _ => panic!("Unexpected Lifecycle Controller request"),
435 }
436 })
437 .detach();
438 lifecycle_controller
439 }
440
441 fn lifecycle_start_fail(error: fsys::StartError) -> fsys::LifecycleControllerProxy {
442 let (lifecycle_controller, mut stream) =
443 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
444 fuchsia_async::Task::local(async move {
445 let req = stream.try_next().await.unwrap().unwrap();
446 match req {
447 fsys::LifecycleControllerRequest::StartInstanceWithArgs { responder, .. } => {
448 responder.send(Err(error)).unwrap();
449 }
450 _ => panic!("Unexpected Lifecycle Controller request"),
451 }
452 })
453 .detach();
454 lifecycle_controller
455 }
456
457 #[fuchsia_async::run_singlethreaded(test)]
458 async fn test_create_child() {
459 let parent = Moniker::parse_str("core").unwrap();
460 let url =
461 AbsoluteComponentUrl::parse("fuchsia-pkg://fuchsia.com/test#meta/test.cm").unwrap();
462 let lc = lifecycle_create_instance(
463 "/core",
464 "foo",
465 "bar",
466 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
467 0,
468 );
469 create_instance_in_collection(
470 &lc,
471 &parent,
472 BorrowedName::new("foo").unwrap(),
473 BorrowedLongName::new("bar").unwrap(),
474 &url,
475 vec![],
476 None,
477 )
478 .await
479 .unwrap();
480 }
481
482 #[fuchsia_async::run_singlethreaded(test)]
483 async fn test_create_child_with_numbered_handles() {
484 let parent = Moniker::parse_str("core").unwrap();
485 let url =
486 AbsoluteComponentUrl::parse("fuchsia-pkg://fuchsia.com/test#meta/test.cm").unwrap();
487 let lc = lifecycle_create_instance(
488 "/core",
489 "foo",
490 "bar",
491 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
492 2,
493 );
494 let (left, right) = fidl::Socket::create_stream();
495 let child_args = fcomponent::CreateChildArgs {
496 numbered_handles: Some(vec![
497 fprocess::HandleInfo { handle: left.into_handle(), id: 0x10 },
498 fprocess::HandleInfo { handle: right.into_handle(), id: 0x11 },
499 ]),
500 ..Default::default()
501 };
502 create_instance_in_collection(
503 &lc,
504 &parent,
505 BorrowedName::new("foo").unwrap(),
506 BorrowedLongName::new("bar").unwrap(),
507 &url,
508 vec![],
509 Some(child_args),
510 )
511 .await
512 .unwrap();
513 }
514
515 #[fuchsia_async::run_singlethreaded(test)]
516 async fn test_create_already_exists() {
517 let parent = Moniker::parse_str("core").unwrap();
518 let url =
519 AbsoluteComponentUrl::parse("fuchsia-pkg://fuchsia.com/test#meta/test.cm").unwrap();
520 let lc = lifecycle_create_fail(fsys::CreateError::InstanceAlreadyExists);
521 let err = create_instance_in_collection(
522 &lc,
523 &parent,
524 BorrowedName::new("foo").unwrap(),
525 BorrowedLongName::new("bar").unwrap(),
526 &url,
527 vec![],
528 None,
529 )
530 .await
531 .unwrap_err();
532 assert_matches!(err, CreateError::InstanceAlreadyExists);
533 }
534
535 #[fuchsia_async::run_singlethreaded(test)]
536 async fn test_destroy_child() {
537 let parent = Moniker::parse_str("core").unwrap();
538 let lc = lifecycle_destroy_instance("core", "foo", "bar");
539 destroy_instance_in_collection(
540 &lc,
541 &parent,
542 BorrowedName::new("foo").unwrap(),
543 BorrowedLongName::new("bar").unwrap(),
544 )
545 .await
546 .unwrap();
547 }
548
549 #[fuchsia_async::run_singlethreaded(test)]
550 async fn test_start() {
551 let moniker = Moniker::parse_str("core/foo").unwrap();
552 let lc = lifecycle_start("core/foo");
553 let _ = start_instance_with_args(&lc, &moniker, fcomponent::StartChildArgs::default())
554 .await
555 .unwrap();
556 }
557
558 #[fuchsia_async::run_singlethreaded(test)]
559 async fn test_stop() {
560 let moniker = Moniker::parse_str("core/foo").unwrap();
561 let lc = lifecycle_stop("core/foo");
562 stop_instance(&lc, &moniker).await.unwrap();
563 }
564
565 #[fuchsia_async::run_singlethreaded(test)]
566 async fn test_resolve() {
567 let moniker = Moniker::parse_str("core/foo").unwrap();
568 let lc = lifecycle_resolve("core/foo");
569 resolve_instance(&lc, &moniker).await.unwrap();
570 }
571
572 #[fuchsia_async::run_singlethreaded(test)]
573 async fn test_unresolve() {
574 let moniker = Moniker::parse_str("core/foo").unwrap();
575 let lc = lifecycle_unresolve("core/foo");
576 unresolve_instance(&lc, &moniker).await.unwrap();
577 }
578
579 #[fuchsia_async::run_singlethreaded(test)]
580 async fn test_instance_not_found() {
581 let moniker = Moniker::parse_str("core/foo").unwrap();
582 let lc = lifecycle_start_fail(fsys::StartError::InstanceNotFound);
583 match start_instance_with_args(&lc, &moniker, fcomponent::StartChildArgs::default()).await {
584 Ok(_) => panic!("start shouldn't succeed"),
585 Err(StartError::ActionError(ActionError::InstanceNotFound)) => {}
586 Err(e) => panic!("start failed unexpectedly: {}", e),
587 }
588 }
589}