use crate::flow;
use crate::io::manifest::resolve_latest;
use crate::io::remote::Remote;
use crate::io::storage::Storage;
use crate::lineage::DomainLineage;
use crate::lineage::PackageLineage;
use crate::paths;
use crate::uri::ManifestUri;
use crate::Error;
use crate::Res;
pub async fn install_package(
lineage: DomainLineage,
paths: &paths::DomainPaths,
storage: &(impl Storage + Sync),
remote: &impl Remote,
manifest_uri: &ManifestUri,
) -> Res<DomainLineage> {
if lineage.packages.contains_key(&manifest_uri.namespace) {
return Err(Error::PackageAlreadyInstalled(
manifest_uri.namespace.clone(),
));
}
flow::cache_remote_manifest(paths, storage, remote, &manifest_uri.clone()).await?;
let installed_manifest_path =
paths.installed_manifest(&manifest_uri.namespace, &manifest_uri.hash);
storage
.create_dir_all(&installed_manifest_path.parent().unwrap())
.await?;
paths::copy_cached_to_installed(paths, storage, manifest_uri).await?;
let objects_dir = paths.objects_dir();
storage.create_dir_all(&objects_dir).await?;
let working_dir = paths.working_dir(&manifest_uri.namespace);
storage.create_dir_all(&working_dir).await?;
let latest = resolve_latest(remote, manifest_uri.into()).await?;
let mut lineage = lineage;
lineage.packages.insert(
manifest_uri.namespace.clone(),
PackageLineage::from_remote(manifest_uri.clone(), latest.hash),
);
Ok(lineage)
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str::FromStr;
use crate::io::storage::LocalStorage;
use crate::mocks;
use crate::uri::S3Uri;
#[tokio::test]
async fn test_if_already_installed() -> Res {
let namespace = ("foo", "bar");
let lineage = DomainLineage {
packages: BTreeMap::from([(namespace.into(), PackageLineage::default())]),
};
let result = install_package(
lineage,
&paths::DomainPaths::default(),
&mocks::storage::MockStorage::default(),
&mocks::remote::MockRemote::default(),
&ManifestUri {
namespace: namespace.into(),
..ManifestUri::default()
},
)
.await;
assert_eq!(
result.unwrap_err().to_string(),
"The package foo/bar is already installed"
);
Ok(())
}
#[tokio::test]
async fn test_installing() -> Res {
let manifest_uri = ManifestUri {
bucket: "a".to_string(),
hash: "abcdef1234".to_string(),
namespace: ("f", "b").into(),
};
let parquet = std::fs::read(mocks::manifest::parquet())?;
let remote = mocks::remote::MockRemote::default();
let remote_uri = S3Uri::from_str(&format!(
"s3://{}/.quilt/packages/1220{}.parquet",
manifest_uri.bucket, manifest_uri.hash
))?;
remote.put_object(&remote_uri, parquet).await?;
let latest_uri = S3Uri::from_str(&format!(
"s3://{}/.quilt/named_packages/{}/latest",
manifest_uri.bucket, manifest_uri.namespace
))?;
remote
.put_object(&latest_uri, manifest_uri.hash.as_bytes().to_vec())
.await?;
let storage = mocks::storage::MockStorage::default();
let result = install_package(
DomainLineage::default(),
&paths::DomainPaths::default(),
&storage,
&remote,
&manifest_uri,
)
.await?;
let installed_package = result.packages.get(&("f", "b").into()).unwrap();
let tracked = installed_package.remote.clone();
assert_eq!(installed_package.latest_hash, "abcdef1234".to_string());
assert_eq!(tracked, manifest_uri);
let installed_manifest_path = PathBuf::from(format!(
".quilt/installed/{}/{}",
tracked.namespace, tracked.hash
));
assert!(storage.exists(&installed_manifest_path).await);
let cached_manifest_path = PathBuf::from(format!(
".quilt/packages/{}/{}",
tracked.bucket, tracked.hash
));
assert!(storage.exists(&cached_manifest_path).await);
Ok(())
}
#[tokio::test]
async fn test_installing_when_no_permissions() -> Res {
let manifest_uri = ManifestUri {
bucket: "a".to_string(),
hash: "h".to_string(),
namespace: ("f", "b").into(),
};
let parquet = std::fs::read(mocks::manifest::parquet())?;
let remote = mocks::remote::MockRemote::default();
let remote_uri = S3Uri::from_str(&format!(
"s3://{}/.quilt/packages/1220{}.parquet",
manifest_uri.bucket, manifest_uri.hash
))?;
remote.put_object(&remote_uri, parquet).await?;
let storage = LocalStorage::new();
let result = install_package(
DomainLineage::default(),
&paths::DomainPaths::new(PathBuf::from("/")),
&storage,
&remote,
&manifest_uri,
)
.await;
let err = result.unwrap_err();
if let Error::Io(orig_err) = err {
assert_eq!(orig_err.kind(), std::io::ErrorKind::PermissionDenied);
} else {
panic!("Expected IO error, got: {:?}", err);
}
Ok(())
}
}