blob: 1bb13aaa32dc4cdee40e6a8864b6ece30102e656 [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::verify::collection::V2ComponentModel,
anyhow::{anyhow, Context, Result},
cm_fidl_analyzer::{
component_instance::ComponentInstanceForAnalyzer,
component_model::ComponentModelForAnalyzer, BreadthFirstModelWalker,
ComponentInstanceVisitor, ComponentModelWalker,
},
cm_rust::{CapabilityName, UseDecl},
moniker::AbsoluteMonikerBase,
routing::{
component_instance::{ComponentInstanceInterface, ExtendedInstanceInterface},
RouteSource,
},
scrutiny::{model::controller::DataController, model::model::*},
serde::{Deserialize, Serialize},
serde_json::{json, value::Value},
std::{collections::HashSet, path::PathBuf, sync::Arc},
};
/// ComponentResolversController
///
/// A DataController which returns a list component node paths of all
/// components that, in their environment, contain a resolver with the
/// given moniker for a scheme with access to a protocol.
#[derive(Default)]
pub struct ComponentResolversController {}
/// The expected query format.
#[derive(Debug, Deserialize, Serialize)]
pub struct ComponentResolverRequest {
/// `resolver` URI scheme of interest
pub scheme: String,
/// Node path of the `resolver`
pub moniker: String,
/// Filter the results to components resolved with a `resolver` with access to a protocol
pub protocol: String,
}
/// The response schema.
#[derive(Debug, Deserialize, Serialize)]
pub struct ComponentResolverResponse {
/// Files accessed to perform this query, for depfile generation.
pub deps: HashSet<PathBuf>,
/// Component monikers that matched the query.
pub monikers: Vec<String>,
}
/// Walks the tree for component node paths of all components that, in their
/// environment, contain a resolver with the given moniker for a scheme
/// with access to a protocol.
/// `monikers` contains the components which match the `request` parameters.
struct ComponentResolversVisitor {
request: ComponentResolverRequest,
monikers: Vec<String>,
}
impl ComponentResolversVisitor {
fn new(request: ComponentResolverRequest) -> Self {
let monikers = Vec::new();
Self { request, monikers }
}
fn get_monikers(&self) -> Vec<String> {
self.monikers.clone()
}
fn check_instance(&mut self, instance: &Arc<ComponentInstanceForAnalyzer>) -> Result<()> {
if let Ok(Some((
ExtendedInstanceInterface::Component(resolver_register_instance),
resolver,
))) = instance.environment().get_registered_resolver(&self.request.scheme)
{
// The resolver is a capability and we need to get the component that provides it.
let resolver_source = match ComponentModelForAnalyzer::route_capability_sync(
routing::RouteRequest::Resolver(resolver),
&resolver_register_instance,
) {
Ok((source, _route)) => {
match source {
RouteSource::Resolver(resolver) => {
match resolver.source_instance().upgrade()? {
routing::component_instance::ExtendedInstanceInterface::Component(
component,
) => component,
routing::component_instance::ExtendedInstanceInterface::AboveRoot(..) => {
return Err(anyhow!("The plugin is unable to verify resolvers declared above the root."));
}
}
}
_ => {
unreachable!("We only care about resolvers!")
}
}
}
Err(err) => {
eprintln!(
"Ignoring invalid resolver configuration for {}: {:#}",
instance.abs_moniker(),
anyhow!(err).context("failed to route to a resolver")
);
return Ok(());
}
};
let moniker = moniker::AbsoluteMoniker::parse_str(&self.request.moniker)?;
if *resolver_source.abs_moniker() == moniker {
for use_decl in &resolver_source.decl_for_testing().uses {
if let UseDecl::Protocol(name) = use_decl {
if name.source_name == CapabilityName(self.request.protocol.clone()) {
self.monikers.push(instance.abs_moniker().to_string());
}
}
}
}
}
Ok(())
}
}
impl ComponentInstanceVisitor for ComponentResolversVisitor {
fn visit_instance(&mut self, instance: &Arc<ComponentInstanceForAnalyzer>) -> Result<()> {
self.check_instance(instance)
.with_context(|| format!("while visiting {}", instance.abs_moniker()))
}
}
impl DataController for ComponentResolversController {
fn query(&self, model: Arc<DataModel>, request: Value) -> Result<Value> {
let tree_data = model
.get::<V2ComponentModel>()
.context("Failed to get V2ComponentModel from ComponentResolversController model")?;
let deps = tree_data.deps.clone();
let controller: ComponentResolverRequest = serde_json::from_value(request)?;
let model = &tree_data.component_model;
let mut walker = BreadthFirstModelWalker::new();
let mut visitor = ComponentResolversVisitor::new(controller);
walker.walk(&model, &mut visitor).context(
"Failed to walk V2ComponentModel with BreadthFirstWalker and ComponentResolversVisitor",
)?;
Ok(json!(ComponentResolverResponse { monikers: visitor.get_monikers(), deps }))
}
fn description(&self) -> String {
"Finds components resolved by a particular resolver".to_string()
}
fn usage(&self) -> String {
"Finds the component node paths of all components that, in their
environment, contain a resolver with the given moniker for scheme with
access to protocol.
Required parameters:
--scheme: the resolver URI scheme to query
--moniker: the node path of the resolver
--resolver: filter results to components resolved with a resolver that has access to the given protocol"
.to_string()
}
}