blob: fa19ecdf3e7983a72cde511e23f47dc6fe920950 [file] [log] [blame]
// 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;
}
}