Added support for Azure storage emulator
This commit is contained in:
Родитель
1e5be2a780
Коммит
2218a66f78
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "azure_sdk_for_rust"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
description = "Rust wrappers around Microsoft Azure REST APIs"
|
||||
readme = "README.md"
|
||||
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]
|
||||
test_e2e = []
|
||||
emulator = []
|
||||
|
|
|
@ -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 {
|
||||
ElementNode(ref mynode) => mynode.name == subnode,
|
||||
_ => false,
|
||||
}).map(|x| match *x {
|
||||
})
|
||||
.map(|x| match *x {
|
||||
ElementNode(ref mynode) => mynode,
|
||||
_ => unreachable!(),
|
||||
}).collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -224,7 +224,8 @@ impl Blob {
|
|||
.ok_or_else(|| {
|
||||
static CL: header::HeaderName = header::CONTENT_LENGTH;
|
||||
AzureError::HeaderNotFound(CL.as_str().to_owned())
|
||||
})?.to_str()?
|
||||
})?
|
||||
.to_str()?
|
||||
.parse::<u64>()?;
|
||||
trace!("content_length == {:?}", content_length);
|
||||
|
||||
|
@ -370,15 +371,15 @@ where
|
|||
{
|
||||
match params {
|
||||
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.blob_name(), COMPLETE_ENCODE_SET),
|
||||
params
|
||||
),
|
||||
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.blob_name(), COMPLETE_ENCODE_SET)
|
||||
),
|
||||
|
|
|
@ -6,10 +6,6 @@ use hyper::{self, Method};
|
|||
use hyper_tls;
|
||||
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 {
|
||||
fn list_blobs<'a>(&'a self) -> blob::requests::ListBlobBuilder<'a, No>;
|
||||
fn get_blob<'a>(&'a self) -> blob::requests::GetBlobBuilder<'a, No, No>;
|
||||
|
@ -50,6 +46,8 @@ pub struct Client {
|
|||
account: String,
|
||||
key: String,
|
||||
hc: hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>,
|
||||
blob_uri: String,
|
||||
table_uri: String,
|
||||
}
|
||||
|
||||
impl Blob for Client {
|
||||
|
@ -178,11 +176,23 @@ impl Client {
|
|||
|
||||
let client = hyper::Client::builder().build(hyper_tls::HttpsConnector::new(4)?);
|
||||
|
||||
Ok(Client {
|
||||
account: account.to_owned(),
|
||||
key: key.to_owned(),
|
||||
hc: client,
|
||||
})
|
||||
if cfg!(feature = "emulator") {
|
||||
Ok(Client {
|
||||
account: "devstoreaccount1".to_owned(),
|
||||
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 {
|
||||
|
@ -193,6 +203,16 @@ impl Client {
|
|||
&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>(
|
||||
&self,
|
||||
uri: &str,
|
||||
|
@ -230,12 +250,9 @@ impl Client {
|
|||
|
||||
/// Uri scheme + authority e.g. http://myaccount.table.core.windows.net/
|
||||
pub(crate) fn get_uri_prefix(&self, service_type: ServiceType) -> String {
|
||||
"https://".to_owned()
|
||||
+ self.account()
|
||||
+ match service_type {
|
||||
ServiceType::Blob => SERVICE_SUFFIX_BLOB,
|
||||
ServiceType::Table => SERVICE_SUFFIX_TABLE,
|
||||
}
|
||||
+ "/"
|
||||
match service_type {
|
||||
ServiceType::Blob => format!("{}/", self.blob_uri()),
|
||||
ServiceType::Table => format!("{}/", self.table_uri()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
|
||||
pub struct CreateRequest<N> {
|
||||
name: String,
|
||||
timeout: Option<u64>
|
||||
}
|
||||
|
||||
impl CreateRequest
|
|
@ -229,14 +229,14 @@ where
|
|||
{
|
||||
match params {
|
||||
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),
|
||||
params
|
||||
),
|
||||
None => format!(
|
||||
"https://{}.blob.core.windows.net/{}",
|
||||
t.client().account(),
|
||||
"{}/{}",
|
||||
t.client().blob_uri(),
|
||||
utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET),
|
||||
),
|
||||
}
|
||||
|
|
|
@ -250,13 +250,14 @@ impl<'a, ContainerNameSet, LeaseDurationSet> AcquireLeaseBuilder<'a, ContainerNa
|
|||
where
|
||||
ContainerNameSet: ToAssign,
|
||||
LeaseDurationSet: ToAssign,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a> AcquireLeaseBuilder<'a, Yes, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = AcquireLeaseResponse, Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?comp=lease&restype=container",
|
||||
self.client().account(),
|
||||
"{}/{}?comp=lease&restype=container",
|
||||
self.client().blob_uri(),
|
||||
self.container_name()
|
||||
);
|
||||
|
||||
|
|
|
@ -192,8 +192,8 @@ impl<'a, ContainerNameSet> BreakLeaseBuilder<'a, ContainerNameSet> where Contain
|
|||
impl<'a> BreakLeaseBuilder<'a, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = BreakLeaseResponse, Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?comp=lease&restype=container",
|
||||
self.client().account(),
|
||||
"{}/{}?comp=lease&restype=container",
|
||||
self.client().blob_uri(),
|
||||
self.container_name()
|
||||
);
|
||||
|
||||
|
|
|
@ -165,7 +165,8 @@ impl<'a, ContainerNameSet, PublicAccessSet> CreateBuilder<'a, ContainerNameSet,
|
|||
where
|
||||
ContainerNameSet: ToAssign,
|
||||
PublicAccessSet: ToAssign,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, ContainerNameSet, PublicAccessSet> ContainerNameSupport<'a> for CreateBuilder<'a, ContainerNameSet, PublicAccessSet>
|
||||
where
|
||||
|
@ -214,11 +215,8 @@ impl<'a> CreateBuilder<'a, No, No> {
|
|||
|
||||
impl<'a> CreateBuilder<'a, Yes, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = (), Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?restype=container",
|
||||
self.client().account(),
|
||||
self.container_name()
|
||||
);
|
||||
let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name());
|
||||
println!("{}", uri);
|
||||
|
||||
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
|
||||
uri = format!("{}&{}", uri, nm);
|
||||
|
|
|
@ -146,11 +146,7 @@ impl<'a> DeleteBuilder<'a, No> {
|
|||
|
||||
impl<'a> DeleteBuilder<'a, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = (), Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?restype=container",
|
||||
self.client().account(),
|
||||
self.container_name()
|
||||
);
|
||||
let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name());
|
||||
|
||||
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
|
||||
uri = format!("{}&{}", uri, nm);
|
||||
|
|
|
@ -72,11 +72,7 @@ where
|
|||
|
||||
impl<'a> GetACLBuilder<'a, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = GetACLResponse, Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?restype=container&comp=acl",
|
||||
self.client().account(),
|
||||
self.container_name()
|
||||
);
|
||||
let mut uri = format!("{}/{}?restype=container&comp=acl", self.client().blob_uri(), self.container_name());
|
||||
|
||||
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
|
||||
uri = format!("{}&{}", uri, nm);
|
||||
|
|
|
@ -72,11 +72,7 @@ where
|
|||
|
||||
impl<'a> GetPropertiesBuilder<'a, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = GetPropertiesResponse, Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?restype=container",
|
||||
self.client().account(),
|
||||
self.container_name()
|
||||
);
|
||||
let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name());
|
||||
|
||||
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
|
||||
uri = format!("{}&{}", uri, nm);
|
||||
|
|
|
@ -78,11 +78,7 @@ impl<'a> ListBuilder<'a> {
|
|||
}
|
||||
|
||||
pub fn finalize(self) -> impl Future<Item = ListContainersResponse, Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net?comp=list&maxresults={}",
|
||||
self.client().account(),
|
||||
self.max_results()
|
||||
);
|
||||
let mut uri = format!("{}?comp=list&maxresults={}", self.client().blob_uri(), self.max_results());
|
||||
|
||||
if self.is_metadata_included() {
|
||||
uri = format!("{}&include=metadata", uri);
|
||||
|
|
|
@ -174,13 +174,14 @@ impl<'a, ContainerNameSet, LeaseIdSet> ReleaseLeaseBuilder<'a, ContainerNameSet,
|
|||
where
|
||||
ContainerNameSet: ToAssign,
|
||||
LeaseIdSet: ToAssign,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a> ReleaseLeaseBuilder<'a, Yes, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = ReleaseLeaseResponse, Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?comp=lease&restype=container",
|
||||
self.client().account(),
|
||||
"{}/{}?comp=lease&restype=container",
|
||||
self.client().blob_uri(),
|
||||
self.container_name()
|
||||
);
|
||||
|
||||
|
|
|
@ -174,13 +174,14 @@ impl<'a, ContainerNameSet, LeaseIdSet> RenewLeaseBuilder<'a, ContainerNameSet, L
|
|||
where
|
||||
ContainerNameSet: ToAssign,
|
||||
LeaseIdSet: ToAssign,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a> RenewLeaseBuilder<'a, Yes, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = RenewLeaseResponse, Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?comp=lease&restype=container",
|
||||
self.client().account(),
|
||||
"{}/{}?comp=lease&restype=container",
|
||||
self.client().blob_uri(),
|
||||
self.container_name()
|
||||
);
|
||||
|
||||
|
|
|
@ -112,11 +112,7 @@ where
|
|||
|
||||
impl<'a> SetACLBuilder<'a, Yes, Yes> {
|
||||
pub fn finalize(self) -> impl Future<Item = PublicAccess, Error = AzureError> {
|
||||
let mut uri = format!(
|
||||
"https://{}.blob.core.windows.net/{}?restype=container&comp=acl",
|
||||
self.client().account(),
|
||||
self.container_name()
|
||||
);
|
||||
let mut uri = format!("{}/{}?restype=container&comp=acl", self.client().blob_uri(), self.container_name());
|
||||
|
||||
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
|
||||
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_DATE),
|
||||
canonicalized_resource_table(u)
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
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),
|
||||
canonicalize_header(h),
|
||||
canonicalized_resource(u)
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
s
|
||||
}
|
||||
}
|
||||
|
@ -139,13 +141,17 @@ fn canonicalize_header(h: &HeaderMap) -> String {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn get_account(u: &url::Url) -> String {
|
||||
fn get_account(u: &url::Url) -> &str {
|
||||
match u.host().unwrap().clone() {
|
||||
url::Host::Domain(dm) => {
|
||||
// debug!("dom == {:?}", dm);
|
||||
|
||||
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"),
|
||||
}
|
||||
|
@ -341,7 +347,8 @@ Wed, 03 May 2017 14:04:56 GMT
|
|||
let url = url::Url::parse(
|
||||
"http://myaccount.blob.core.windows.\
|
||||
net/mycontainer?restype=container&comp=metadata",
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
super::canonicalized_resource(&url),
|
||||
"/myaccount/mycontainer\ncomp:metadata\nrestype:container"
|
||||
|
@ -354,7 +361,8 @@ Wed, 03 May 2017 14:04:56 GMT
|
|||
"http://myaccount.blob.core.windows.\
|
||||
net/mycontainer?restype=container&comp=list&include=snapshots&\
|
||||
include=metadata&include=uncommittedblobs",
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
super::canonicalized_resource(&url),
|
||||
"/myaccount/mycontainer\ncomp:list\ninclude:metadata,snapshots,\
|
||||
|
@ -367,7 +375,8 @@ Wed, 03 May 2017 14:04:56 GMT
|
|||
let url = url::Url::parse(
|
||||
"https://myaccount-secondary.blob.core.windows.\
|
||||
net/mycontainer/myblob",
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(super::canonicalized_resource(&url), "/myaccount-secondary/mycontainer/myblob");
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче