eager_package_config/
pkg_resolver.rs1use fuchsia_url::fuchsia_pkg::UnpinnedAbsolutePackageUrl;
6use omaha_client::cup_ecdsa::PublicKeys;
7use omaha_client::version::Version;
8use serde::{Deserialize, Serialize};
9
10#[cfg(target_os = "fuchsia")]
11use {fidl_fuchsia_io as fio, futures::stream::StreamExt as _, std::collections::HashSet};
12
13#[cfg(target_os = "fuchsia")]
14const EAGER_PACKAGE_CONFIG_PATH: &str = "/config/data/eager_package_config.json";
15
16#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
17pub struct EagerPackageConfigs {
18 pub packages: Vec<EagerPackageConfig>,
19}
20
21#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
22pub struct EagerPackageConfig {
23 pub url: UnpinnedAbsolutePackageUrl,
24 #[serde(default, skip_serializing_if = "is_false")]
25 pub executable: bool,
26 pub public_keys: PublicKeys,
27 pub minimum_required_version: Version,
28 #[serde(default = "return_true", skip_serializing_if = "bool::clone")]
29 pub cache_fallback: bool,
30}
31
32#[cfg(target_os = "fuchsia")]
33impl EagerPackageConfigs {
34 pub async fn from_namespace() -> Result<Self, EagerPackageConfigsError> {
37 Self::from_path(EAGER_PACKAGE_CONFIG_PATH).await
38 }
39
40 async fn from_path(path: &str) -> Result<Self, EagerPackageConfigsError> {
41 let proxy = fuchsia_fs::file::open_in_namespace(
42 path,
43 fio::PERM_READABLE | fio::Flags::FLAG_SEND_REPRESENTATION,
44 )
45 .map_err(EagerPackageConfigsError::Open)?;
46 match fuchsia_fs::file::read(&proxy).await {
47 Ok(json) => Self::from_json(&json),
48 Err(e) => {
49 match proxy.take_event_stream().next().await {
50 Some(Ok(fio::FileEvent::OnOpen_ { s, .. }))
52 if s == zx::Status::NOT_FOUND.into_raw() =>
53 {
54 Ok(EagerPackageConfigs { packages: Vec::new() })
55 }
56 Some(Err(fidl::Error::ClientChannelClosed { epitaph, .. }))
58 if epitaph == zx::Status::NOT_FOUND =>
59 {
60 Ok(EagerPackageConfigs { packages: Vec::new() })
61 }
62 _ => Err(EagerPackageConfigsError::Read(e)),
63 }
64 }
65 }
66 }
67
68 fn from_json(json: &[u8]) -> Result<Self, EagerPackageConfigsError> {
69 let configs: Self = serde_json::from_slice(json)?;
70 if configs.packages.iter().map(|config| config.url.path()).collect::<HashSet<_>>().len()
71 < configs.packages.len()
72 {
73 return Err(EagerPackageConfigsError::DuplicatePath);
74 }
75 Ok(configs)
76 }
77}
78
79#[cfg(target_os = "fuchsia")]
80#[derive(Debug, thiserror::Error)]
81pub enum EagerPackageConfigsError {
82 #[error("open eager package config")]
83 Open(#[from] fuchsia_fs::node::OpenError),
84 #[error("read eager package config")]
85 Read(#[from] fuchsia_fs::file::ReadError),
86 #[error("parse eager package config from json")]
87 Json(#[from] serde_json::Error),
88 #[error("eager package URL must have unique path")]
89 DuplicatePath,
90}
91
92fn return_true() -> bool {
93 true
94}
95
96fn is_false(b: &bool) -> bool {
97 !b
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use assert_matches::assert_matches;
104 use omaha_client::cup_ecdsa::test_support::{
105 make_default_json_public_keys_for_test, make_default_public_keys_for_test,
106 };
107
108 #[fuchsia::test]
109 async fn not_found_tmp() {
110 let configs = EagerPackageConfigs::from_path("/tmp/not-found").await.unwrap();
111 assert_eq!(configs.packages, vec![]);
112 }
113
114 #[fuchsia::test]
115 async fn not_found_pkg() {
116 let configs = EagerPackageConfigs::from_path("/pkg/not-found").await.unwrap();
117 assert_eq!(configs.packages, vec![]);
118 }
119
120 #[fuchsia::test]
121 async fn success() {
122 let json = serde_json::json!({
123 "packages":[
124 {
125 "url": "fuchsia-pkg://example.com/package_service_1",
126 "public_keys": make_default_json_public_keys_for_test(),
127 "minimum_required_version": "1.2.3.4"
128 }
129 ]
130 });
131 assert_eq!(
132 EagerPackageConfigs::from_json(json.to_string().as_bytes()).unwrap(),
133 EagerPackageConfigs {
134 packages: vec![EagerPackageConfig {
135 url: "fuchsia-pkg://example.com/package_service_1".parse().unwrap(),
136 executable: false,
137 public_keys: make_default_public_keys_for_test(),
138 minimum_required_version: [1, 2, 3, 4].into(),
139 cache_fallback: true,
140 }]
141 }
142 );
143 }
144
145 #[fuchsia::test]
146 async fn duplicate_path() {
147 let json = serde_json::json!({
148 "packages":[
149 {
150 "url": "fuchsia-pkg://example.com/package_service_1",
151 "public_keys": make_default_json_public_keys_for_test(),
152 "minimum_required_version": "1.2.3.4"
153 },
154 {
155 "url": "fuchsia-pkg://another-example.com/package_service_1",
156 "public_keys": make_default_json_public_keys_for_test(),
157 "minimum_required_version": "1.2.3.4"
158 }
159 ]
160 });
161 assert_matches!(
162 EagerPackageConfigs::from_json(json.to_string().as_bytes()),
163 Err(EagerPackageConfigsError::DuplicatePath)
164 );
165 }
166}