use cache::{
Cache,
CacheRead,
CacheWrite,
Storage,
};
use futures::future::Future;
use simples3::{
AutoRefreshingProvider,
Bucket,
ChainProvider,
ProfileProvider,
ProvideAwsCredentials,
Ssl,
};
use std::env;
use std::io;
use std::rc::Rc;
use std::time::{Instant, Duration};
use tokio_core::reactor::Handle;
use errors::*;
pub struct S3Cache {
bucket: Rc<Bucket>,
provider: AutoRefreshingProvider<ChainProvider>,
}
impl S3Cache {
pub fn new(bucket: &str, endpoint: &str, handle: &Handle) -> Result<S3Cache> {
let home = env::home_dir().ok_or("Couldn't find home directory")?;
let profile_providers = vec![
ProfileProvider::with_configuration(home.join(".aws").join("credentials"), "default"),
ProfileProvider::with_configuration(home.join(".boto"), "Credentials"),
];
let provider = AutoRefreshingProvider::new(ChainProvider::with_profile_providers(profile_providers, handle));
let bucket = Rc::new(Bucket::new(bucket, endpoint, Ssl::No, handle)?);
Ok(S3Cache {
bucket: bucket,
provider: provider,
})
}
}
fn normalize_key(key: &str) -> String {
format!("{}/{}/{}/{}", &key[0..1], &key[1..2], &key[2..3], &key)
}
impl Storage for S3Cache {
fn get(&self, key: &str) -> SFuture<Cache> {
let key = normalize_key(key);
Box::new(self.bucket.get(&key).then(|result| {
match result {
Ok(data) => {
let hit = CacheRead::from(io::Cursor::new(data))?;
Ok(Cache::Hit(hit))
}
Err(e) => {
warn!("Got AWS error: {:?}", e);
Ok(Cache::Miss)
}
}
}))
}
fn put(&self, key: &str, entry: CacheWrite) -> SFuture<Duration> {
let key = normalize_key(&key);
let start = Instant::now();
let data = match entry.finish() {
Ok(data) => data,
Err(e) => return f_err(e),
};
let credentials = self.provider.credentials().chain_err(|| {
"failed to get AWS credentials"
});
let bucket = self.bucket.clone();
let response = credentials.and_then(move |credentials| {
bucket.put(&key, data, &credentials).chain_err(|| {
"failed to put cache entry in s3"
})
});
Box::new(response.map(move |_| start.elapsed()))
}
fn location(&self) -> String {
format!("S3, bucket: {}", self.bucket)
}
fn current_size(&self) -> Option<u64> { None }
fn max_size(&self) -> Option<u64> { None }
}