blob: 4e137cc37b2165c7d608a208485f6d6e9d4fea4f [file] [log] [blame]
// Copyright 2021 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::{
component_model::{BuildAnalyzerModelError, Child},
environment::EnvironmentForAnalyzer,
node_path::NodePath,
route::RouteMapper,
},
async_trait::async_trait,
cm_moniker::{InstancedAbsoluteMoniker, InstancedChildMoniker},
cm_rust::{CapabilityDecl, CollectionDecl, ComponentDecl, ExposeDecl, OfferDecl, UseDecl},
moniker::{AbsoluteMoniker, AbsoluteMonikerBase, ChildMoniker, ChildMonikerBase},
routing::{
capability_source::{BuiltinCapabilities, NamespaceCapabilities},
component_id_index::ComponentIdIndex,
component_instance::{
ComponentInstanceInterface, ExtendedInstanceInterface, ResolvedInstanceInterface,
TopInstanceInterface, WeakExtendedInstanceInterface,
},
config::RuntimeConfig,
environment::{EnvironmentInterface, RunnerRegistry},
error::ComponentInstanceError,
policy::GlobalPolicyChecker,
resolving::{ComponentAddress, ComponentResolutionContext},
},
std::{
collections::HashMap,
sync::{Arc, RwLock},
},
};
/// A representation of a v2 component instance.
#[derive(Debug)]
pub struct ComponentInstanceForAnalyzer {
instanced_moniker: InstancedAbsoluteMoniker,
abs_moniker: AbsoluteMoniker,
pub(crate) decl: ComponentDecl,
url: String,
parent: WeakExtendedInstanceInterface<Self>,
children: RwLock<HashMap<ChildMoniker, Arc<Self>>>,
pub(crate) environment: Arc<EnvironmentForAnalyzer>,
policy_checker: GlobalPolicyChecker,
component_id_index: Arc<ComponentIdIndex>,
persistent_storage: bool,
}
impl ComponentInstanceForAnalyzer {
/// Exposes the component's ComponentDecl. This is referenced directly in tests.
pub fn decl_for_testing(&self) -> &ComponentDecl {
&self.decl
}
/// Returns a representation of the instance's position in the component instance tree.
pub fn node_path(&self) -> NodePath {
NodePath::absolute_from_vec(
self.abs_moniker.path().into_iter().map(|m| m.as_str()).collect(),
)
}
// Creates a new root component instance.
pub(crate) fn new_root(
decl: ComponentDecl,
url: String,
top_instance: Arc<TopInstanceForAnalyzer>,
runtime_config: Arc<RuntimeConfig>,
policy_checker: GlobalPolicyChecker,
component_id_index: Arc<ComponentIdIndex>,
runner_registry: RunnerRegistry,
persistent_storage: bool,
) -> Arc<Self> {
let environment =
EnvironmentForAnalyzer::new_root(runner_registry, &runtime_config, &top_instance);
let instanced_moniker = InstancedAbsoluteMoniker::root();
let abs_moniker = instanced_moniker.clone().without_instance_ids();
Arc::new(Self {
instanced_moniker,
abs_moniker,
decl,
url,
parent: WeakExtendedInstanceInterface::from(&ExtendedInstanceInterface::AboveRoot(
top_instance,
)),
children: RwLock::new(HashMap::new()),
environment,
policy_checker,
component_id_index,
persistent_storage,
})
}
// Creates a new non-root component instance as a child of `parent`.
pub(crate) fn new_for_child(
child: &Child,
absolute_url: String,
child_component_decl: ComponentDecl,
parent: Arc<Self>,
policy_checker: GlobalPolicyChecker,
component_id_index: Arc<ComponentIdIndex>,
) -> Result<Arc<Self>, BuildAnalyzerModelError> {
let environment = EnvironmentForAnalyzer::new_for_child(&parent, child)?;
let instanced_moniker = parent.instanced_moniker.child(InstancedChildMoniker::new(
child.child_moniker.name.clone(),
child.child_moniker.collection.clone(),
0,
));
let abs_moniker = instanced_moniker.clone().without_instance_ids();
let persistent_storage = parent.persistent_storage();
Ok(Arc::new(Self {
instanced_moniker,
abs_moniker,
decl: child_component_decl,
url: absolute_url,
parent: WeakExtendedInstanceInterface::from(&ExtendedInstanceInterface::Component(
parent,
)),
children: RwLock::new(HashMap::new()),
environment,
policy_checker,
component_id_index,
persistent_storage,
}))
}
// Returns all children of the component instance.
pub(crate) fn get_children(&self) -> Vec<Arc<Self>> {
self.children
.read()
.expect("failed to acquire read lock")
.values()
.map(|c| Arc::clone(c))
.collect()
}
// Adds a new child to this component instance.
pub(crate) fn add_child(&self, child_moniker: ChildMoniker, child: Arc<Self>) {
self.children.write().expect("failed to acquire write lock").insert(child_moniker, child);
}
// A (nearly) no-op sync function used to implement the async trait method `lock_resolved_instance`
// for `ComponentInstanceInterface`.
pub(crate) fn resolve<'a>(
self: &'a Arc<Self>,
) -> Result<Box<dyn ResolvedInstanceInterface<Component = Self> + 'a>, ComponentInstanceError>
{
Ok(Box::new(&**self))
}
pub fn environment(&self) -> &Arc<EnvironmentForAnalyzer> {
&self.environment
}
}
#[async_trait]
impl ComponentInstanceInterface for ComponentInstanceForAnalyzer {
type TopInstance = TopInstanceForAnalyzer;
type DebugRouteMapper = RouteMapper;
fn instanced_moniker(&self) -> &InstancedAbsoluteMoniker {
&self.instanced_moniker
}
fn abs_moniker(&self) -> &AbsoluteMoniker {
&self.abs_moniker
}
fn child_moniker(&self) -> Option<&ChildMoniker> {
self.abs_moniker.leaf()
}
fn instanced_child_moniker(&self) -> Option<&InstancedChildMoniker> {
self.instanced_moniker.leaf()
}
fn url(&self) -> &str {
&self.url
}
fn environment(&self) -> &dyn EnvironmentInterface<Self> {
self.environment.as_ref()
}
fn try_get_parent(&self) -> Result<ExtendedInstanceInterface<Self>, ComponentInstanceError> {
Ok(self.parent.upgrade()?)
}
fn try_get_policy_checker(&self) -> Result<GlobalPolicyChecker, ComponentInstanceError> {
Ok(self.policy_checker.clone())
}
fn try_get_component_id_index(&self) -> Result<Arc<ComponentIdIndex>, ComponentInstanceError> {
Ok(Arc::clone(&self.component_id_index))
}
// The trait definition requires this function to be async, but `ComponentInstanceForAnalyzer`'s
// implementation must not await. This method is called by `route_capability`, which must
// return immediately for `ComponentInstanceForAnalyzer` (see
// `ComponentModelForAnalyzer::route_capability_sync()`).
//
// TODO(fxbug.dev/87204): Remove this comment when Scrutiny's `DataController` can make async
// function calls.
async fn lock_resolved_state<'a>(
self: &'a Arc<Self>,
) -> Result<
Box<dyn ResolvedInstanceInterface<Component = ComponentInstanceForAnalyzer> + 'a>,
ComponentInstanceError,
> {
self.resolve()
}
fn new_route_mapper() -> RouteMapper {
RouteMapper::new()
}
fn persistent_storage(&self) -> bool {
self.persistent_storage
}
}
impl ResolvedInstanceInterface for ComponentInstanceForAnalyzer {
type Component = Self;
fn uses(&self) -> Vec<UseDecl> {
self.decl.uses.clone()
}
fn exposes(&self) -> Vec<ExposeDecl> {
self.decl.exposes.clone()
}
fn offers(&self) -> Vec<OfferDecl> {
self.decl.offers.clone()
}
fn capabilities(&self) -> Vec<CapabilityDecl> {
self.decl.capabilities.clone()
}
fn collections(&self) -> Vec<CollectionDecl> {
self.decl.collections.clone()
}
fn get_child(&self, moniker: &ChildMoniker) -> Option<Arc<Self>> {
self.children.read().expect("failed to acquire read lock").get(moniker).map(Arc::clone)
}
// This is a static model with no notion of a collection.
fn children_in_collection(&self, _collection: &str) -> Vec<(ChildMoniker, Arc<Self>)> {
vec![]
}
fn address(&self) -> ComponentAddress {
ComponentAddress::new_absolute("", "", "", None)
}
fn context_to_resolve_children(&self) -> Option<ComponentResolutionContext> {
None
}
}
/// A representation of `ComponentManager`'s instance, providing a set of capabilities to
/// the root component instance.
#[derive(Debug, Default)]
pub struct TopInstanceForAnalyzer {
namespace_capabilities: NamespaceCapabilities,
builtin_capabilities: BuiltinCapabilities,
}
impl TopInstanceForAnalyzer {
pub fn new(
namespace_capabilities: NamespaceCapabilities,
builtin_capabilities: BuiltinCapabilities,
) -> Arc<Self> {
Arc::new(Self { namespace_capabilities, builtin_capabilities })
}
}
impl TopInstanceInterface for TopInstanceForAnalyzer {
fn namespace_capabilities(&self) -> &NamespaceCapabilities {
&self.namespace_capabilities
}
fn builtin_capabilities(&self) -> &BuiltinCapabilities {
&self.builtin_capabilities
}
}
#[cfg(test)]
mod tests {
use {super::*, cm_rust_testing::ComponentDeclBuilder, futures::FutureExt};
// Spot-checks that `ComponentInstanceForAnalyzer`'s implementation of the `ComponentInstanceInterface`
// trait method `lock_resolved_state()` returns immediately. In addition, updates to that method should
// be reviewed to make sure that this property holds; otherwise, `ComponentModelForAnalyzer`'s sync
// methods may panic.
#[test]
fn lock_resolved_state_is_sync() {
let decl = ComponentDeclBuilder::new().build();
let url = "some_url".to_string();
let instance = ComponentInstanceForAnalyzer::new_root(
decl,
url,
TopInstanceForAnalyzer::new(vec![], vec![]),
Arc::new(RuntimeConfig::default()),
GlobalPolicyChecker::default(),
Arc::new(ComponentIdIndex::default()),
RunnerRegistry::default(),
false,
);
assert!(instance.lock_resolved_state().now_or_never().is_some())
}
}