blob: c6879a3fa03a68bc42dcbd406055a27dcb8074e8 [file] [log] [blame]
// 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.
#[cfg(test)]
pub(crate) mod for_tests {
use crate::cache::for_tests::CacheForTest;
use anyhow::{anyhow, Context, Error};
use blobfs_ramdisk::BlobfsRamdisk;
use fidl_fuchsia_pkg_ext::RepositoryConfigs;
use fuchsia_component_test::{
Capability, ChildOptions, ChildRef, DirectoryContents, RealmBuilder, RealmInstance, Ref,
Route,
};
use fuchsia_pkg_testing::serve::ServedRepository;
use fuchsia_url::RepositoryUrl;
use futures::{FutureExt as _, StreamExt as _};
use std::sync::Arc;
use {
fidl_fuchsia_io as fio, fidl_fuchsia_metrics as fmetrics, fidl_fuchsia_pkg as fpkg,
fidl_fuchsia_pkg_ext as pkg, fuchsia_async as fasync,
};
const SSL_TEST_CERTS_PATH: &str = "/pkg/data/ssl/cert.pem";
const SSL_CERT_FILE_NAME: &str = "cert.pem";
pub const EMPTY_REPO_PATH: &str = "/pkg/empty-repo";
/// Represents the sandboxed package resolver.
pub struct Resolver {
proxy: fpkg::PackageResolverProxy,
}
impl Resolver {
fn new_with_proxy(pkg_resolver_proxy: fpkg::PackageResolverProxy) -> Result<Self, Error> {
Ok(Self { proxy: pkg_resolver_proxy })
}
}
/// This wraps the `Resolver` in order to reduce test boilerplate.
pub struct ResolverForTest {
pub cache: CacheForTest,
pub resolver: Arc<Resolver>,
_served_repo: Arc<ServedRepository>,
}
pub struct ResolverRealm {
pub resolver: ChildRef,
pub cache: ChildRef,
}
impl ResolverForTest {
pub async fn realm_setup(
realm_builder: &RealmBuilder,
served_repo: Arc<ServedRepository>,
repo_url: RepositoryUrl,
blobfs: &BlobfsRamdisk,
) -> Result<ResolverRealm, Error> {
let cache_ref =
CacheForTest::realm_setup(realm_builder, blobfs).await.expect("setting up cache");
let repo_config =
RepositoryConfigs::Version1(vec![served_repo.make_repo_config(repo_url)]);
let cert_bytes = std::fs::read(std::path::Path::new(SSL_TEST_CERTS_PATH)).unwrap();
let service_reflector = realm_builder
.add_local_child(
"pkg_resolver_service_reflector",
move |handles| {
let mut fs = fuchsia_component::server::ServiceFs::new();
// Not necessary for updates, but prevents spam of irrelevant error logs.
fs.dir("svc").add_fidl_service(move |stream| {
fasync::Task::spawn(
Arc::new(mock_metrics::MockMetricEventLoggerFactory::new())
.run_logger_factory(stream),
)
.detach()
});
async move {
fs.serve_connection(handles.outgoing_dir).unwrap();
let () = fs.collect().await;
Ok(())
}
.boxed()
},
ChildOptions::new(),
)
.await
.unwrap();
let pkg_resolver = realm_builder
.add_child("pkg-resolver", "#meta/pkg-resolver.cm", ChildOptions::new())
.await
.unwrap();
realm_builder
.read_only_directory(
"config-data",
vec![&pkg_resolver],
DirectoryContents::new().add_file(
"repositories/test.json",
serde_json::to_string(&repo_config).unwrap(),
),
)
.await
.unwrap();
realm_builder
.read_only_directory(
"root-ssl-certificates",
vec![&pkg_resolver],
DirectoryContents::new().add_file(SSL_CERT_FILE_NAME, cert_bytes),
)
.await
.unwrap();
realm_builder
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
.from(Ref::parent())
.to(&pkg_resolver),
)
.await
.unwrap();
realm_builder
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.net.name.Lookup"))
.capability(Capability::protocol_by_name("fuchsia.posix.socket.Provider"))
.from(Ref::parent())
.to(&pkg_resolver),
)
.await
.unwrap();
realm_builder
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.pkg.PackageResolver-ota"))
.from(&pkg_resolver)
.to(Ref::parent()),
)
.await
.unwrap();
realm_builder
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.pkg.PackageCache"))
.capability(Capability::protocol_by_name("fuchsia.space.Manager"))
.from(&cache_ref)
.to(&pkg_resolver),
)
.await
.unwrap();
realm_builder
.add_route(
Route::new()
.capability(
Capability::protocol::<fmetrics::MetricEventLoggerFactoryMarker>(),
)
.from(&service_reflector)
.to(&pkg_resolver),
)
.await
.unwrap();
Ok(ResolverRealm { resolver: pkg_resolver, cache: cache_ref })
}
pub async fn new(
realm_instance: &RealmInstance,
blobfs: BlobfsRamdisk,
served_repo: Arc<ServedRepository>,
) -> Result<Self, Error> {
let cache = CacheForTest::new(realm_instance, blobfs).await.unwrap();
let resolver_proxy = realm_instance
.root
.connect_to_named_protocol_at_exposed_dir::<fpkg::PackageResolverMarker>(
"fuchsia.pkg.PackageResolver-ota",
)
.expect("connect to pkg resolver");
let resolver = Resolver::new_with_proxy(resolver_proxy).unwrap();
let cache_proxy = cache.cache.package_cache_proxy().unwrap();
assert_eq!(cache_proxy.sync().await.unwrap(), Ok(()));
Ok(ResolverForTest { cache, resolver: Arc::new(resolver), _served_repo: served_repo })
}
/// Resolve a package using the resolver, returning the root directory of the package,
/// and the context for resolving relative package URLs.
pub async fn resolve_package(
&self,
url: &str,
) -> Result<(fio::DirectoryProxy, pkg::ResolutionContext), Error> {
let proxy = &self.resolver.proxy;
let (package, package_remote) =
fidl::endpoints::create_proxy().context("creating package directory endpoints")?;
let resolved_context = proxy
.resolve(url, package_remote)
.await
.unwrap()
.map_err(|e| anyhow!("Package resolver error: {:?}", e))?;
Ok((package, (&resolved_context).try_into().expect("resolver returns valid context")))
}
}
}
#[cfg(test)]
pub mod tests {
use super::for_tests::{ResolverForTest, EMPTY_REPO_PATH};
use anyhow::{Context, Error};
use fuchsia_async as fasync;
use fuchsia_component_test::RealmBuilder;
use fuchsia_pkg_testing::{PackageBuilder, RepositoryBuilder};
use std::sync::Arc;
const TEST_REPO_URL: &str = "fuchsia-pkg://test";
#[fasync::run_singlethreaded(test)]
pub async fn test_resolver() -> Result<(), Error> {
let name = "test-resolver";
let package = PackageBuilder::new(name)
.add_resource_at("data/file1", "hello".as_bytes())
.add_resource_at("data/file2", "hello two".as_bytes())
.build()
.await
.unwrap();
let repo = Arc::new(
RepositoryBuilder::from_template_dir(EMPTY_REPO_PATH)
.add_package(&package)
.build()
.await
.context("Building repo")
.unwrap(),
);
let realm_builder = RealmBuilder::new().await.unwrap();
let served_repo = Arc::new(Arc::clone(&repo).server().start().unwrap());
let blobfs =
blobfs_ramdisk::BlobfsRamdisk::start().await.context("starting blobfs").unwrap();
let _resolver_realm = ResolverForTest::realm_setup(
&realm_builder,
Arc::clone(&served_repo),
TEST_REPO_URL.parse().unwrap(),
&blobfs,
)
.await
.unwrap();
let realm_instance = realm_builder.build().await.unwrap();
let resolver = ResolverForTest::new(&realm_instance, blobfs, served_repo)
.await
.context("launching resolver")?;
let (root_dir, _resolved_context) =
resolver.resolve_package(&format!("{TEST_REPO_URL}/{name}")).await.unwrap();
package.verify_contents(&root_dir).await.unwrap();
Ok(())
}
}