JCLOUDS-1335: Azure Blob object access tiers

This commit is contained in:
Andrew Gaul 2017-09-20 20:45:00 -07:00
parent 5facb65a7e
commit fc147dc0c5
13 changed files with 157 additions and 7 deletions

View File

@ -27,6 +27,7 @@ import javax.inject.Named;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@ -45,6 +46,7 @@ import org.jclouds.azureblob.binders.BindAzureBlocksToRequest;
import org.jclouds.azureblob.binders.BindAzureContentMetadataToRequest;
import org.jclouds.azureblob.binders.BindAzureCopyOptionsToRequest;
import org.jclouds.azureblob.binders.BindPublicAccessToRequest;
import org.jclouds.azureblob.domain.AccessTier;
import org.jclouds.azureblob.domain.AzureBlob;
import org.jclouds.azureblob.domain.BlobProperties;
import org.jclouds.azureblob.domain.ContainerProperties;
@ -434,6 +436,14 @@ public interface AzureBlobClient extends Closeable {
@PathParam("container") @ParamValidators(ContainerNameValidator.class) String container,
@PathParam("name") String name, @BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> metadata);
@Named("SetAccessTier")
@PUT
@Path("{container}/{name}")
@QueryParams(keys = { "comp" }, values = { "tier" })
void setBlobTier(
@PathParam("container") @ParamValidators(ContainerNameValidator.class) String container,
@PathParam("name") String name, @HeaderParam("x-ms-access-tier") AccessTier tier);
/**
* The Delete Blob operation marks the specified blob for deletion. The blob is later deleted
* during garbage collection.

View File

@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.azureblob.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.CaseFormat;
public enum AccessTier {
HOT,
COOL,
ARCHIVE;
public String value() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
}
@Override
public String toString() {
return value();
}
public static AccessTier fromValue(String tier) {
return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(tier, "tier")));
}
}

View File

@ -31,6 +31,9 @@ public interface BlobProperties extends Comparable<BlobProperties> {
*/
BlobType getType();
/** @return access tier or null if not set */
AccessTier getTier();
LeaseStatus getLeaseStatus();
URI getUrl();

View File

