/**
* Copyright Microsoft Corporation
*
* Licensed 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 com.microsoft.azure.storage.blob;
import com.microsoft.azure.storage.AccessCondition;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageErrorCodeStrings;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.TestRunners;
import com.microsoft.azure.storage.TestRunners.CloudTests;
import com.microsoft.azure.storage.TestRunners.DevFabricTests;
import com.microsoft.azure.storage.TestRunners.DevStoreTests;
import com.microsoft.azure.storage.TestRunners.SlowTests;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.util.UUID;
import static org.junit.Assert.*;
@Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class })
public class LeaseTests {
protected CloudBlobContainer container;
@Before
public void leaseTestMethodSetup() throws StorageException, URISyntaxException {
this.container = BlobTestHelper.getRandomContainerReference();
this.container.create();
}
@After
public void leaseTestMethodTearDown() throws StorageException {
this.container.deleteIfExists();
}
@Test
public void testContainerLeaseInvalidParams() throws StorageException, URISyntaxException {
try {
this.container.acquireLease(100, null);
} catch(StorageException ex) {
assertEquals("The value of the parameter 'leaseTimeInSeconds' should be between 15 and 60.",
ex.getMessage());
}
try {
this.container.breakLease(100);
} catch(StorageException ex) {
assertEquals("The value of the parameter 'breakPeriodInSeconds' should be between 0 and 60.",
ex.getMessage());
}
}
@Test
public void testContainerAcquireLease() throws StorageException, URISyntaxException {
CloudBlobContainer leaseContainer1 = BlobTestHelper.getRandomContainerReference();
leaseContainer1.create();
String proposedLeaseId1 = UUID.randomUUID().toString();
CloudBlobContainer leaseContainer2 = BlobTestHelper.getRandomContainerReference();
leaseContainer2.create();
String proposedLeaseId2 = UUID.randomUUID().toString();
try {
// 15 sec
OperationContext operationContext1 = new OperationContext();
leaseContainer1.acquireLease(15, proposedLeaseId1, null /*access condition*/,
null/* BlobRequestOptions */, operationContext1);
assertTrue(operationContext1.getLastResult().getStatusCode() == HttpURLConnection.HTTP_CREATED);
// infinite
String leaseId1;
String leaseId2;
OperationContext operationContext2 = new OperationContext();
leaseId1 = leaseContainer2.acquireLease(null /* infinite lease */, proposedLeaseId2,
null /*access condition*/, null/* BlobRequestOptions */, operationContext2);
assertTrue(operationContext2.getLastResult().getStatusCode() == HttpURLConnection.HTTP_CREATED);
leaseId2 = leaseContainer2.acquireLease(null /* infinite lease */, proposedLeaseId2);
assertEquals(leaseId1, leaseId2);
}
finally {
// cleanup
AccessCondition condition = new AccessCondition();
condition.setLeaseID(proposedLeaseId1);
leaseContainer1.releaseLease(condition);
leaseContainer1.deleteIfExists();
condition = new AccessCondition();
condition.setLeaseID(proposedLeaseId2);
leaseContainer2.releaseLease(condition);
leaseContainer2.deleteIfExists();
}
}
@Test
public void testContainerReleaseLease() throws StorageException {
// 15 sec
String proposedLeaseId = UUID.randomUUID().toString();
String leaseId = this.container.acquireLease(15, proposedLeaseId);
AccessCondition condition = new AccessCondition();
condition.setLeaseID(leaseId);
OperationContext operationContext1 = new OperationContext();
this.container.releaseLease(condition, null/* BlobRequestOptions */, operationContext1);
assertTrue(operationContext1.getLastResult().getStatusCode() == HttpURLConnection.HTTP_OK);
// infinite
leaseId = this.container.acquireLease();
condition = new AccessCondition();
condition.setLeaseID(leaseId);
OperationContext operationContext2 = new OperationContext();
this.container.releaseLease(condition, null/* BlobRequestOptions */, operationContext2);
assertTrue(operationContext2.getLastResult().getStatusCode() == HttpURLConnection.HTTP_OK);
}
@Test
@Category(SlowTests.class)
public void testContainerBreakLease() throws StorageException, InterruptedException {
String proposedLeaseId = UUID.randomUUID().toString();
try {
// 5 sec
this.container.acquireLease(15, proposedLeaseId);
AccessCondition condition = new AccessCondition();
condition.setLeaseID(proposedLeaseId);
OperationContext operationContext1 = new OperationContext();
this.container.breakLease(0, condition, null/* BlobRequestOptions */, operationContext1);
assertTrue(operationContext1.getLastResult().getStatusCode() == HttpURLConnection.HTTP_ACCEPTED);
Thread.sleep(15 * 1000);
// infinite
proposedLeaseId = this.container.acquireLease();
condition = new AccessCondition();
condition.setLeaseID(proposedLeaseId);
OperationContext operationContext2 = new OperationContext();
this.container.breakLease(0, condition, null/* BlobRequestOptions */, operationContext2);
assertTrue(operationContext2.getLastResult().getStatusCode() == HttpURLConnection.HTTP_ACCEPTED);
}
finally {
// cleanup
AccessCondition condition = new AccessCondition();
condition.setLeaseID(proposedLeaseId);
this.container.releaseLease(condition);
}
}
@Test
public void testContainerRenewLeaseTest() throws StorageException {
String proposedLeaseId = UUID.randomUUID().toString();
try {
// 5 sec
this.container.acquireLease(15, proposedLeaseId);
AccessCondition condition = new AccessCondition();
condition.setLeaseID(proposedLeaseId);
OperationContext operationContext1 = new OperationContext();
this.container.renewLease(condition, null/* BlobRequestOptions */, operationContext1);
assertTrue(operationContext1.getLastResult().getStatusCode() == HttpURLConnection.HTTP_OK);
this.container.releaseLease(condition);
// infinite
proposedLeaseId = this.container.acquireLease();
condition = new AccessCondition();
condition.setLeaseID(proposedLeaseId);
OperationContext operationContext2 = new OperationContext();
this.container.renewLease(condition, null/* BlobRequestOptions */, operationContext2);
assertTrue(operationContext2.getLastResult().getStatusCode() == HttpURLConnection.HTTP_OK);
}
finally {
// cleanup
AccessCondition condition = new AccessCondition();
condition.setLeaseID(proposedLeaseId);
this.container.releaseLease(condition);
}
}
@Test
public void testContainerChangeLeaseTest() throws StorageException {
// Get Lease
String leaseID1;
String leaseID2;
OperationContext operationContext = new OperationContext();
leaseID1 = this.container.acquireLease(null /* infinite lease */, null /*proposed lease id */,
null /*access condition*/, null/* BlobRequestOptions */, operationContext);
assertTrue(operationContext.getLastResult().getStatusCode() == HttpURLConnection.HTTP_CREATED);
//Change leased state with idempotent change
final String proposedLeaseId = UUID.randomUUID().toString();
leaseID2 = this.container.changeLease(proposedLeaseId, AccessCondition.generateLeaseCondition(leaseID1));
leaseID2 = this.container.changeLease(proposedLeaseId, AccessCondition.generateLeaseCondition(leaseID1));
//Change lease state with same proposed ID but different lease ID
leaseID2 = this.container.changeLease(proposedLeaseId, AccessCondition.generateLeaseCondition(leaseID2));
leaseID2 = this.container.changeLease(proposedLeaseId, AccessCondition.generateLeaseCondition(leaseID1));
//Change lease (wrong lease ID specified)
final String proposedLeaseId2 = UUID.randomUUID().toString();
leaseID2 = this.container.changeLease(proposedLeaseId2, AccessCondition.generateLeaseCondition(leaseID2));
try {
this.container.changeLease(proposedLeaseId, AccessCondition.generateLeaseCondition(leaseID1));
}
catch (StorageException ex) {
Assert.assertEquals(HttpURLConnection.HTTP_CONFLICT, ex.getHttpStatusCode());
}
// Change released lease
this.container.releaseLease(AccessCondition.generateLeaseCondition(leaseID2));
try {
this.container.changeLease(leaseID2, AccessCondition.generateLeaseCondition(leaseID2));
}
catch (StorageException ex) {
Assert.assertEquals(HttpURLConnection.HTTP_CONFLICT, ex.getHttpStatusCode());
}
// Change a breaking lease (same ID)
leaseID1 = this.container.acquireLease(null /* infinite lease */, null /*proposed lease id */,
null /*access condition*/, null/* BlobRequestOptions */, operationContext);
this.container.breakLease(60);
try {
this.container.changeLease(proposedLeaseId2, AccessCondition.generateLeaseCondition(leaseID1));
}
catch (StorageException ex) {
Assert.assertEquals(HttpURLConnection.HTTP_CONFLICT, ex.getHttpStatusCode());
}
// Change broken lease
this.container.breakLease(0);
try {
this.container.changeLease(proposedLeaseId, AccessCondition.generateLeaseCondition(leaseID1));
}
catch (StorageException ex) {
Assert.assertEquals(HttpURLConnection.HTTP_CONFLICT, ex.getHttpStatusCode());
}
// Change broken lease (to previous lease)
leaseID1 = this.container.acquireLease(null /* infinite lease */, null /*proposed lease id */,
null /*access condition*/, null/* BlobRequestOptions */, operationContext);
this.container.breakLease(0);
try {
this.container.changeLease(leaseID1, AccessCondition.generateLeaseCondition(leaseID1));
}
catch (StorageException ex) {
Assert.assertEquals(HttpURLConnection.HTTP_CONFLICT, ex.getHttpStatusCode());
}
}
@Test
public void testBlobLeaseAcquireAndRelease() throws StorageException, IOException, URISyntaxException {
final int length = 128;
final CloudBlob blobRef = BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "test", 128, null);
// Get Lease
OperationContext operationContext = new OperationContext();
final String leaseID = blobRef.acquireLease(15, null /*proposed lease id */, null /*access condition*/,
null/* BlobRequestOptions */, operationContext);
final AccessCondition leaseCondition = AccessCondition.generateLeaseCondition(leaseID);
assertTrue(operationContext.getLastResult().getStatusCode() == HttpURLConnection.HTTP_CREATED);
tryUploadWithBadLease(length, blobRef, null, StorageErrorCodeStrings.LEASE_ID_MISSING);
// Try to upload with lease
blobRef.upload(BlobTestHelper.getRandomDataStream(length), -1, leaseCondition, null, null);
// Release lease
blobRef.releaseLease(leaseCondition);
// now upload with no lease specified.
blobRef.upload(BlobTestHelper.getRandomDataStream(length), -1);
}
@Test
public void testBlobLeaseChange() throws StorageException, IOException, URISyntaxException {
final int length = 128;
final CloudBlob blobRef = BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "test", 128, null);
// Get Lease
OperationContext operationContext = new OperationContext();
final String leaseID1 = blobRef.acquireLease(15, null /*proposed lease id */, null /*access condition*/,
null/* BlobRequestOptions */, operationContext);
final AccessCondition leaseCondition1 = AccessCondition.generateLeaseCondition(leaseID1);
assertTrue(operationContext.getLastResult().getStatusCode() == HttpURLConnection.HTTP_CREATED);
final String leaseID2 = UUID.randomUUID().toString();
final AccessCondition leaseCondition2 = AccessCondition.generateLeaseCondition(leaseID2);
// Try to upload without lease
tryUploadWithBadLease(length, blobRef, null, StorageErrorCodeStrings.LEASE_ID_MISSING);
// Try to upload with incorrect lease
tryUploadWithBadLease(length, blobRef, leaseCondition2,
StorageErrorCodeStrings.LEASE_ID_MISMATCH_WITH_BLOB_OPERATION);
// Try to upload with correct lease
blobRef.upload(BlobTestHelper.getRandomDataStream(length), -1, leaseCondition1, null, null);
// Fail to change the lease with a bad accessCondition
try {
blobRef.changeLease(leaseID2, leaseCondition2);
fail("Did not throw expected exception.");
}
catch (final StorageException ex) {
assertEquals(ex.getHttpStatusCode(), 409);
assertEquals(ex.getErrorCode(), StorageErrorCodeStrings.LEASE_ID_MISMATCH_WITH_LEASE_OPERATION);
}
// Change the lease
blobRef.changeLease(leaseID2, leaseCondition1);
// Try to upload without lease
tryUploadWithBadLease(length, blobRef, null, StorageErrorCodeStrings.LEASE_ID_MISSING);
// Try to upload with incorrect lease
tryUploadWithBadLease(length, blobRef, leaseCondition1,
StorageErrorCodeStrings.LEASE_ID_MISMATCH_WITH_BLOB_OPERATION);
// Try to upload with correct lease
blobRef.upload(BlobTestHelper.getRandomDataStream(length), -1, leaseCondition2, null, null);
}
/**
* Try to upload with a bad lease
*
* @param length
* @param blobRef
* @throws IOException
*/
private void tryUploadWithBadLease(final int length, final CloudBlob blobRef, final AccessCondition leaseCondition,
final String expectedError) throws IOException {
try {
blobRef.upload(BlobTestHelper.getRandomDataStream(length), -1, leaseCondition, null, null);
fail("Did not throw expected exception");
}
catch (final StorageException ex) {
assertEquals(ex.getHttpStatusCode(), 412);
assertEquals(ex.getErrorCode(), expectedError);
}
}
@Test
public void testBlobLeaseBreak() throws StorageException, IOException, URISyntaxException {
final CloudBlob blobRef = BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "test", 128, null);
// Get Lease
String leaseID = blobRef.acquireLease();
OperationContext operationContext = new OperationContext();
final AccessCondition leaseCondition = AccessCondition.generateLeaseCondition(leaseID);
blobRef.breakLease(0, leaseCondition, null/* BlobRequestOptions */, operationContext);
assertTrue(operationContext.getLastResult().getStatusCode() == HttpURLConnection.HTTP_ACCEPTED);
}
@Test
@Category(SlowTests.class)
public void testBlobLeaseRenew() throws StorageException, IOException, InterruptedException, URISyntaxException {
final CloudBlob blobRef = BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "test", 128, null);
// Get Lease
final String leaseID = blobRef.acquireLease(15, null);
Thread.sleep(1000);
AccessCondition leaseCondition = AccessCondition.generateLeaseCondition(leaseID);
OperationContext operationContext = new OperationContext();
blobRef.renewLease(leaseCondition, null/* BlobRequestOptions */, operationContext);
assertTrue(operationContext.getLastResult().getStatusCode() == HttpURLConnection.HTTP_OK);
}
}