blob: 456ebd914d4a49ab94f7b939b150b26ea1559a3f [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::repository::{FileSystemRepository, PmRepository, Repository, RepositoryKeyConfig},
anyhow::{anyhow, Context, Result},
camino::Utf8PathBuf,
fuchsia_pkg::PackageBuilder,
futures::io::AllowStdIo,
maplit::hashmap,
std::{
fs::{copy, create_dir, create_dir_all, File},
path::{Path, PathBuf},
},
tuf::{
crypto::Ed25519PrivateKey, interchange::Json, metadata::TargetPath,
repo_builder::RepoBuilder, repository::FileSystemRepositoryBuilder,
},
walkdir::WalkDir,
};
const EMPTY_REPO_PATH: &str = "host_x64/test_data/ffx_lib_pkg/empty-repo";
#[cfg(test)]
pub(crate) const PKG1_HASH: &str =
"2881455493b5870aaea36537d70a2adc635f516ac2092598f4b6056dabc6b25d";
#[cfg(test)]
pub(crate) const PKG2_HASH: &str =
"050907f009ff634f9aa57bff541fb9e9c2c62b587c23578e77637cda3bd69458";
#[cfg(test)]
pub(crate) const PKG1_BIN_HASH: &str =
"72e1e7a504f32edf4f23e7e8a3542c1d77d12541142261cfe272decfa75f542d";
#[cfg(test)]
pub(crate) const PKG1_LIB_HASH: &str =
"8a8a5f07f935a4e8e1fd1a1eda39da09bb2438ec0adfb149679ddd6e7e1fbb4f";
pub fn repo_key() -> RepositoryKeyConfig {
RepositoryKeyConfig::Ed25519Key(
[
29, 76, 86, 76, 184, 70, 108, 73, 249, 127, 4, 47, 95, 63, 36, 35, 101, 255, 212, 33,
10, 154, 26, 130, 117, 157, 125, 88, 175, 214, 109, 113,
]
.to_vec(),
)
}
pub fn repo_private_key() -> Ed25519PrivateKey {
Ed25519PrivateKey::from_ed25519(&[
80, 121, 161, 145, 5, 165, 178, 98, 248, 146, 132, 195, 60, 32, 72, 122, 150, 223, 124,
216, 217, 43, 74, 9, 221, 38, 156, 113, 181, 63, 234, 98, 190, 11, 152, 63, 115, 150, 218,
103, 92, 64, 198, 185, 62, 71, 252, 237, 124, 30, 158, 168, 163, 42, 31, 233, 82, 186, 143,
81, 151, 96, 179, 7,
])
.unwrap()
}
fn copy_dir(from: &Path, to: &Path) -> Result<()> {
let walker = WalkDir::new(from);
for entry in walker.into_iter() {
let entry = entry?;
let to_path = to.join(entry.path().strip_prefix(from)?);
if entry.metadata()?.is_dir() {
create_dir(&to_path).context(format!("creating {:?}", to_path))?;
} else {
copy(entry.path(), &to_path).context(format!(
"copying {:?} to {:?}",
entry.path(),
to_path
))?;
}
}
Ok(())
}
pub async fn make_readonly_empty_repository(name: &str) -> Result<Repository> {
let backend = PmRepository::new(Utf8PathBuf::from(EMPTY_REPO_PATH));
Repository::new(name, Box::new(backend)).await.map_err(|e| anyhow!(e))
}
pub async fn make_writable_empty_repository(name: &str, root: Utf8PathBuf) -> Result<Repository> {
let src = PathBuf::from(EMPTY_REPO_PATH).canonicalize()?;
copy_dir(&src, root.as_std_path())?;
let backend = PmRepository::new(root);
Ok(Repository::new(name, Box::new(backend)).await?)
}
pub async fn make_repository(metadata_dir: &Path, blobs_dir: &Path) {
create_dir_all(&metadata_dir).unwrap();
create_dir_all(&blobs_dir).unwrap();
// Construct some packages for the repository.
let build_tmp = tempfile::tempdir().unwrap();
let build_path = build_tmp.path();
let packages = ["package1", "package2"].map(|name| {
let package_path = build_path.join(name);
let mut builder = PackageBuilder::new(name);
builder.api_level(7).unwrap();
builder
.add_contents_as_blob(
format!("bin/{}", name),
format!("binary {}", name).as_bytes(),
&package_path,
)
.unwrap();
builder
.add_contents_as_blob(
format!("lib/{}", name),
format!("lib {}", name).as_bytes(),
&package_path,
)
.unwrap();
builder
.add_contents_to_far(
format!("meta/{}.cm", name),
format!("cm {}", name).as_bytes(),
&package_path,
)
.unwrap();
builder
.add_contents_to_far(
format!("meta/{}.cmx", name),
format!("cmx {}", name).as_bytes(),
&package_path,
)
.unwrap();
let meta_far_path = package_path.join("meta.far");
let manifest = builder.build(&package_path, &meta_far_path).unwrap();
// Copy the package blobs into the blobs directory.
let mut meta_far_merkle = None;
for blob in manifest.blobs() {
let merkle = blob.merkle.to_string();
if blob.path == "meta/" {
meta_far_merkle = Some(merkle.clone());
}
let mut src = std::fs::File::open(&blob.source_path).unwrap();
let mut dst = std::fs::File::create(blobs_dir.join(merkle)).unwrap();
std::io::copy(&mut src, &mut dst).unwrap();
}
(name, meta_far_path, meta_far_merkle.unwrap())
});
// Write TUF metadata
let repo = FileSystemRepositoryBuilder::<Json>::new(metadata_dir)
.targets_prefix("targets")
.build()
.unwrap();
let key = repo_private_key();
let mut builder = RepoBuilder::create(repo)
.trusted_root_keys(&[&key])
.trusted_targets_keys(&[&key])
.trusted_snapshot_keys(&[&key])
.trusted_timestamp_keys(&[&key])
.stage_root()
.unwrap();
// Add all the packages to the metadata.
for (name, meta_far_path, meta_far_merkle) in packages {
builder = builder
.add_target_with_custom(
TargetPath::new(name).unwrap(),
AllowStdIo::new(File::open(meta_far_path).unwrap()),
hashmap! { "merkle".into() => meta_far_merkle.into() },
)
.await
.unwrap();
}
builder.commit().await.unwrap();
}
pub async fn make_pm_repository(name: &str, dir: impl Into<Utf8PathBuf>) -> Repository {
let dir = dir.into();
let metadata_dir = dir.join("repository");
let blobs_dir = metadata_dir.join("blobs");
make_repository(metadata_dir.as_std_path(), blobs_dir.as_std_path()).await;
let backend = PmRepository::new(dir);
Repository::new(name, Box::new(backend)).await.unwrap()
}
pub async fn make_file_system_repository(
name: &str,
metadata_dir: impl Into<Utf8PathBuf>,
blobs_dir: impl Into<Utf8PathBuf>,
) -> Repository {
let metadata_dir = metadata_dir.into();
let blobs_dir = blobs_dir.into();
make_repository(metadata_dir.as_std_path(), blobs_dir.as_std_path()).await;
let backend = FileSystemRepository::new(metadata_dir, blobs_dir);
Repository::new(name, Box::new(backend)).await.unwrap()
}