blob: 464fba9d28427559c8894d5a45ba82f10da9e348 [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::*,
channel,
model::addable_directory::AddableDirectoryWithResult,
model::{
error::ModelError,
hooks::{Event, EventPayload, EventType, Hook, HooksRegistration},
},
},
async_trait::async_trait,
directory_broker,
fidl::endpoints::{ClientEnd, ServerEnd},
fidl_fuchsia_io::DirectoryMarker,
fuchsia_zircon as zx,
futures::{executor::block_on, lock::Mutex, prelude::*},
moniker::AbsoluteMoniker,
std::{
cmp::Eq,
collections::HashMap,
fmt,
ops::Deref,
path::PathBuf,
pin::Pin,
sync::{Arc, Weak},
},
vfs::{
directory::entry::DirectoryEntry, directory::immutable::simple as pfs,
execution_scope::ExecutionScope, path::Path as pfsPath,
},
};
struct ComponentInstance {
pub abs_moniker: AbsoluteMoniker,
pub children: Mutex<Vec<Arc<ComponentInstance>>>,
}
impl Clone for ComponentInstance {
// This is used by TestHook. ComponentInstance is immutable so when a change
// needs to be made, TestHook clones ComponentInstance and makes the change
// in the new copy.
fn clone(&self) -> Self {
let children = block_on(self.children.lock());
return ComponentInstance {
abs_moniker: self.abs_moniker.clone(),
children: Mutex::new(children.clone()),
};
}
}
impl PartialEq for ComponentInstance {
fn eq(&self, other: &Self) -> bool {
self.abs_moniker == other.abs_moniker
}
}
impl Eq for ComponentInstance {}
impl ComponentInstance {
pub async fn print(&self) -> String {
let mut s: String =
self.abs_moniker.leaf().map_or(String::new(), |m| format!("{}", m.to_partial()));
let mut children = self.children.lock().await;
if children.is_empty() {
return s;
}
// The position of a child in the children vector is a function of timing.
// In order to produce stable topology strings across runs, we sort the set
// of children here by moniker.
children.sort_by(|a, b| a.abs_moniker.cmp(&b.abs_moniker));
s.push('(');
let mut count = 0;
for child in children.iter() {
// If we've seen a previous child, then add a comma to separate children.
if count > 0 {
s.push(',');
}
s.push_str(&child.boxed_print().await);
count += 1;
}
s.push(')');
s
}
fn boxed_print<'a>(&'a self) -> Pin<Box<dyn Future<Output = String> + 'a>> {
Box::pin(self.print())
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Lifecycle {
Bind(AbsoluteMoniker),
Stop(AbsoluteMoniker),
PreDestroy(AbsoluteMoniker),
Destroy(AbsoluteMoniker),
}
impl fmt::Display for Lifecycle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Lifecycle::Bind(m) => write!(f, "bind({})", m),
Lifecycle::Stop(m) => write!(f, "stop({})", m),
Lifecycle::PreDestroy(m) => write!(f, "predestroy({})", m),
Lifecycle::Destroy(m) => write!(f, "destroy({})", m),
}
}
}
/// TestHook is a Hook that generates a strings representing the component
/// topology.
pub struct TestHook {
instances: Mutex<HashMap<AbsoluteMoniker, Arc<ComponentInstance>>>,
lifecycle_events: Mutex<Vec<Lifecycle>>,
}
impl fmt::Display for TestHook {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}", self.print())?;
Ok(())
}
}
impl TestHook {
pub fn new() -> TestHook {
Self { instances: Mutex::new(HashMap::new()), lifecycle_events: Mutex::new(vec![]) }
}
/// Returns the set of hooks into the component manager that TestHook is interested in.
pub fn hooks(self: &Arc<Self>) -> Vec<HooksRegistration> {
vec![HooksRegistration::new(
"TestHook",
vec![
EventType::Discovered,
EventType::Destroyed,
EventType::MarkedForDestruction,
EventType::Started,
EventType::Stopped,
],
Arc::downgrade(self) as Weak<dyn Hook>,
)]
}
/// Recursively traverse the Instance tree to generate a string representing the component
/// topology.
pub fn print(&self) -> String {
let instances = block_on(self.instances.lock());
let abs_moniker = AbsoluteMoniker::root();
let root_instance =
instances.get(&abs_moniker).map(|x| x.clone()).expect("Unable to find root instance.");
block_on(root_instance.print())
}
/// Return the sequence of lifecycle events.
pub fn lifecycle(&self) -> Vec<Lifecycle> {
block_on(self.lifecycle_events.lock()).clone()
}
pub async fn on_started_async<'a>(
&'a self,
target_moniker: &AbsoluteMoniker,
) -> Result<(), ModelError> {
self.create_instance_if_necessary(target_moniker).await?;
let mut events = self.lifecycle_events.lock().await;
events.push(Lifecycle::Bind(target_moniker.clone()));
Ok(())
}
pub async fn on_stopped_async<'a>(
&'a self,
target_moniker: &AbsoluteMoniker,
) -> Result<(), ModelError> {
let mut events = self.lifecycle_events.lock().await;
events.push(Lifecycle::Stop(target_moniker.clone()));
Ok(())
}
pub async fn on_marked_for_destruction_async<'a>(
&'a self,
target_moniker: &AbsoluteMoniker,
) -> Result<(), ModelError> {
// TODO: Can this be changed not restrict to dynamic instances? Static instances can be
// deleted too.
if let Some(child_moniker) = target_moniker.leaf() {
if child_moniker.collection().is_some() {
self.remove_instance(target_moniker).await?;
}
}
let mut events = self.lifecycle_events.lock().await;
events.push(Lifecycle::PreDestroy(target_moniker.clone()));
Ok(())
}
pub async fn on_destroyed_async<'a>(
&'a self,
target_moniker: &AbsoluteMoniker,
) -> Result<(), ModelError> {
let mut events = self.lifecycle_events.lock().await;
events.push(Lifecycle::Destroy(target_moniker.clone()));
Ok(())
}
pub async fn create_instance_if_necessary(
&self,
abs_moniker: &AbsoluteMoniker,
) -> Result<(), ModelError> {
let mut instances = self.instances.lock().await;
let new_instance = match instances.get(abs_moniker) {
Some(old_instance) => Arc::new((old_instance.deref()).clone()),
None => Arc::new(ComponentInstance {
abs_moniker: abs_moniker.clone(),
children: Mutex::new(vec![]),
}),
};
instances.insert(abs_moniker.clone(), new_instance.clone());
if let Some(parent_moniker) = abs_moniker.parent() {
// If the parent isn't available yet then opt_parent_instance will have a value
// of None.
let opt_parent_instance = instances.get(&parent_moniker).map(|x| x.clone());
// If the parent is available then add this instance as a child to it.
if let Some(parent_instance) = opt_parent_instance {
let mut children = parent_instance.children.lock().await;
let opt_index =
children.iter().position(|c| c.abs_moniker == new_instance.abs_moniker);
if let Some(index) = opt_index {
children.remove(index);
}
children.push(new_instance.clone());
}
}
Ok(())
}
pub async fn remove_instance(&self, abs_moniker: &AbsoluteMoniker) -> Result<(), ModelError> {
let mut instances = self.instances.lock().await;
if let Some(parent_moniker) = abs_moniker.parent() {
instances.remove(&abs_moniker);
let parent_instance = instances
.get(&parent_moniker)
.expect(&format!("parent instance {} not found", parent_moniker));
let mut children = parent_instance.children.lock().await;
let opt_index = children.iter().position(|c| c.abs_moniker == *abs_moniker);
if let Some(index) = opt_index {
children.remove(index);
}
}
Ok(())
}
}
#[async_trait]
impl Hook for TestHook {
async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError> {
match &event.result {
Ok(EventPayload::Destroyed) => {
self.on_destroyed_async(&event.target_moniker).await?;
}
Ok(EventPayload::Discovered { .. }) => {
self.create_instance_if_necessary(&event.target_moniker).await?;
}
Ok(EventPayload::MarkedForDestruction) => {
self.on_marked_for_destruction_async(&event.target_moniker).await?;
}
Ok(EventPayload::Started { .. }) => {
self.on_started_async(&event.target_moniker).await?;
}
Ok(EventPayload::Stopped { .. }) => {
self.on_stopped_async(&event.target_moniker).await?;
}
_ => (),
};
Ok(())
}
}
pub struct HubInjectionTestHook;
impl HubInjectionTestHook {
pub fn new() -> Self {
Self
}
pub fn hooks(self: &Arc<Self>) -> Vec<HooksRegistration> {
vec![HooksRegistration::new(
"TestHook",
vec![EventType::CapabilityRouted],
Arc::downgrade(self) as Weak<dyn Hook>,
)]
}
pub async fn on_scoped_framework_capability_routed_async<'a>(
&'a self,
scope_moniker: AbsoluteMoniker,
capability: &'a InternalCapability,
mut capability_provider: Option<Box<dyn CapabilityProvider>>,
) -> Result<Option<Box<dyn CapabilityProvider>>, ModelError> {
// This Hook is about injecting itself between the Hub and the Model.
// If the Hub hasn't been installed, then there's nothing to do here.
match (&capability_provider, capability) {
(Some(_), InternalCapability::Directory(source_name)) => {
if source_name.str() != "hub" {
return Ok(capability_provider);
}
}
_ => return Ok(capability_provider),
};
Ok(Some(Box::new(HubInjectionCapabilityProvider::new(
scope_moniker,
capability_provider.take().expect("Unable to take original capability."),
))))
}
}
#[async_trait]
impl Hook for HubInjectionTestHook {
async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError> {
if let Ok(EventPayload::CapabilityRouted {
source: CapabilitySource::Framework { capability, scope_moniker },
capability_provider,
}) = &event.result
{
let mut capability_provider = capability_provider.lock().await;
*capability_provider = self
.on_scoped_framework_capability_routed_async(
scope_moniker.clone(),
capability,
capability_provider.take(),
)
.await?;
}
Ok(())
}
}
struct HubInjectionCapabilityProvider {
abs_moniker: AbsoluteMoniker,
intercepted_capability: Box<dyn CapabilityProvider>,
}
impl HubInjectionCapabilityProvider {
pub fn new(
abs_moniker: AbsoluteMoniker,
intercepted_capability: Box<dyn CapabilityProvider>,
) -> Self {
HubInjectionCapabilityProvider { abs_moniker, intercepted_capability }
}
}
#[async_trait]
impl CapabilityProvider for HubInjectionCapabilityProvider {
async fn open(
self: Box<Self>,
flags: u32,
open_mode: u32,
relative_path: PathBuf,
server_end: &mut zx::Channel,
) -> Result<(), ModelError> {
let (client_chan, mut server_chan) = zx::Channel::create().unwrap();
self.intercepted_capability
.open(flags, open_mode, PathBuf::new(), &mut server_chan)
.await?;
let hub_proxy = ClientEnd::<DirectoryMarker>::new(client_chan)
.into_proxy()
.expect("failed to create directory proxy");
let dir = pfs::simple();
dir.add_node(
"old_hub",
directory_broker::DirectoryBroker::from_directory_proxy(hub_proxy),
&self.abs_moniker,
)?;
let mut relative_path = relative_path.to_str().expect("path is not utf8").to_string();
relative_path.push('/');
let path =
pfsPath::validate_and_split(relative_path).expect("failed to split and validate path");
let server_end = channel::take_channel(server_end);
dir.open(ExecutionScope::new(), flags, open_mode, path, ServerEnd::new(server_end));
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[fuchsia_async::run_singlethreaded(test)]
async fn test_hook_test() {
let root = AbsoluteMoniker::root();
let a: AbsoluteMoniker = vec!["a:0"].into();
let ab: AbsoluteMoniker = vec!["a:0", "b:0"].into();
let ac: AbsoluteMoniker = vec!["a:0", "c:0"].into();
let abd: AbsoluteMoniker = vec!["a:0", "b:0", "d:0"].into();
let abe: AbsoluteMoniker = vec!["a:0", "b:0", "e:0"].into();
let acf: AbsoluteMoniker = vec!["a:0", "c:0", "f:0"].into();
// Try adding parent followed by children then verify the topology string
// is correct.
{
let test_hook = TestHook::new();
assert!(test_hook.create_instance_if_necessary(&root).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&a).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&ab).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&ac).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&abd).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&abe).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&acf).await.is_ok());
assert_eq!("(a(b(d,e),c(f)))", test_hook.print());
}
// Changing the order of monikers should not affect the output string.
{
let test_hook = TestHook::new();
assert!(test_hook.create_instance_if_necessary(&root).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&a).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&ac).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&ab).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&abd).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&abe).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&acf).await.is_ok());
assert_eq!("(a(b(d,e),c(f)))", test_hook.print());
}
// Submitting children before parents should still succeed.
{
let test_hook = TestHook::new();
assert!(test_hook.create_instance_if_necessary(&root).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&acf).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&abe).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&abd).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&ab).await.is_ok());
// Model will call create_instance_if_necessary for ab's children again
// after the call to bind_instance for ab.
assert!(test_hook.create_instance_if_necessary(&abe).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&abd).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&ac).await.is_ok());
// Model will call create_instance_if_necessary for ac's children again
// after the call to bind_instance for ac.
assert!(test_hook.create_instance_if_necessary(&acf).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&a).await.is_ok());
// Model will call create_instance_if_necessary for a's children again
// after the call to bind_instance for a.
assert!(test_hook.create_instance_if_necessary(&ab).await.is_ok());
assert!(test_hook.create_instance_if_necessary(&ac).await.is_ok());
assert_eq!("(a(b(d,e),c(f)))", test_hook.print());
}
}
}