[component_id_index] Realms can look up instance IDs
Specifically:
* component manager gets a path to the component ID index through
RuntimeConfig.component_id_index_path
* ModelContext parses & builds a ComponentIdIndex from
RuntimeConfig.component_id_index_path
Storage routing can now lookup instance_id when figuring out directory
structure (done in followup CL).
Change-Id: I2903ba00299eb431d4286c29fe8413e05393351d
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/460023
Commit-Queue: Vardhan Mudunuru <vardhan@google.com>
Reviewed-by: Gary Bressler <geb@google.com>
API-Review: Gary Bressler <geb@google.com>
Testability-Review: Gary Bressler <geb@google.com>
diff --git a/sdk/fidl/fuchsia.component.internal/config.fidl b/sdk/fidl/fuchsia.component.internal/config.fidl
index 9fb3632a..405ae07 100644
--- a/sdk/fidl/fuchsia.component.internal/config.fidl
+++ b/sdk/fidl/fuchsia.component.internal/config.fidl
@@ -60,6 +60,10 @@
/// is passed to component manager. If value is passed in both places, then
/// an error is raised.
10: string:fuchsia.component.MAX_URL_SCHEME_LENGTH root_component_url;
+
+ /// Path to the component ID index. An empty value defaults to an empty index.
+ /// An invalid index causes component_manager to abort.
+ 11: string:fuchsia.component.MAX_PATH_LENGTH component_id_index_path;
};
/// The builtin resolver to use, if any.
diff --git a/src/sys/component_manager/BUILD.gn b/src/sys/component_manager/BUILD.gn
index 5ccebccbe..03a93f0 100644
--- a/src/sys/component_manager/BUILD.gn
+++ b/src/sys/component_manager/BUILD.gn
@@ -48,6 +48,7 @@
"//src/sys/lib/cm_fidl_validator",
"//src/sys/lib/cm_rust",
"//src/sys/lib/cm_types",
+ "//src/sys/lib/component_id_index",
"//src/sys/lib/directory_broker",
"//src/sys/lib/moniker",
"//src/sys/lib/runner",
@@ -115,6 +116,7 @@
"src/model/actions/stop.rs",
"src/model/addable_directory.rs",
"src/model/binding.rs",
+ "src/model/component_id_index.rs",
"src/model/context.rs",
"src/model/dir_tree.rs",
"src/model/environment.rs",
diff --git a/src/sys/component_manager/src/builtin_environment.rs b/src/sys/component_manager/src/builtin_environment.rs
index 5e40959..bc7f82c 100644
--- a/src/sys/component_manager/src/builtin_environment.rs
+++ b/src/sys/component_manager/src/builtin_environment.rs
@@ -240,7 +240,7 @@
runtime_config: Arc::clone(&runtime_config),
namespace_capabilities: runtime_config.namespace_capabilities.clone(),
};
- let model = Arc::new(Model::new(params));
+ let model = Arc::new(Model::new(params).await?);
// If we previously created a resolver that requires the Model (in
// add_available_resolvers_from_namespace), send the just-created model to it.
diff --git a/src/sys/component_manager/src/config.rs b/src/sys/component_manager/src/config.rs
index c15536a..5ab0619 100644
--- a/src/sys/component_manager/src/config.rs
+++ b/src/sys/component_manager/src/config.rs
@@ -74,6 +74,10 @@
/// is passed to component manager. If value is passed in both places, then
/// an error is raised.
pub root_component_url: Option<Url>,
+
+ /// Path to the component ID index, parsed from
+ /// `fuchsia.component.internal.RuntimeConfig.component_id_index_path`.
+ pub component_id_index_path: Option<String>,
}
/// Runtime security policy.
@@ -142,6 +146,7 @@
builtin_pkg_resolver: BuiltinPkgResolver::None,
out_dir_contents: OutDirContents::None,
root_component_url: Default::default(),
+ component_id_index_path: None,
}
}
}
@@ -247,6 +252,7 @@
.unwrap_or(default.builtin_pkg_resolver),
out_dir_contents: config.out_dir_contents.unwrap_or(default.out_dir_contents),
root_component_url,
+ component_id_index_path: config.component_id_index_path,
})
}
}
@@ -397,6 +403,7 @@
builtin_pkg_resolver: None,
out_dir_contents: None,
root_component_url: None,
+ component_id_index_path: None,
..component_internal::Config::EMPTY
}, RuntimeConfig::default()),
all_leaf_nodes_none => (component_internal::Config {
@@ -418,6 +425,7 @@
namespace_capabilities: None,
out_dir_contents: None,
root_component_url: None,
+ component_id_index_path: None,
..component_internal::Config::EMPTY
}, RuntimeConfig {
debug:false, list_children_batch_size: 5,
@@ -482,6 +490,7 @@
]),
out_dir_contents: Some(component_internal::OutDirContents::Svc),
root_component_url: Some(FOO_PKG_URL.to_string()),
+ component_id_index_path: Some("/boot/config/component_id_index".to_string()),
..component_internal::Config::EMPTY
},
RuntimeConfig {
@@ -540,6 +549,7 @@
builtin_pkg_resolver: BuiltinPkgResolver::None,
out_dir_contents: OutDirContents::Svc,
root_component_url: Some(Url::new(FOO_PKG_URL.to_string()).unwrap()),
+ component_id_index_path: Some("/boot/config/component_id_index".to_string()),
}
),
}
@@ -564,6 +574,7 @@
namespace_capabilities: None,
out_dir_contents: None,
root_component_url: None,
+ component_id_index_path: None,
..component_internal::Config::EMPTY
}, MonikerError, MonikerError::InvalidMoniker {rep: "bad".to_string()}),
invalid_capability_policy_empty_allowlist_cap => (component_internal::Config {
@@ -592,6 +603,7 @@
namespace_capabilities: None,
out_dir_contents: None,
root_component_url: None,
+ component_id_index_path: None,
..component_internal::Config::EMPTY
}, PolicyConfigError, PolicyConfigError::EmptyAllowlistedCapability),
invalid_capability_policy_empty_source_moniker => (component_internal::Config {
@@ -619,6 +631,7 @@
namespace_capabilities: None,
out_dir_contents: None,
root_component_url: None,
+ component_id_index_path: None,
..component_internal::Config::EMPTY
}, PolicyConfigError, PolicyConfigError::EmptySourceMoniker),
invalid_root_component_url => (component_internal::Config {
@@ -632,6 +645,7 @@
namespace_capabilities: None,
out_dir_contents: None,
root_component_url: Some("invalid url".to_string()),
+ component_id_index_path: None,
..component_internal::Config::EMPTY
}, ParseError, ParseError::InvalidValue),
}
diff --git a/src/sys/component_manager/src/model/component_id_index.rs b/src/sys/component_manager/src/model/component_id_index.rs
new file mode 100644
index 0000000..bad147b
--- /dev/null
+++ b/src/sys/component_manager/src/model/component_id_index.rs
@@ -0,0 +1,152 @@
+// Copyright 2020 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 {
+ clonable_error::ClonableError,
+ component_id_index, fidl_fuchsia_component_internal as fcomponent_internal, io_util,
+ moniker::{AbsoluteMoniker, MonikerError},
+ std::collections::HashMap,
+ thiserror::Error,
+};
+
+pub type ComponentInstanceId = String;
+
+#[derive(Debug, Clone, Error)]
+pub enum ComponentIdIndexError {
+ #[error("could not read index file {}", .path)]
+ IndexUnreadable {
+ #[source]
+ err: ClonableError,
+ path: String,
+ },
+ #[error("Index error")]
+ IndexError(#[from] component_id_index::IndexError),
+ #[error("invalid moniker")]
+ MonikerError(#[from] MonikerError),
+}
+
+/// ComponentIdIndex parses a given index and provides methods to look up instance IDs.
+#[derive(Debug, Default)]
+pub struct ComponentIdIndex {
+ _moniker_to_instance_id: HashMap<AbsoluteMoniker, ComponentInstanceId>,
+}
+
+impl ComponentIdIndex {
+ pub async fn new(index_file_path: &str) -> Result<Self, ComponentIdIndexError> {
+ let fidl_index = io_util::file::read_in_namespace_to_fidl::<
+ fcomponent_internal::ComponentIdIndex,
+ >(index_file_path)
+ .await
+ .map_err(|err| ComponentIdIndexError::IndexUnreadable {
+ path: index_file_path.to_string(),
+ err: err.into(),
+ })?;
+
+ let index = component_id_index::Index::from_fidl(fidl_index)?;
+
+ let mut moniker_to_instance_id = HashMap::<AbsoluteMoniker, ComponentInstanceId>::new();
+ for entry in &index.instances {
+ if let Some(moniker) = &entry.moniker {
+ let absolute_moniker = AbsoluteMoniker::parse_string_without_instances(moniker)
+ .map_err(|e| ComponentIdIndexError::MonikerError(e))?;
+ moniker_to_instance_id.insert(
+ absolute_moniker,
+ entry
+ .instance_id
+ .as_ref()
+ .ok_or_else(|| {
+ ComponentIdIndexError::IndexError(
+ component_id_index::IndexError::ValidationError(
+ component_id_index::ValidationError::MissingInstanceIds {
+ entries: vec![entry.clone()],
+ },
+ ),
+ )
+ })?
+ .clone(),
+ );
+ }
+ }
+ Ok(Self { _moniker_to_instance_id: moniker_to_instance_id })
+ }
+
+ pub fn look_up_moniker(&self, moniker: &AbsoluteMoniker) -> Option<&ComponentInstanceId> {
+ self._moniker_to_instance_id.get(moniker)
+ }
+}
+
+#[cfg(test)]
+pub mod tests {
+ use super::*;
+ use anyhow::Result;
+ use fidl::encoding::encode_persistent;
+ use fuchsia_async as fasync;
+ use std::convert::TryFrom;
+ use std::io::Write;
+ use tempfile::NamedTempFile;
+
+ fn make_index_file(index: component_id_index::Index) -> Result<NamedTempFile> {
+ let mut tmp_file = NamedTempFile::new()?;
+ tmp_file.write_all(
+ encode_persistent(&mut fcomponent_internal::ComponentIdIndex::try_from(index)?)?
+ .as_ref(),
+ )?;
+ Ok(tmp_file)
+ }
+
+ #[fasync::run_singlethreaded(test)]
+ async fn invalid_moniker() {
+ let index_file = make_index_file(component_id_index::Index {
+ instances: vec![component_id_index::InstanceIdEntry {
+ instance_id: Some("0".repeat(64)),
+ appmgr_moniker: None,
+ moniker: Some("invalid moniker".to_string()),
+ }],
+ ..component_id_index::Index::default()
+ })
+ .unwrap();
+
+ let moniker = ComponentIdIndex::new(index_file.path().to_str().unwrap()).await;
+ assert!(matches!(
+ moniker.err().unwrap(),
+ ComponentIdIndexError::MonikerError(MonikerError::InvalidMoniker { rep: _ }),
+ ));
+ }
+
+ #[fasync::run_singlethreaded(test)]
+ async fn look_up_moniker_no_exists() {
+ let index_file = make_index_file(component_id_index::Index::default()).unwrap();
+ let index = ComponentIdIndex::new(index_file.path().to_str().unwrap()).await.unwrap();
+ assert!(index
+ .look_up_moniker(&AbsoluteMoniker::parse_string_without_instances("/a/b/c").unwrap())
+ .is_none());
+ }
+
+ #[fasync::run_singlethreaded(test)]
+ async fn look_up_moniker_exists() {
+ let iid = "0".repeat(64);
+ let index_file = make_index_file(component_id_index::Index {
+ instances: vec![component_id_index::InstanceIdEntry {
+ instance_id: Some(iid.clone()),
+ appmgr_moniker: None,
+ moniker: Some("/a/b/c".to_string()),
+ }],
+ ..component_id_index::Index::default()
+ })
+ .unwrap();
+ let index = ComponentIdIndex::new(index_file.path().to_str().unwrap()).await.unwrap();
+ assert_eq!(
+ Some(&iid),
+ index.look_up_moniker(
+ &AbsoluteMoniker::parse_string_without_instances("/a/b/c").unwrap()
+ )
+ );
+ }
+
+ #[fasync::run_singlethreaded(test)]
+ async fn index_unreadable() {
+ let result = ComponentIdIndex::new("/this/path/doesnt/exist").await;
+ assert!(matches!(result, Err(ComponentIdIndexError::IndexUnreadable { path: _, err: _ })));
+ }
+}
diff --git a/src/sys/component_manager/src/model/context.rs b/src/sys/component_manager/src/model/context.rs
index 2d93d34..32cff8c 100644
--- a/src/sys/component_manager/src/model/context.rs
+++ b/src/sys/component_manager/src/model/context.rs
@@ -5,7 +5,9 @@
use {
crate::{
config::RuntimeConfig,
- model::{error::ModelError, policy::GlobalPolicyChecker},
+ model::{
+ component_id_index::ComponentIdIndex, error::ModelError, policy::GlobalPolicyChecker,
+ },
},
std::sync::{Arc, Weak},
};
@@ -15,18 +17,29 @@
/// want to share with Realms.
pub struct ModelContext {
policy_checker: GlobalPolicyChecker,
+ component_id_index: ComponentIdIndex,
}
impl ModelContext {
/// Constructs a new ModelContext from a RuntimeConfig.
- pub fn new(runtime_config: Arc<RuntimeConfig>) -> Self {
- Self { policy_checker: GlobalPolicyChecker::new(runtime_config) }
+ pub async fn new(runtime_config: Arc<RuntimeConfig>) -> Result<Self, ModelError> {
+ Ok(Self {
+ component_id_index: match &runtime_config.component_id_index_path {
+ Some(path) => ComponentIdIndex::new(&path).await?,
+ None => ComponentIdIndex::default(),
+ },
+ policy_checker: GlobalPolicyChecker::new(runtime_config),
+ })
}
/// Returns the runtime policy checker for the model.
pub fn policy(&self) -> &GlobalPolicyChecker {
&self.policy_checker
}
+
+ pub fn component_id_index(&self) -> &ComponentIdIndex {
+ &self.component_id_index
+ }
}
/// A wrapper for a weak reference to `ModelContext`. It implements an upgrade()
diff --git a/src/sys/component_manager/src/model/environment.rs b/src/sys/component_manager/src/model/environment.rs
index dc43ef5..a0027be 100644
--- a/src/sys/component_manager/src/model/environment.rs
+++ b/src/sys/component_manager/src/model/environment.rs
@@ -311,12 +311,16 @@
registry
};
- let model = Arc::new(Model::new(ModelParams {
- runtime_config: Arc::new(RuntimeConfig::default()),
- root_component_url: "test:///root".to_string(),
- root_environment: Environment::new_root(RunnerRegistry::new(runners), resolvers),
- namespace_capabilities: vec![],
- }));
+ let model = Arc::new(
+ Model::new(ModelParams {
+ runtime_config: Arc::new(RuntimeConfig::default()),
+ root_component_url: "test:///root".to_string(),
+ root_environment: Environment::new_root(RunnerRegistry::new(runners), resolvers),
+ namespace_capabilities: vec![],
+ })
+ .await
+ .unwrap(),
+ );
let realm = model.bind(&vec!["a:0", "b:0"].into(), &BindReason::Eager).await?;
assert_eq!(realm.component_url, "test:///b");
@@ -374,12 +378,16 @@
registry
};
- let model = Arc::new(Model::new(ModelParams {
- runtime_config: Arc::new(RuntimeConfig::default()),
- root_component_url: "test:///root".to_string(),
- root_environment: Environment::new_root(RunnerRegistry::new(runners), resolvers),
- namespace_capabilities: vec![],
- }));
+ let model = Arc::new(
+ Model::new(ModelParams {
+ runtime_config: Arc::new(RuntimeConfig::default()),
+ root_component_url: "test:///root".to_string(),
+ root_environment: Environment::new_root(RunnerRegistry::new(runners), resolvers),
+ namespace_capabilities: vec![],
+ })
+ .await
+ .unwrap(),
+ );
let realm = model.bind(&vec!["a:0", "b:0"].into(), &BindReason::Eager).await?;
assert_eq!(realm.component_url, "test:///b");
@@ -441,12 +449,16 @@
registry
};
- let model = Arc::new(Model::new(ModelParams {
- runtime_config: Arc::new(RuntimeConfig::default()),
- root_component_url: "test:///root".to_string(),
- root_environment: Environment::new_root(RunnerRegistry::new(runners), resolvers),
- namespace_capabilities: vec![],
- }));
+ let model = Arc::new(
+ Model::new(ModelParams {
+ runtime_config: Arc::new(RuntimeConfig::default()),
+ root_component_url: "test:///root".to_string(),
+ root_environment: Environment::new_root(RunnerRegistry::new(runners), resolvers),
+ namespace_capabilities: vec![],
+ })
+ .await
+ .unwrap(),
+ );
// Add instance to collection.
{
let parent_realm = model.bind(&vec!["a:0"].into(), &BindReason::Eager).await?;
@@ -506,12 +518,16 @@
registry
};
- let model = Arc::new(Model::new(ModelParams {
- runtime_config: Arc::new(RuntimeConfig::default()),
- root_component_url: "test:///root".to_string(),
- root_environment: Environment::new_root(RunnerRegistry::new(runners), resolvers),
- namespace_capabilities: vec![],
- }));
+ let model = Arc::new(
+ Model::new(ModelParams {
+ runtime_config: Arc::new(RuntimeConfig::default()),
+ root_component_url: "test:///root".to_string(),
+ root_environment: Environment::new_root(RunnerRegistry::new(runners), resolvers),
+ namespace_capabilities: vec![],
+ })
+ .await
+ .unwrap(),
+ );
let realm = model.bind(&vec!["a:0", "b:0"].into(), &BindReason::Eager).await?;
assert_eq!(realm.component_url, "test:///b");
@@ -557,12 +573,16 @@
registry.register("test".to_string(), Box::new(resolver)).unwrap();
registry
};
- let model = Arc::new(Model::new(ModelParams {
- runtime_config: Arc::new(RuntimeConfig::default()),
- root_component_url: "test:///root".to_string(),
- root_environment: Environment::new_root(RunnerRegistry::default(), registry),
- namespace_capabilities: vec![],
- }));
+ let model = Arc::new(
+ Model::new(ModelParams {
+ runtime_config: Arc::new(RuntimeConfig::default()),
+ root_component_url: "test:///root".to_string(),
+ root_environment: Environment::new_root(RunnerRegistry::default(), registry),
+ namespace_capabilities: vec![],
+ })
+ .await
+ .unwrap(),
+ );
assert_matches!(
model.bind(&vec!["a:0", "b:0"].into(), &BindReason::Eager).await,
Err(ModelError::ResolverError { .. })
diff --git a/src/sys/component_manager/src/model/error.rs b/src/sys/component_manager/src/model/error.rs
index 2d812e6..1bbd30e 100644
--- a/src/sys/component_manager/src/model/error.rs
+++ b/src/sys/component_manager/src/model/error.rs
@@ -4,9 +4,9 @@
use {
crate::model::{
- environment::EnvironmentError, events::error::EventsError, policy::PolicyError,
- resolver::ResolverError, rights::RightsError, routing::RoutingError, runner::RunnerError,
- storage::StorageError,
+ component_id_index::ComponentIdIndexError, environment::EnvironmentError,
+ events::error::EventsError, policy::PolicyError, resolver::ResolverError,
+ rights::RightsError, routing::RoutingError, runner::RunnerError, storage::StorageError,
},
anyhow::Error,
clonable_error::ClonableError,
@@ -30,6 +30,11 @@
CollectionNotFound { name: String },
#[error("context not found")]
ContextNotFound,
+ #[error("component id index invalid: {}", err)]
+ ComponentIdIndexError {
+ #[from]
+ err: ComponentIdIndexError,
+ },
#[error("environment {} not found in realm {}", name, moniker)]
EnvironmentNotFound { name: String, moniker: AbsoluteMoniker },
#[error("environment {} in realm {} is not valid: {}", name, moniker, err)]
diff --git a/src/sys/component_manager/src/model/events/source_factory.rs b/src/sys/component_manager/src/model/events/source_factory.rs
index 23a11b8..980bd99 100644
--- a/src/sys/component_manager/src/model/events/source_factory.rs
+++ b/src/sys/component_manager/src/model/events/source_factory.rs
@@ -266,15 +266,19 @@
registry.register("test".to_string(), Box::new(resolver)).unwrap();
registry
};
- Arc::new(Model::new(ModelParams {
- runtime_config: Arc::new(RuntimeConfig::default()),
- root_component_url: "test:///root".to_string(),
- root_environment: Environment::new_root(
- RunnerRegistry::default(),
- resolver_registry,
- ),
- namespace_capabilities: vec![],
- }))
+ Arc::new(
+ Model::new(ModelParams {
+ runtime_config: Arc::new(RuntimeConfig::default()),
+ root_component_url: "test:///root".to_string(),
+ root_environment: Environment::new_root(
+ RunnerRegistry::default(),
+ resolver_registry,
+ ),
+ namespace_capabilities: vec![],
+ })
+ .await
+ .unwrap(),
+ )
};
let event_registry = Arc::new(EventRegistry::new(Arc::downgrade(&model)));
let event_source_factory = Arc::new(EventSourceFactory::new(
@@ -301,12 +305,16 @@
async fn passes_on_capability_routed_from_framework_not_on_root() {
let model = {
let resolver = ResolverRegistry::new();
- Arc::new(Model::new(ModelParams {
- runtime_config: Arc::new(RuntimeConfig::default()),
- root_component_url: "test:///root".to_string(),
- root_environment: Environment::new_root(RunnerRegistry::default(), resolver),
- namespace_capabilities: vec![],
- }))
+ Arc::new(
+ Model::new(ModelParams {
+ runtime_config: Arc::new(RuntimeConfig::default()),
+ root_component_url: "test:///root".to_string(),
+ root_environment: Environment::new_root(RunnerRegistry::default(), resolver),
+ namespace_capabilities: vec![],
+ })
+ .await
+ .unwrap(),
+ )
};
let event_registry = Arc::new(EventRegistry::new(Arc::downgrade(&model)));
let event_source_factory = Arc::new(EventSourceFactory::new(
diff --git a/src/sys/component_manager/src/model/mod.rs b/src/sys/component_manager/src/model/mod.rs
index 96e3272..621ebba 100644
--- a/src/sys/component_manager/src/model/mod.rs
+++ b/src/sys/component_manager/src/model/mod.rs
@@ -14,6 +14,7 @@
// fuctionality in this module. Factor out the externally-depended code into its own module.
pub mod testing;
+pub(crate) mod component_id_index;
pub(crate) mod context;
pub(crate) mod environment;
pub(crate) mod events;
diff --git a/src/sys/component_manager/src/model/model.rs b/src/sys/component_manager/src/model/model.rs
index d486974..fa19ecd 100644
--- a/src/sys/component_manager/src/model/model.rs
+++ b/src/sys/component_manager/src/model/model.rs
@@ -43,17 +43,21 @@
impl Model {
/// Creates a new component model and initializes its topology.
- pub fn new(params: ModelParams) -> Model {
+ 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));
+ 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,
));
- Model { root_realm, _context: context, _component_manager_realm: component_manager_realm }
+ 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
diff --git a/src/sys/lib/component_id_index/src/fidl_convert.rs b/src/sys/lib/component_id_index/src/fidl_convert.rs
index 6f7e42f..fbc0fea 100644
--- a/src/sys/lib/component_id_index/src/fidl_convert.rs
+++ b/src/sys/lib/component_id_index/src/fidl_convert.rs
@@ -7,7 +7,7 @@
use std::convert::TryFrom;
use thiserror::Error;
-#[derive(Error, Debug, PartialEq)]
+#[derive(Error, Clone, Debug, PartialEq)]
pub enum FidlConversionError {
#[error("Missing appmgr_restrict_isolated_persistent_storage")]
MissingAppmgrRestrictIsolatedPersistentStorage,
diff --git a/src/sys/lib/component_id_index/src/lib.rs b/src/sys/lib/component_id_index/src/lib.rs
index 087ec00..6a8ea25 100644
--- a/src/sys/lib/component_id_index/src/lib.rs
+++ b/src/sys/lib/component_id_index/src/lib.rs
@@ -5,34 +5,44 @@
// This library must remain platform-agnostic because it used by a host tool and within Fuchsia.
use anyhow::{anyhow, Context, Result};
+use fidl_fuchsia_component_internal as fcomponent_internal;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
+use std::convert::TryFrom;
use std::fs;
use std::str;
use thiserror::Error;
pub mod fidl_convert;
-#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AppmgrMoniker {
pub url: String,
pub realm_path: Vec<String>,
pub transitional_realm_paths: Option<Vec<Vec<String>>>,
}
-#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct InstanceIdEntry {
pub instance_id: Option<String>,
pub appmgr_moniker: Option<AppmgrMoniker>,
pub moniker: Option<String>,
}
-#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Index {
pub appmgr_restrict_isolated_persistent_storage: Option<bool>,
pub instances: Vec<InstanceIdEntry>,
}
+#[derive(Debug, Clone, Error, PartialEq)]
+pub enum IndexError {
+ #[error("invalid index")]
+ ValidationError(#[from] ValidationError),
+ #[error("could not convert FIDL index")]
+ FidlConversionError(#[from] fidl_convert::FidlConversionError),
+}
+
impl Index {
// Construct an Index by merging index source files.
//
@@ -42,22 +52,40 @@
// See `ValidationError` for possible errors.
pub fn from_files_with_decoder(
index_file_paths: &[String],
- decoder: impl Fn(&str) -> anyhow::Result<Index>,
+ decoder: impl Fn(&[u8]) -> anyhow::Result<Index>,
) -> anyhow::Result<Index> {
let mut ctx = MergeContext::new();
for input_file_path in index_file_paths {
let contents = fs::read_to_string(&input_file_path)
.with_context(|| anyhow!("Could not read index file {}", &input_file_path))?;
- let index = decoder(contents.as_str())
+ let index = decoder(contents.as_str().as_bytes())
.with_context(|| anyhow!("Could not parse index file {}", &input_file_path))?;
ctx.merge(&input_file_path, &index)
.with_context(|| anyhow!("Could not merge index file {}", &input_file_path))?;
}
Ok(ctx.output())
}
+
+ // Construct an Index from the given FIDL-schema'd index.
+ //
+ // The given fidl_index is validated.
+ pub fn from_fidl(
+ fidl_index: fcomponent_internal::ComponentIdIndex,
+ ) -> Result<Index, IndexError> {
+ let native_index = Index::try_from(fidl_index)?;
+ let mut ctx = MergeContext::new();
+ ctx.merge("", &native_index)?;
+ Ok(ctx.output())
+ }
}
-#[derive(Error, Debug, PartialEq)]
+impl Default for Index {
+ fn default() -> Self {
+ Index { appmgr_restrict_isolated_persistent_storage: Some(true), instances: vec![] }
+ }
+}
+
+#[derive(Error, Debug, Clone, PartialEq)]
pub enum ValidationError {
#[error("Instance ID '{}' must be unique but exists in following index files:\n {}\n {}", .instance_id, .source1, .source2)]
DuplicateIds { instance_id: String, source1: String, source2: String },
diff --git a/src/sys/lib/moniker/src/lib.rs b/src/sys/lib/moniker/src/lib.rs
index 25d5342..e380f65 100644
--- a/src/sys/lib/moniker/src/lib.rs
+++ b/src/sys/lib/moniker/src/lib.rs
@@ -535,7 +535,7 @@
}
/// Errors produced by `MonikerEnvironment`.
-#[derive(Debug, Error, PartialEq, Eq)]
+#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum MonikerError {
#[error("invalid moniker: {}", rep)]
InvalidMoniker { rep: String },
diff --git a/tools/component_id_index/src/main.rs b/tools/component_id_index/src/main.rs
index c981cc8..d2fec6c 100644
--- a/tools/component_id_index/src/main.rs
+++ b/tools/component_id_index/src/main.rs
@@ -41,7 +41,8 @@
// Make an Index using a set of JSON5-encoded index files.
fn merge_index_from_json5_files(index_files: &[String]) -> anyhow::Result<Index> {
Index::from_files_with_decoder(index_files, |json5| {
- serde_json5::from_str(json5).context("Unable to parse JSON5")
+ let json5_str = std::str::from_utf8(json5).context("Unable to parse as UTF-8")?;
+ serde_json5::from_str(json5_str).context("Unable to parse JSON5")
}).map_err(|e|{
match e.downcast_ref::<ValidationError>() {
Some(ValidationError::MissingInstanceIds{entries}) => {
diff --git a/tools/component_manager_config/src/compile.rs b/tools/component_manager_config/src/compile.rs
index c0445e6..04dd1b4 100644
--- a/tools/component_manager_config/src/compile.rs
+++ b/tools/component_manager_config/src/compile.rs
@@ -32,6 +32,7 @@
builtin_pkg_resolver: Option<BuiltinPkgResolver>,
out_dir_contents: Option<OutDirContents>,
root_component_url: Option<Url>,
+ component_id_index_path: Option<String>,
}
#[derive(Deserialize, Debug)]
@@ -186,6 +187,7 @@
Some(root_component_url) => Some(root_component_url.as_str().to_string()),
None => None,
},
+ component_id_index_path: config.component_id_index_path,
..Self::EMPTY
})
}
@@ -274,6 +276,7 @@
extend_if_unset!(self, another, builtin_pkg_resolver);
extend_if_unset!(self, another, out_dir_contents);
extend_if_unset!(self, another, root_component_url);
+ extend_if_unset!(self, another, component_id_index_path);
Ok(self)
}
@@ -336,6 +339,7 @@
args.input.iter().map(Config::from_json_file).collect::<Result<Vec<Config>, _>>()?;
let config_json =
configs.into_iter().try_fold(Config::default(), |acc, next| acc.extend(next))?;
+
let mut config_fidl: component_internal::Config = config_json.try_into()?;
let bytes = encode_persistent(&mut config_fidl).map_err(|e| Error::FidlEncoding(e))?;
let mut file = File::create(args.output).map_err(|e| Error::Io(e))?;
@@ -407,6 +411,7 @@
num_threads: 321,
out_dir_contents: "svc",
root_component_url: "fuchsia-pkg://fuchsia.com/foo#meta/foo.cmx",
+ component_id_index_path: "/this/is/an/absolute/path",
}"#;
let config = compile_str(input).expect("failed to compile");
assert_eq!(
@@ -475,6 +480,7 @@
num_threads: Some(321),
out_dir_contents: Some(component_internal::OutDirContents::Svc),
root_component_url: Some("fuchsia-pkg://fuchsia.com/foo#meta/foo.cmx".to_string()),
+ component_id_index_path: Some("/this/is/an/absolute/path".to_string()),
..component_internal::Config::EMPTY
}
);