Added support for Azure storage emulator

This commit is contained in:
Francesco Cogno 2018-11-05 20:37:41 +01:00
Родитель 1e5be2a780
Коммит 2218a66f78
18 изменённых файлов: 170 добавлений и 85 удалений

Просмотреть файл

@ -1,6 +1,6 @@
[package] [package]
name = "azure_sdk_for_rust" name = "azure_sdk_for_rust"
version = "0.10.0" version = "0.10.1"
description = "Rust wrappers around Microsoft Azure REST APIs" description = "Rust wrappers around Microsoft Azure REST APIs"
readme = "README.md" readme = "README.md"
authors = ["Francesco Cogno <francesco.cogno@outlook.com>", "Max Gortman <mgortman@microsoft.com>", "Dong Liu <doliu@microsoft.com>"] authors = ["Francesco Cogno <francesco.cogno@outlook.com>", "Max Gortman <mgortman@microsoft.com>", "Dong Liu <doliu@microsoft.com>"]
@ -41,3 +41,4 @@ tokio-core = "0.1"
[features] [features]
test_e2e = [] test_e2e = []
emulator = []

82
examples/container02.rs Normal file
Просмотреть файл

@ -0,0 +1,82 @@
extern crate azure_sdk_for_rust;
extern crate chrono;
extern crate env_logger;
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate log;
extern crate md5;
extern crate tokio_core;
use azure_sdk_for_rust::prelude::*;
use azure_sdk_for_rust::storage::container::PublicAccess;
use futures::future::*;
use std::error::Error;
use tokio_core::reactor::Core;
fn main() {
env_logger::init();
code().unwrap();
}
// We run a separate method to use the elegant quotation mark operator.
// A series of unwrap(), unwrap() would have achieved the same result.
fn code() -> Result<(), Box<Error>> {
let mut core = Core::new()?;
// this will only work with the emulator
let client = Client::new("", "")?;
// create container
let future = client
.create_container()
.with_container_name("emulcont")
.with_public_access(PublicAccess::None)
.finalize();
core.run(future.map(|res| println!("{:?}", res)))?;
//let data = b"something";
//// this is not mandatory but it helps preventing
//// spurious data to be uploaded.
//let digest = md5::compute(&data[..]);
//let future = client
// .put_block_blob()
// .with_container_name(&container_name)
// .with_blob_name("blob0.txt")
// .with_content_type("text/plain")
// .with_body(&data[..])
// .with_content_md5(&digest[..])
// .finalize();
//core.run(future.map(|res| println!("{:?}", res)))?;
//let future = client
// .put_block_blob()
// .with_container_name(&container_name)
// .with_blob_name("blob1.txt")
// .with_content_type("text/plain")
// .with_body(&data[..])
// .with_content_md5(&digest[..])
// .finalize();
//core.run(future.map(|res| println!("{:?}", res)))?;
//let future = client
// .put_block_blob()
// .with_container_name(&container_name)
// .with_blob_name("blob2.txt")
// .with_content_type("text/plain")
// .with_body(&data[..])
// .with_content_md5(&digest[..])
// .finalize();
//core.run(future.map(|res| println!("{:?}", res)))?;
//let future = client
// .list_blobs()
// .with_container_name(&container_name)
// .with_include_metadata()
// .finalize();
//core.run(future.map(|res| println!("{:?}", res)))?;
Ok(())
}

Просмотреть файл

@ -119,10 +119,12 @@ pub fn find_subnodes<'a>(node: &'a Element, subnode: &str) -> Vec<&'a Element> {
.filter(|x| match **x { .filter(|x| match **x {
ElementNode(ref mynode) => mynode.name == subnode, ElementNode(ref mynode) => mynode.name == subnode,
_ => false, _ => false,
}).map(|x| match *x { })
.map(|x| match *x {
ElementNode(ref mynode) => mynode, ElementNode(ref mynode) => mynode,
_ => unreachable!(), _ => unreachable!(),
}).collect::<Vec<_>>() })
.collect::<Vec<_>>()
} }
#[inline] #[inline]

Просмотреть файл

