Skip to content

Commit 0b7a49a

Browse files
authored
add blob expiry support (#913)
1 parent 4635491 commit 0b7a49a

File tree

9 files changed

+170
-2
lines changed

9 files changed

+170
-2
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use azure_storage::core::prelude::*;
2+
use azure_storage_blobs::prelude::*;
3+
4+
#[tokio::main]
5+
async fn main() -> azure_core::Result<()> {
6+
// First we retrieve the account name and access key from environment variables.
7+
let account =
8+
std::env::var("STORAGE_ACCOUNT").expect("Set env variable STORAGE_ACCOUNT first!");
9+
let master_key =
10+
std::env::var("STORAGE_ACCESS_KEY").expect("Set env variable STORAGE_ACCESS_KEY first!");
11+
12+
let container = std::env::args()
13+
.nth(1)
14+
.expect("please specify container name as command line parameter");
15+
let blob = std::env::args()
16+
.nth(2)
17+
.expect("please specify blob name as command line parameter");
18+
19+
let storage_client = StorageClient::new_access_key(&account, &master_key);
20+
21+
let blob_client = storage_client
22+
.container_client(&container)
23+
.blob_client(&blob);
24+
25+
let response = blob_client
26+
.set_blob_expiry(BlobExpiry::RelativeToNow(1000 * 60 * 60 * 24 * 100))
27+
.into_future()
28+
.await?;
29+
30+
println!("response: {:?}", response);
31+
32+
Ok(())
33+
}

sdk/storage_blobs/examples/set_blob_properties_00.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ use azure_storage_blobs::prelude::*;
55

66
#[tokio::main]
77
async fn main() -> azure_core::Result<()> {
8-
// First we retrieve the account name and master key from environment variables.
8+
// First we retrieve the account name and access key from environment variables.
99
let account =
1010
std::env::var("STORAGE_ACCOUNT").expect("Set env variable STORAGE_ACCOUNT first!");
1111
let master_key =
12-
std::env::var("STORAGE_MASTER_KEY").expect("Set env variable STORAGE_MASTER_KEY first!");
12+
std::env::var("STORAGE_ACCESS_KEY").expect("Set env variable STORAGE_ACCESS_KEY first!");
1313

1414
let container = std::env::args()
1515
.nth(1)

sdk/storage_blobs/src/blob/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ pub struct BlobProperties {
186186
pub remaining_retention_days: Option<u32>,
187187
pub tag_count: Option<u32>,
188188
pub rehydrate_priority: Option<RehydratePriority>,
189+
#[serde(default)]
190+
#[serde(rename = "Expiry-Time")]
191+
#[serde(with = "azure_core::parsing::rfc2822_time_format_optional")]
192+
pub expiry_time: Option<DateTime<Utc>>,
189193
#[serde(flatten)]
190194
extra: HashMap<String, String>, // For debug purposes, should be compiled out in the future
191195
}
@@ -298,6 +302,7 @@ impl Blob {
298302
remaining_retention_days: None, // TODO: Not present or documentation bug?
299303
tag_count: None, // TODO
300304
rehydrate_priority: None, // TODO
305+
expiry_time: None,
301306
extra: HashMap::new(),
302307
},
303308
metadata,

sdk/storage_blobs/src/blob/operations/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mod put_page_blob;
2121
mod release_lease;
2222
mod renew_lease;
2323
mod set_blob_tier;
24+
mod set_expiry;
2425
mod set_metadata;
2526
mod set_properties;
2627
mod update_page;
@@ -47,6 +48,7 @@ pub use put_page_blob::*;
4748
pub use release_lease::*;
4849
pub use renew_lease::*;
4950
pub use set_blob_tier::*;
51+
pub use set_expiry::*;
5052
pub use set_metadata::*;
5153
pub use set_properties::*;
5254
pub use update_page::*;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use crate::prelude::*;
2+
use azure_core::{headers::*, prelude::*, RequestId, Response};
3+
use std::convert::{TryFrom, TryInto};
4+
5+
#[derive(Debug, Clone)]
6+
pub struct SetBlobExpiryBuilder {
7+
blob_client: BlobClient,
8+
blob_expiry: BlobExpiry,
9+
lease_id: Option<LeaseId>,
10+
context: Context,
11+
}
12+
13+
impl SetBlobExpiryBuilder {
14+
pub(crate) fn new(blob_client: BlobClient, expiry: BlobExpiry) -> Self {
15+
Self {
16+
blob_client,
17+
blob_expiry: expiry,
18+
lease_id: None,
19+
context: Context::new(),
20+
}
21+
}
22+
23+
setters! {
24+
lease_id: LeaseId => Some(lease_id),
25+
context: Context => context,
26+
}
27+
28+
pub fn into_future(mut self) -> FutureResponse {
29+
Box::pin(async move {
30+
let mut url = self.blob_client.url_with_segments(None)?;
31+
url.query_pairs_mut().append_pair("comp", "expiry");
32+
33+
let mut headers = self.blob_expiry.to_headers();
34+
headers.add(self.lease_id);
35+
36+
let mut request =
37+
self.blob_client
38+
.finalize_request(url, azure_core::Method::Put, headers, None)?;
39+
40+
let response = self
41+
.blob_client
42+
.send(&mut self.context, &mut request)
43+
.await?;
44+
response.try_into()
45+
})
46+
}
47+
}
48+
49+
#[derive(Debug, Clone)]
50+
pub struct SetBlobExpiryResponse {
51+
pub request_id: RequestId,
52+
pub client_request_id: Option<String>,
53+
pub version: String,
54+
}
55+
56+
impl TryFrom<Response> for SetBlobExpiryResponse {
57+
type Error = crate::Error;
58+
59+
fn try_from(response: Response) -> Result<Self, Self::Error> {
60+
let headers = response.headers();
61+
Ok(SetBlobExpiryResponse {
62+
request_id: request_id_from_headers(headers)?,
63+
client_request_id: client_request_id_from_headers_optional(headers),
64+
version: version_from_headers(headers)?,
65+
})
66+
}
67+
}
68+
69+
pub type FutureResponse =
70+
futures::future::BoxFuture<'static, azure_core::Result<SetBlobExpiryResponse>>;
71+
#[cfg(feature = "into_future")]
72+
impl std::future::IntoFuture for SetBlobExpiryBuilder {
73+
type IntoFuture = Response;
74+
type Output = <Response as std::future::Future>::Output;
75+
fn into_future(self) -> Self::IntoFuture {
76+
Self::into_future(self)
77+
}
78+
}

sdk/storage_blobs/src/clients/blob_client.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ impl BlobClient {
106106
SetBlobTierBuilder::new(self.clone())
107107
}
108108

109+
/// Set an expiry time on an existing blob.
110+
///
111+
/// This operation is only allowed on Hierarchical Namespace enabled
112+
/// accounts.
113+
///
114+
/// Ref: https://docs.microsoft.com/en-us/rest/api/storageservices/set-blob-expiry
115+
pub fn set_blob_expiry(&self, blob_expiry: BlobExpiry) -> SetBlobExpiryBuilder {
116+
SetBlobExpiryBuilder::new(self.clone(), blob_expiry)
117+
}
118+
109119
pub fn update_page(
110120
&self,
111121
ba512_range: BA512Range,

sdk/storage_blobs/src/container/operations/list_blobs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ mod tests {
211211
<Properties>
212212
<Creation-Time>Thu, 01 Jul 2021 10:44:59 GMT</Creation-Time>
213213
<Last-Modified>Thu, 01 Jul 2021 10:44:59 GMT</Last-Modified>
214+
<Expiry-Time>Thu, 07 Jul 2022 14:38:48 GMT</Expiry-Time>
214215
<Etag>0x8D93C7D4629C227</Etag>
215216
<Content-Length>8</Content-Length>
216217
<Content-Type>text/plain</Content-Type>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use azure_core::headers::Headers;
2+
use chrono::{DateTime, Utc};
3+
4+
const EXPIRY_TIME: &str = "x-ms-expiry-time";
5+
const EXPIRY_OPTION: &str = "x-ms-expiry-option";
6+
7+
#[derive(Debug, Clone)]
8+
pub enum BlobExpiry {
9+
RelativeToCreation(u64),
10+
RelativeToNow(u64),
11+
Absolute(DateTime<Utc>),
12+
NeverExpire,
13+
}
14+
15+
impl BlobExpiry {
16+
pub fn to_headers(&self) -> Headers {
17+
let mut headers = Headers::new();
18+
match self {
19+
BlobExpiry::RelativeToCreation(duration) => {
20+
headers.insert(EXPIRY_OPTION, "RelativeToCreation");
21+
headers.insert(EXPIRY_TIME, duration.to_string());
22+
}
23+
BlobExpiry::RelativeToNow(duration) => {
24+
headers.insert(EXPIRY_OPTION, "RelativeToNow");
25+
headers.insert(EXPIRY_TIME, duration.to_string());
26+
}
27+
BlobExpiry::Absolute(date) => {
28+
headers.insert(EXPIRY_OPTION, "Abosolute");
29+
headers.insert(EXPIRY_TIME, date.to_rfc2822());
30+
}
31+
BlobExpiry::NeverExpire => {
32+
headers.insert(EXPIRY_OPTION, "NeverExpire");
33+
}
34+
}
35+
headers
36+
}
37+
}

sdk/storage_blobs/src/options/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod blob_content_encoding;
88
mod blob_content_language;
99
mod blob_content_md5;
1010
mod blob_content_type;
11+
mod blob_expiry;
1112
mod blob_versioning;
1213
mod block_id;
1314
mod condition_append_position;
@@ -26,6 +27,7 @@ pub use blob_content_encoding::BlobContentEncoding;
2627
pub use blob_content_language::BlobContentLanguage;
2728
pub use blob_content_md5::BlobContentMD5;
2829
pub use blob_content_type::BlobContentType;
30+
pub use blob_expiry::BlobExpiry;
2931
pub use blob_versioning::BlobVersioning;
3032
pub use block_id::BlockId;
3133
pub use condition_append_position::ConditionAppendPosition;

0 commit comments

Comments
 (0)