| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::capability::NamespaceCapabilities, |
| crate::config::RuntimeConfig, |
| crate::model::{ |
| actions::ActionKey, |
| binding::Binder, |
| context::ModelContext, |
| environment::Environment, |
| error::ModelError, |
| realm::{BindReason, ComponentManagerRealm, Realm}, |
| }, |
| moniker::AbsoluteMoniker, |
| std::sync::Arc, |
| }; |
| |
| /// Parameters for initializing a component model, particularly the root of the component |
| /// instance tree. |
| pub struct ModelParams { |
| // TODO(viktard): Merge into RuntimeConfig |
| /// The URL of the root component. |
| pub root_component_url: String, |
| /// The environment provided to the root realm. |
| pub root_environment: Environment, |
| /// Global runtime configuration for the component_manager. |
| pub runtime_config: Arc<RuntimeConfig>, |
| /// The namespace capabilities offered by component manager |
| pub namespace_capabilities: NamespaceCapabilities, |
| } |
| |
| /// The component model holds authoritative state about a tree of component instances, including |
| /// each instance's identity, lifecycle, capabilities, and topological relationships. It also |
| /// provides operations for instantiating, destroying, querying, and controlling component |
| /// instances at runtime. |
| pub struct Model { |
| pub root_realm: Arc<Realm>, |
| _context: Arc<ModelContext>, |
| _component_manager_realm: Arc<ComponentManagerRealm>, |
| } |
| |
| impl Model { |
| /// Creates a new component model and initializes its topology. |
| pub async fn new(params: ModelParams) -> Result<Model, ModelError> { |
| let component_manager_realm = |
| Arc::new(ComponentManagerRealm::new(params.namespace_capabilities)); |
| let context = Arc::new(ModelContext::new(params.runtime_config).await?); |
| let root_realm = Arc::new(Realm::new_root_realm( |
| params.root_environment, |
| Arc::downgrade(&context), |
| Arc::downgrade(&component_manager_realm), |
| params.root_component_url, |
| )); |
| Ok(Model { |
| root_realm, |
| _context: context, |
| _component_manager_realm: component_manager_realm, |
| }) |
| } |
| |
| /// Looks up a realm by absolute moniker. The component instance in the realm will be resolved |
| /// if that has not already happened. |
| pub async fn look_up_realm( |
| &self, |
| look_up_abs_moniker: &AbsoluteMoniker, |
| ) -> Result<Arc<Realm>, ModelError> { |
| let mut cur_realm = self.root_realm.clone(); |
| for moniker in look_up_abs_moniker.path().iter() { |
| cur_realm = { |
| let cur_state = cur_realm.lock_resolved_state().await?; |
| if let Some(r) = cur_state.all_child_realms().get(moniker) { |
| r.clone() |
| } else { |
| return Err(ModelError::instance_not_found(look_up_abs_moniker.clone())); |
| } |
| }; |
| } |
| let _ = cur_realm.lock_resolved_state().await?; |
| Ok(cur_realm) |
| } |
| |
| /// Binds to the root realm, starting the component tree |
| pub async fn start(self: &Arc<Model>) { |
| let root_moniker = AbsoluteMoniker::root(); |
| if let Err(e) = self.bind(&root_moniker, &BindReason::Root).await { |
| // If we fail binding to the root realm, but the root realm is being shutdown, that's |
| // ok. The system is tearing down, so it doesn't matter any more if we never got |
| // everything started that we wanted to. |
| let action_set = self.root_realm.lock_actions().await; |
| if !action_set.contains(&ActionKey::Shutdown) { |
| panic!( |
| "failed to bind to root component {}: {:?}", |
| self.root_realm.component_url, e |
| ); |
| } |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| pub mod tests { |
| use { |
| crate::{ |
| config::RuntimeConfig, |
| model::actions::ShutdownAction, |
| model::testing::test_helpers::{new_test_model, ComponentDeclBuilder, TestModelResult}, |
| }, |
| fidl_fuchsia_sys2 as fsys, fuchsia_async as fasync, |
| }; |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn shutting_down_when_start_fails() { |
| let components = vec![( |
| "root", |
| ComponentDeclBuilder::new() |
| .add_child(cm_rust::ChildDecl { |
| name: "bad-scheme".to_string(), |
| url: "bad-scheme://sdf".to_string(), |
| startup: fsys::StartupMode::Eager, |
| environment: None, |
| }) |
| .build(), |
| )]; |
| |
| let TestModelResult { model, .. } = |
| new_test_model("root", components, RuntimeConfig::default()).await; |
| |
| let _ = model |
| .root_realm |
| .lock_actions() |
| .await |
| .register_inner(&model.root_realm, ShutdownAction::new()); |
| |
| model.start().await; |
| } |
| |
| #[should_panic] |
| #[fasync::run_singlethreaded(test)] |
| async fn not_shutting_down_when_start_fails() { |
| let components = vec![( |
| "root", |
| ComponentDeclBuilder::new() |
| .add_child(cm_rust::ChildDecl { |
| name: "bad-scheme".to_string(), |
| url: "bad-scheme://sdf".to_string(), |
| startup: fsys::StartupMode::Eager, |
| environment: None, |
| }) |
| .build(), |
| )]; |
| |
| let TestModelResult { model, .. } = |
| new_test_model("root", components, RuntimeConfig::default()).await; |
| |
| model.start().await; |
| } |
| } |