use crate::store::PullPolicy;
use crate::store::Store;
use async_trait::async_trait;
use oci_distribution::secrets::RegistryAuth;
use oci_distribution::Reference;
use std::sync::Arc;
pub trait InterceptingStore: Store {
fn intercepts(&self, image_ref: &Reference) -> bool;
}
pub trait ComposableStore {
fn with_override(
self,
interceptor: Arc<dyn InterceptingStore + Send + Sync>,
) -> Arc<dyn Store + Send + Sync>;
}
impl ComposableStore for Arc<dyn Store + Send + Sync> {
fn with_override(
self,
interceptor: Arc<dyn InterceptingStore + Send + Sync>,
) -> Arc<dyn Store + Send + Sync> {
Arc::new(CompositeStore {
base: self,
interceptor,
})
}
}
impl<S> ComposableStore for Arc<S>
where
S: Store + Send + Sync + 'static,
{
fn with_override(
self,
interceptor: Arc<dyn InterceptingStore + Send + Sync>,
) -> Arc<dyn Store + Send + Sync> {
Arc::new(CompositeStore {
base: self,
interceptor,
})
}
}
struct CompositeStore {
base: Arc<dyn Store + Send + Sync>,
interceptor: Arc<dyn InterceptingStore + Send + Sync>,
}
#[async_trait]
impl Store for CompositeStore {
async fn get(
&self,
image_ref: &Reference,
pull_policy: PullPolicy,
auth: &RegistryAuth,
) -> anyhow::Result<Vec<u8>> {
if self.interceptor.intercepts(image_ref) {
self.interceptor.get(image_ref, pull_policy, auth).await
} else {
self.base.get(image_ref, pull_policy, auth).await
}
}
}
#[cfg(test)]
mod test {
use super::*;
use oci_distribution::secrets::RegistryAuth;
use std::convert::TryFrom;
struct FakeBase {}
struct FakeInterceptor {}
#[async_trait]
impl Store for FakeBase {
async fn get(
&self,
_image_ref: &Reference,
_pull_policy: PullPolicy,
_auth: &RegistryAuth,
) -> anyhow::Result<Vec<u8>> {
Ok(vec![11, 10, 5, 14])
}
}
#[async_trait]
impl Store for FakeInterceptor {
async fn get(
&self,
_image_ref: &Reference,
_pull_policy: PullPolicy,
_auth: &RegistryAuth,
) -> anyhow::Result<Vec<u8>> {
Ok(vec![1, 2, 3])
}
}
impl InterceptingStore for FakeInterceptor {
fn intercepts(&self, image_ref: &Reference) -> bool {
image_ref.whole().starts_with("int")
}
}
#[tokio::test]
async fn if_interceptor_matches_then_composite_store_returns_intercepting_value() {
let store = Arc::new(FakeBase {}).with_override(Arc::new(FakeInterceptor {}));
let result = store
.get(
&Reference::try_from("int/foo").unwrap(),
PullPolicy::Never,
&RegistryAuth::Anonymous,
)
.await
.unwrap();
assert_eq!(3, result.len());
assert_eq!(1, result[0]);
}
#[tokio::test]
async fn if_interceptor_does_not_match_then_composite_store_returns_base_value() {
let store = Arc::new(FakeBase {}).with_override(Arc::new(FakeInterceptor {}));
let result = store
.get(
&Reference::try_from("mint/foo").unwrap(),
PullPolicy::Never,
&RegistryAuth::Anonymous,
)
.await
.unwrap();
assert_eq!(4, result.len());
assert_eq!(11, result[0]);
}
}