@ -224,7 +224,8 @@ impl Blob {
.ok_or_else(|| { .ok_or_else(|| {
static CL: header::HeaderName = header::CONTENT_LENGTH; static CL: header::HeaderName = header::CONTENT_LENGTH;
AzureError::HeaderNotFound(CL.as_str().to_owned()) AzureError::HeaderNotFound(CL.as_str().to_owned())
})?.to_str()? })?
.to_str()?
.parse::<u64>()?; .parse::<u64>()?;
trace!("content_length == {:?}", content_length); trace!("content_length == {:?}", content_length);
@ -370,15 +371,15 @@ where
{ {
match params { match params {
Some(ref params) => format!( Some(ref params) => format!(
"https://{}.blob.core.windows.net/{}/{}?{}", "{}/{}/{}?{}",
t.client().account(), t.client().blob_uri(),
utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET), utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET),
utf8_percent_encode(t.blob_name(), COMPLETE_ENCODE_SET), utf8_percent_encode(t.blob_name(), COMPLETE_ENCODE_SET),
params params
), ),
None => format!( None => format!(
"https://{}.blob.core.windows.net/{}/{}", "{}/{}/{}",
t.client().account(), t.client().blob_uri(),
utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET), utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET),
utf8_percent_encode(t.blob_name(), COMPLETE_ENCODE_SET) utf8_percent_encode(t.blob_name(), COMPLETE_ENCODE_SET)
), ),

Просмотреть файл

@ -6,10 +6,6 @@ use hyper::{self, Method};
use hyper_tls; use hyper_tls;
use std::borrow::Borrow; use std::borrow::Borrow;
// Can be variant for different cloud environment
const SERVICE_SUFFIX_BLOB: &str = ".blob.core.windows.net";
const SERVICE_SUFFIX_TABLE: &str = ".table.core.windows.net";
pub trait Blob { pub trait Blob {
fn list_blobs<'a>(&'a self) -> blob::requests::ListBlobBuilder<'a, No>; fn list_blobs<'a>(&'a self) -> blob::requests::ListBlobBuilder<'a, No>;
fn get_blob<'a>(&'a self) -> blob::requests::GetBlobBuilder<'a, No, No>; fn get_blob<'a>(&'a self) -> blob::requests::GetBlobBuilder<'a, No, No>;
@ -50,6 +46,8 @@ pub struct Client {
account: String, account: String,
key: String, key: String,
hc: hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>, hc: hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>,
blob_uri: String,
table_uri: String,
} }
impl Blob for Client { impl Blob for Client {
@ -178,11 +176,23 @@ impl Client {
let client = hyper::Client::builder().build(hyper_tls::HttpsConnector::new(4)?); let client = hyper::Client::builder().build(hyper_tls::HttpsConnector::new(4)?);
Ok(Client { if cfg!(feature = "emulator") {
account: account.to_owned(), Ok(Client {
key: key.to_owned(), account: "devstoreaccount1".to_owned(),
hc: client, key: "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==".to_owned(),
}) hc: client,
blob_uri: "http://127.0.0.1:10000/devstoreaccount1".to_owned(),
table_uri: "http://127.0.0.1:10002/devstoreaccount1".to_owned(),
})
} else {
Ok(Client {
account: account.to_owned(),
key: key.to_owned(),
hc: client,
blob_uri: format!("https://{}.blob.core.windows.net", account),
table_uri: format!("https://{}.table.core.windows.net", account),
})
}
} }
pub fn account(&self) -> &str { pub fn account(&self) -> &str {
@ -193,6 +203,16 @@ impl Client {
&self.key &self.key
} }
#[inline]
pub(crate) fn blob_uri(&self) -> &str {
&self.blob_uri
}
#[inline]
pub(crate) fn table_uri(&self) -> &str {
&self.table_uri
}
pub(crate) fn perform_request<F>( pub(crate) fn perform_request<F>(
&self, &self,
uri: &str, uri: &str,
@ -230,12 +250,9 @@ impl Client {
/// Uri scheme + authority e.g. http://myaccount.table.core.windows.net/ /// Uri scheme + authority e.g. http://myaccount.table.core.windows.net/
pub(crate) fn get_uri_prefix(&self, service_type: ServiceType) -> String { pub(crate) fn get_uri_prefix(&self, service_type: ServiceType) -> String {
"https://".to_owned() match service_type {
+ self.account() ServiceType::Blob => format!("{}/", self.blob_uri()),
+ match service_type { ServiceType::Table => format!("{}/", self.table_uri()),
ServiceType::Blob => SERVICE_SUFFIX_BLOB, }
ServiceType::Table => SERVICE_SUFFIX_TABLE,
}
+ "/"
} }
} }

Просмотреть файл

@ -1,8 +0,0 @@
pub struct CreateRequest<N> {
name: String,
timeout: Option<u64>
}
impl CreateRequest

Просмотреть файл

@ -229,14 +229,14 @@ where
{ {
match params { match params {
Some(ref params) => format!( Some(ref params) => format!(
"https://{}.blob.core.windows.net/{}?{}", "{}/{}?{}",
t.client().account(), t.client().blob_uri(),
utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET), utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET),
params params
), ),
None => format!( None => format!(
"https://{}.blob.core.windows.net/{}", "{}/{}",
t.client().account(), t.client().blob_uri(),
utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET), utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET),
), ),
} }

Просмотреть файл

@ -250,13 +250,14 @@ impl<'a, ContainerNameSet, LeaseDurationSet> AcquireLeaseBuilder<'a, ContainerNa
where where
ContainerNameSet: ToAssign, ContainerNameSet: ToAssign,
LeaseDurationSet: ToAssign, LeaseDurationSet: ToAssign,
{} {
}
impl<'a> AcquireLeaseBuilder<'a, Yes, Yes> { impl<'a> AcquireLeaseBuilder<'a, Yes, Yes> {
pub fn finalize(self) -> impl Future<Item = AcquireLeaseResponse, Error = AzureError> { pub fn finalize(self) -> impl Future<Item = AcquireLeaseResponse, Error = AzureError> {
let mut uri = format!( let mut uri = format!(
"https://{}.blob.core.windows.net/{}?comp=lease&restype=container", "{}/{}?comp=lease&restype=container",
self.client().account(), self.client().blob_uri(),
self.container_name() self.container_name()
); );

Просмотреть файл

@ -192,8 +192,8 @@ impl<'a, ContainerNameSet> BreakLeaseBuilder<'a, ContainerNameSet> where Contain
impl<'a> BreakLeaseBuilder<'a, Yes> { impl<'a> BreakLeaseBuilder<'a, Yes> {
pub fn finalize(self) -> impl Future<Item = BreakLeaseResponse, Error = AzureError> { pub fn finalize(self) -> impl Future<Item = BreakLeaseResponse, Error = AzureError> {
let mut uri = format!( let mut uri = format!(
"https://{}.blob.core.windows.net/{}?comp=lease&restype=container", "{}/{}?comp=lease&restype=container",
self.client().account(), self.client().blob_uri(),
self.container_name() self.container_name()
); );

Просмотреть файл

@ -165,7 +165,8 @@ impl<'a, ContainerNameSet, PublicAccessSet> CreateBuilder<'a, ContainerNameSet,
where where
ContainerNameSet: ToAssign, ContainerNameSet: ToAssign,
PublicAccessSet: ToAssign, PublicAccessSet: ToAssign,
{} {
}
impl<'a, ContainerNameSet, PublicAccessSet> ContainerNameSupport<'a> for CreateBuilder<'a, ContainerNameSet, PublicAccessSet> impl<'a, ContainerNameSet, PublicAccessSet> ContainerNameSupport<'a> for CreateBuilder<'a, ContainerNameSet, PublicAccessSet>
where where
@ -214,11 +215,8 @@ impl<'a> CreateBuilder<'a, No, No> {
impl<'a> CreateBuilder<'a, Yes, Yes> { impl<'a> CreateBuilder<'a, Yes, Yes> {
pub fn finalize(self) -> impl Future<Item = (), Error = AzureError> { pub fn finalize(self) -> impl Future<Item = (), Error = AzureError> {
let mut uri = format!( let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name());
"https://{}.blob.core.windows.net/{}?restype=container", println!("{}", uri);
self.client().account(),
self.container_name()
);
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
uri = format!("{}&{}", uri, nm); uri = format!("{}&{}", uri, nm);

Просмотреть файл

@ -146,11 +146,7 @@ impl<'a> DeleteBuilder<'a, No> {
impl<'a> DeleteBuilder<'a, Yes> { impl<'a> DeleteBuilder<'a, Yes> {
pub fn finalize(self) -> impl Future<Item = (), Error = AzureError> { pub fn finalize(self) -> impl Future<Item = (), Error = AzureError> {
let mut uri = format!( let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name());
"https://{}.blob.core.windows.net/{}?restype=container",
self.client().account(),
self.container_name()
);
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
uri = format!("{}&{}", uri, nm); uri = format!("{}&{}", uri, nm);

Просмотреть файл

@ -72,11 +72,7 @@ where
impl<'a> GetACLBuilder<'a, Yes> { impl<'a> GetACLBuilder<'a, Yes> {
pub fn finalize(self) -> impl Future<Item = GetACLResponse, Error = AzureError> { pub fn finalize(self) -> impl Future<Item = GetACLResponse, Error = AzureError> {
let mut uri = format!( let mut uri = format!("{}/{}?restype=container&comp=acl", self.client().blob_uri(), self.container_name());
"https://{}.blob.core.windows.net/{}?restype=container&comp=acl",
self.client().account(),
self.container_name()
);
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
uri = format!("{}&{}", uri, nm); uri = format!("{}&{}", uri, nm);

Просмотреть файл

@ -72,11 +72,7 @@ where
impl<'a> GetPropertiesBuilder<'a, Yes> { impl<'a> GetPropertiesBuilder<'a, Yes> {
pub fn finalize(self) -> impl Future<Item = GetPropertiesResponse, Error = AzureError> { pub fn finalize(self) -> impl Future<Item = GetPropertiesResponse, Error = AzureError> {
let mut uri = format!( let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name());
"https://{}.blob.core.windows.net/{}?restype=container",
self.client().account(),
self.container_name()
);
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
uri = format!("{}&{}", uri, nm); uri = format!("{}&{}", uri, nm);

Просмотреть файл

@ -78,11 +78,7 @@ impl<'a> ListBuilder<'a> {
} }
pub fn finalize(self) -> impl Future<Item = ListContainersResponse, Error = AzureError> { pub fn finalize(self) -> impl Future<Item = ListContainersResponse, Error = AzureError> {
let mut uri = format!( let mut uri = format!("{}?comp=list&maxresults={}", self.client().blob_uri(), self.max_results());
"https://{}.blob.core.windows.net?comp=list&maxresults={}",
self.client().account(),
self.max_results()
);
if self.is_metadata_included() { if self.is_metadata_included() {
uri = format!("{}&include=metadata", uri); uri = format!("{}&include=metadata", uri);

Просмотреть файл

@ -174,13 +174,14 @@ impl<'a, ContainerNameSet, LeaseIdSet> ReleaseLeaseBuilder<'a, ContainerNameSet,
where where
ContainerNameSet: ToAssign, ContainerNameSet: ToAssign,
LeaseIdSet: ToAssign, LeaseIdSet: ToAssign,
{} {
}
impl<'a> ReleaseLeaseBuilder<'a, Yes, Yes> { impl<'a> ReleaseLeaseBuilder<'a, Yes, Yes> {
pub fn finalize(self) -> impl Future<Item = ReleaseLeaseResponse, Error = AzureError> { pub fn finalize(self) -> impl Future<Item = ReleaseLeaseResponse, Error = AzureError> {
let mut uri = format!( let mut uri = format!(
"https://{}.blob.core.windows.net/{}?comp=lease&restype=container", "{}/{}?comp=lease&restype=container",
self.client().account(), self.client().blob_uri(),
self.container_name() self.container_name()
); );

Просмотреть файл

@ -174,13 +174,14 @@ impl<'a, ContainerNameSet, LeaseIdSet> RenewLeaseBuilder<'a, ContainerNameSet, L
where where
ContainerNameSet: ToAssign, ContainerNameSet: ToAssign,
LeaseIdSet: ToAssign, LeaseIdSet: ToAssign,
{} {
}
impl<'a> RenewLeaseBuilder<'a, Yes, Yes> { impl<'a> RenewLeaseBuilder<'a, Yes, Yes> {
pub fn finalize(self) -> impl Future<Item = RenewLeaseResponse, Error = AzureError> { pub fn finalize(self) -> impl Future<Item = RenewLeaseResponse, Error = AzureError> {
let mut uri = format!( let mut uri = format!(
"https://{}.blob.core.windows.net/{}?comp=lease&restype=container", "{}/{}?comp=lease&restype=container",
self.client().account(), self.client().blob_uri(),
self.container_name() self.container_name()
); );

Просмотреть файл

@ -112,11 +112,7 @@ where
impl<'a> SetACLBuilder<'a, Yes, Yes> { impl<'a> SetACLBuilder<'a, Yes, Yes> {
pub fn finalize(self) -> impl Future<Item = PublicAccess, Error = AzureError> { pub fn finalize(self) -> impl Future<Item = PublicAccess, Error = AzureError> {
let mut uri = format!( let mut uri = format!("{}/{}?restype=container&comp=acl", self.client().blob_uri(), self.container_name());
"https://{}.blob.core.windows.net/{}?restype=container&comp=acl",
self.client().account(),
self.container_name()
);
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
uri = format!("{}&{}", uri, nm); uri = format!("{}&{}", uri, nm);

Просмотреть файл

@ -67,7 +67,8 @@ fn string_to_sign(h: &HeaderMap, u: &url::Url, method: &Method, service_type: Se
add_if_exists(h, header::CONTENT_TYPE), add_if_exists(h, header::CONTENT_TYPE),
add_if_exists(h, HEADER_DATE), add_if_exists(h, HEADER_DATE),
canonicalized_resource_table(u) canonicalized_resource_table(u)
).unwrap(); )
.unwrap();
s s
} }
_ => { _ => {
@ -95,7 +96,8 @@ fn string_to_sign(h: &HeaderMap, u: &url::Url, method: &Method, service_type: Se
add_if_exists(h, header::RANGE), add_if_exists(h, header::RANGE),
canonicalize_header(h), canonicalize_header(h),
canonicalized_resource(u) canonicalized_resource(u)
).unwrap(); )
.unwrap();
s s
} }
} }
@ -139,13 +141,17 @@ fn canonicalize_header(h: &HeaderMap) -> String {
} }
#[inline] #[inline]
fn get_account(u: &url::Url) -> String { fn get_account(u: &url::Url) -> &str {
match u.host().unwrap().clone() { match u.host().unwrap().clone() {
url::Host::Domain(dm) => { url::Host::Domain(dm) => {
// debug!("dom == {:?}", dm); // debug!("dom == {:?}", dm);
let first_dot = dm.find('.').unwrap(); let first_dot = dm.find('.').unwrap();
String::from(&dm[0..first_dot]) &dm[0..first_dot]
}
url::Host::Ipv4(_) => {
// this must be the emulator
"devstoreaccount1"
} }
_ => panic!("only Domains are supported in canonicalized_resource"), _ => panic!("only Domains are supported in canonicalized_resource"),
} }
@ -341,7 +347,8 @@ Wed, 03 May 2017 14:04:56 GMT
let url = url::Url::parse( let url = url::Url::parse(
"http://myaccount.blob.core.windows.\ "http://myaccount.blob.core.windows.\
net/mycontainer?restype=container&comp=metadata", net/mycontainer?restype=container&comp=metadata",
).unwrap(); )
.unwrap();
assert_eq!( assert_eq!(
super::canonicalized_resource(&url), super::canonicalized_resource(&url),
"/myaccount/mycontainer\ncomp:metadata\nrestype:container" "/myaccount/mycontainer\ncomp:metadata\nrestype:container"
@ -354,7 +361,8 @@ Wed, 03 May 2017 14:04:56 GMT
"http://myaccount.blob.core.windows.\ "http://myaccount.blob.core.windows.\
net/mycontainer?restype=container&comp=list&include=snapshots&\ net/mycontainer?restype=container&comp=list&include=snapshots&\
include=metadata&include=uncommittedblobs", include=metadata&include=uncommittedblobs",
).unwrap(); )
.unwrap();
assert_eq!( assert_eq!(
super::canonicalized_resource(&url), super::canonicalized_resource(&url),
"/myaccount/mycontainer\ncomp:list\ninclude:metadata,snapshots,\ "/myaccount/mycontainer\ncomp:list\ninclude:metadata,snapshots,\
@ -367,7 +375,8 @@ Wed, 03 May 2017 14:04:56 GMT
let url = url::Url::parse( let url = url::Url::parse(
"https://myaccount-secondary.blob.core.windows.\ "https://myaccount-secondary.blob.core.windows.\
net/mycontainer/myblob", net/mycontainer/myblob",
).unwrap(); )
.unwrap();
assert_eq!(super::canonicalized_resource(&url), "/myaccount-secondary/mycontainer/myblob"); assert_eq!(super::canonicalized_resource(&url), "/myaccount-secondary/mycontainer/myblob");
} }