@ -40,6 +40,8 @@ public interface MutableBlobProperties extends BlobProperties {
* @see ListableContainerProperties#getContainer
*/
void setContainer(String container);
void setTier(AccessTier tier);
/**
* @see ListableContainerProperties#getLastModified

View File

@ -23,6 +23,7 @@ import java.util.Date;
import java.util.Map;
import org.jclouds.azureblob.domain.BlobProperties;
import org.jclouds.azureblob.domain.AccessTier;
import org.jclouds.azureblob.domain.BlobType;
import org.jclouds.azureblob.domain.LeaseStatus;
import org.jclouds.io.ContentMetadata;
@ -37,6 +38,7 @@ import com.google.common.collect.Maps;
public class BlobPropertiesImpl implements BlobProperties {
private final BlobType type;
private final AccessTier tier;
private final String name;
private final String container;
private final URI url;
@ -46,12 +48,21 @@ public class BlobPropertiesImpl implements BlobProperties {
private final LeaseStatus leaseStatus;
private final BaseImmutableContentMetadata contentMetadata;
// TODO: should this take Cache-Control as well?
@Deprecated
public BlobPropertiesImpl(BlobType type, String name, String container, URI url, @Nullable Date lastModified, @Nullable String eTag,
long size, String contentType, @Nullable byte[] contentMD5, @Nullable String contentMetadata,
@Nullable String contentLanguage, @Nullable Date currentExpires, LeaseStatus leaseStatus,
Map<String, String> metadata) {
this(type, null, name, container, url, lastModified, eTag, size, contentType, contentMD5, contentMetadata, contentLanguage, currentExpires, leaseStatus, metadata);
}
// TODO: should this take Cache-Control as well?
public BlobPropertiesImpl(BlobType type, @Nullable AccessTier tier, String name, String container, URI url, @Nullable Date lastModified, @Nullable String eTag,
long size, String contentType, @Nullable byte[] contentMD5, @Nullable String contentMetadata,
@Nullable String contentLanguage, @Nullable Date currentExpires, LeaseStatus leaseStatus,
Map<String, String> metadata) {
this.type = checkNotNull(type, "type");
this.tier = tier;
this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus");
this.name = checkNotNull(name, "name");
this.container = checkNotNull(container, "container");
@ -71,6 +82,11 @@ public class BlobPropertiesImpl implements BlobProperties {
return type;
}
@Override
public AccessTier getTier() {
return tier;
}
/**
*{@inheritDoc}
*/

View File

@ -16,10 +16,13 @@
*/
package org.jclouds.azureblob.domain.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI;
import java.util.Date;
import java.util.Map;
import org.jclouds.azureblob.domain.AccessTier;
import org.jclouds.azureblob.domain.BlobProperties;
import org.jclouds.azureblob.domain.BlobType;
import org.jclouds.azureblob.domain.LeaseStatus;
@ -36,6 +39,7 @@ import com.google.common.collect.Maps;
public class MutableBlobPropertiesImpl implements MutableBlobProperties {
private BlobType type = BlobType.BLOCK_BLOB;
private AccessTier tier;
private LeaseStatus leaseStatus = LeaseStatus.UNLOCKED;
private String name;
@ -77,6 +81,16 @@ public class MutableBlobPropertiesImpl implements MutableBlobProperties {
this.type = type;
}
@Override
public AccessTier getTier() {
return tier;
}
@Override
public void setTier(AccessTier tier) {
this.tier = checkNotNull(tier);
}
/**
*{@inheritDoc}
*/

View File

@ -21,6 +21,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import javax.inject.Inject;
import org.jclouds.azureblob.blobstore.functions.BlobMetadataToBlobProperties;
import org.jclouds.azureblob.domain.AccessTier;
import org.jclouds.azureblob.domain.MutableBlobProperties;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
@ -55,6 +56,10 @@ public class ParseBlobPropertiesFromHeaders implements Function<HttpResponse, Mu
BlobMetadata base = blobMetadataParser.apply(from);
MutableBlobProperties to = blobToBlobProperties.apply(base);
to.setContainer(container);
String tier = from.getFirstHeaderOrNull("x-ms-access-tier");
if (tier != null) {
to.setTier(AccessTier.fromValue(tier));
}
return to;
}

View File

@ -26,6 +26,7 @@ import java.util.Set;
import javax.inject.Inject;
import org.jclouds.azureblob.domain.AccessTier;
import org.jclouds.azureblob.domain.BlobProperties;
import org.jclouds.azureblob.domain.BlobType;
import org.jclouds.azureblob.domain.LeaseStatus;
@ -70,6 +71,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
private String currentContentEncoding;
private String currentContentLanguage;
private BlobType currentBlobType;
private AccessTier currentAccessTier;
private Date currentExpires;
private boolean inBlob;
private boolean inBlobPrefix;
@ -129,16 +131,19 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
nextMarker = (nextMarker.equals("")) ? null : nextMarker;
} else if (qName.equals("BlobType")) {
currentBlobType = BlobType.fromValue(currentText.toString());
} else if (qName.equals("AccessTier")) {
currentAccessTier = AccessTier.fromValue(currentText.toString());
} else if (qName.equals("LeaseStatus")) {
currentLeaseStatus = LeaseStatus.fromValue(currentText.toString());
} else if (qName.equals("Blob")) {
URI currentUrl = uriBuilder(containerUrl).appendPath(Strings2.urlEncode(currentName)).build();
BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentName, containerUrl.getPath().replace("/",
BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentAccessTier, currentName, containerUrl.getPath().replace("/",
""), currentUrl, currentLastModified, currentETag, currentSize, currentContentType,
currentContentMD5, currentContentEncoding, currentContentLanguage, currentExpires,
currentLeaseStatus, currentMetadata);
blobMetadata.add(md);
currentBlobType = null;
currentAccessTier = null;
currentName = null;
currentLastModified = null;
currentETag = null;

View File

@ -38,6 +38,7 @@ import java.util.Set;
import org.jclouds.azure.storage.AzureStorageResponseException;
import org.jclouds.azure.storage.domain.BoundedSet;
import org.jclouds.azure.storage.options.ListOptions;
import org.jclouds.azureblob.domain.AccessTier;
import org.jclouds.azureblob.domain.AzureBlob;
import org.jclouds.azureblob.domain.BlobProperties;
import org.jclouds.azureblob.domain.ContainerProperties;
@ -560,7 +561,7 @@ public class AzureBlobClientLiveTest extends BaseBlobStoreIntegrationTest {
assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read());
}
@Test
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" })
public void testSetBlobProperties() throws Exception {
String blobName = "blob-name";
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
@ -591,4 +592,35 @@ public class AzureBlobClientLiveTest extends BaseBlobStoreIntegrationTest {
assertThat(contentMetadata.getContentLanguage()).isEqualTo(contentLanguage);
assertThat(contentMetadata.getContentType()).isEqualTo(contentType);
}
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" })
public void testSetBlobTier() throws Exception {
String blobName = "tier-blob-name";
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
// create blob
AzureBlob object = getApi().newBlob();
object.getProperties().setName(blobName);
object.setPayload(byteSource.read());
getApi().putBlob(privateContainer, object);
// default
BlobProperties properties = getApi().getBlobProperties(privateContainer, blobName);
assertThat(properties.getTier()).isNull();
// hot
getApi().setBlobTier(privateContainer, blobName, AccessTier.HOT);
properties = getApi().getBlobProperties(privateContainer, blobName);
assertThat(properties.getTier()).isEqualTo(AccessTier.HOT);
// cool
getApi().setBlobTier(privateContainer, blobName, AccessTier.COOL);
properties = getApi().getBlobProperties(privateContainer, blobName);
assertThat(properties.getTier()).isEqualTo(AccessTier.COOL);
// archive
getApi().setBlobTier(privateContainer, blobName, AccessTier.ARCHIVE);
properties = getApi().getBlobProperties(privateContainer, blobName);
assertThat(properties.getTier()).isEqualTo(AccessTier.ARCHIVE);
}
}

View File

@ -33,6 +33,7 @@ import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
import org.jclouds.azure.storage.options.ListOptions;
import org.jclouds.azureblob.AzureBlobFallbacks.FalseIfContainerAlreadyExists;
import org.jclouds.azureblob.domain.AccessTier;
import org.jclouds.azureblob.domain.AzureBlob;
import org.jclouds.azureblob.domain.ListBlobsInclude;
import org.jclouds.azureblob.domain.PublicAccess;
@ -379,6 +380,23 @@ public class AzureBlobClientTest extends BaseRestAnnotationProcessingTest<AzureB
assertFallbackClassEquals(method, null);
}
public void testSetBlobTier() throws Exception {
AccessTier tier = AccessTier.COOL;
Invokable<?, ?> method = method(AzureBlobClient.class, "setBlobTier", String.class, String.class, AccessTier.class);
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("container", "blob", tier));
assertRequestLineEquals(request,
"PUT https://identity.blob.core.windows.net/container/blob?comp=tier HTTP/1.1");
assertNonPayloadHeadersEqual(request,
"x-ms-access-tier: " + tier + "\n" +
"x-ms-version: 2017-04-17\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
assertSaxResponseParserClassEquals(method, null);
assertFallbackClassEquals(method, null);
}
public void testCopyBlob() throws Exception {
Invokable<?, ?> method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class);
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(

View File

@ -22,6 +22,7 @@ import java.io.InputStream;
import java.net.URI;
import java.util.Set;
import org.jclouds.azureblob.domain.AccessTier;
import org.jclouds.azureblob.domain.BlobProperties;
import org.jclouds.azureblob.domain.BlobType;
import org.jclouds.azureblob.domain.LeaseStatus;
@ -56,17 +57,17 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest
public void testApplyInputStream() {
InputStream is = getClass().getResourceAsStream("/test_list_blobs.xml");
Set<BlobProperties> contents = ImmutableSet.<BlobProperties> of(
new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob1.txt", "mycontainer", URI
new BlobPropertiesImpl(BlobType.BLOCK_BLOB, AccessTier.HOT, "blob1.txt", "mycontainer", URI
.create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"), dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55D050B8B", 8,
"text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
.<String, String> of()),
new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob2.txt", "mycontainer", URI
new BlobPropertiesImpl(BlobType.BLOCK_BLOB, AccessTier.COOL, "blob2.txt", "mycontainer", URI
.create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 14,
"text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
.<String, String> of()),
new BlobPropertiesImpl(BlobType.PAGE_BLOB, "newblob1.txt", "mycontainer", URI
new BlobPropertiesImpl(BlobType.PAGE_BLOB, AccessTier.ARCHIVE, "newblob1.txt", "mycontainer", URI
.create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 25,
"text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
@ -84,7 +85,7 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest
public void testOptions() {
InputStream is = getClass().getResourceAsStream("/test_list_blobs_options.xml");
Set<BlobProperties> contents = ImmutableSet.<BlobProperties> of(new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "a",
Set<BlobProperties> contents = ImmutableSet.<BlobProperties> of(new BlobPropertiesImpl(BlobType.BLOCK_BLOB, AccessTier.HOT, "a",
"adriancole-blobstore3", URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3/a"),
dateService.rfc822DateParse("Sat, 30 Jan 2010 17:46:15 GMT"), "0x8CC6FEB41736428", 8,
"application/octet-stream", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap.<String, String> of()));

View File

@ -13,6 +13,7 @@
<Content-Length>8</Content-Length>
<Content-Type>text/plain; charset=UTF-8</Content-Type>
<BlobType>BlockBlob</BlobType>
<AccessTier>Hot</AccessTier>
<LeaseStatus>unlocked</LeaseStatus>
<Content-Encoding />
<Content-Language />
@ -26,6 +27,7 @@
<Content-Length>14</Content-Length>
<Content-Type>text/plain; charset=UTF-8</Content-Type>
<BlobType>BlockBlob</BlobType>
<AccessTier>Cool</AccessTier>
<LeaseStatus>unlocked</LeaseStatus>
<Content-Encoding />
<Content-Language />
@ -42,6 +44,7 @@
<Content-Length>25</Content-Length>
<Content-Type>text/plain; charset=UTF-8</Content-Type>
<BlobType>PageBlob</BlobType>
<AccessTier>Archive</AccessTier>
<LeaseStatus>unlocked</LeaseStatus>
<Content-Encoding />
<Content-Language />

View File

@ -18,6 +18,7 @@
<Content-MD5 />
<Cache-Control />
<BlobType>BlockBlob</BlobType>
<AccessTier>Hot</AccessTier>
<LeaseStatus>unlocked</LeaseStatus>
</Properties>
<Metadata />