Issue 191: changed encryption service so that it can decode a PEM key. updated chef to work in google appengine

This commit is contained in:
Adrian Cole 2010-07-22 15:35:03 -07:00
parent 126460fd1d
commit 7c3518f7ff
24 changed files with 718 additions and 448 deletions

View File

@ -34,7 +34,7 @@ import org.jclouds.logging.Logger;
import com.google.inject.Inject;
/**
* Handles Retryable responses with error codes in the 3xx range
* Handles Retryable responses with error codes in the 4xx range
*
* @author Adrian Cole
*/

View File

@ -23,6 +23,7 @@ import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.testng.Assert.assertEquals;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Map;
@ -42,6 +43,7 @@ import org.jclouds.io.Payloads;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
/**
@ -49,7 +51,15 @@ import com.google.common.collect.ImmutableMap;
*/
@Test(testName = "s3.ParseObjectMetadataFromHeadersTest")
public class ParseObjectMetadataFromHeadersTest {
private static final EncryptionService encryptionService = new JCEEncryptionService();
protected volatile static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
}
}
@Test
void testNormal() throws Exception {
@ -59,8 +69,8 @@ public class ParseObjectMetadataFromHeadersTest {
http.getHeaders().put(HttpHeaders.CACHE_CONTROL, "cacheControl");
http.getHeaders().put("Content-Disposition", "contentDisposition");
http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding");
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http,
"\"abc\""), blobToObjectMetadata, encryptionService, "x-amz-meta-");
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http, "\"abc\""),
blobToObjectMetadata, encryptionService, "x-amz-meta-");
MutableObjectMetadata response = parser.apply(http);
assertEquals(response, expects);
}
@ -75,8 +85,8 @@ public class ParseObjectMetadataFromHeadersTest {
http.getHeaders().put("Content-Disposition", "contentDisposition");
http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding");
http.getHeaders().put("x-amz-meta-object-eTag", "\"abc\"");
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http,
null), blobToObjectMetadata, encryptionService, "x-amz-meta-");
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http, null),
blobToObjectMetadata, encryptionService, "x-amz-meta-");
MutableObjectMetadata response = parser.apply(http);
assertEquals(response, expects);
}

View File

@ -21,6 +21,7 @@ package org.jclouds.aws.s3.xml;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.TreeSet;
import org.jclouds.aws.s3.domain.CanonicalUser;
@ -39,6 +40,7 @@ import org.jclouds.util.Utils;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
@Test(groups = "unit", testName = "s3.ListBucketHandlerTest")
@ -46,7 +48,15 @@ public class ListBucketHandlerTest extends BaseHandlerTest {
public static final String listBucketWithPrefixAppsSlash = "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Name>adriancole.org.jclouds.aws.s3.amazons3testdelimiter</Name><Prefix>apps/</Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>apps/0</Key><LastModified>2009-05-07T18:27:08.000Z</LastModified><ETag>&quot;c82e6a0025c31c5de5947fda62ac51ab&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/1</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>&quot;944fab2c5a9a6bacf07db5e688310d7a&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/2</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>&quot;a227b8888045c8fd159fb495214000f0&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/3</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>&quot;c9caa76c3dec53e2a192608ce73eef03&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/4</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>&quot;1ce5d0dcc6154a647ea90c7bdf82a224&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/5</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>&quot;79433524d87462ee05708a8ef894ed55&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/6</Key><LastModified>2009-05-07T18:27:10.000Z</LastModified><ETag>&quot;dd00a060b28ddca8bc5a21a49e306f67&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/7</Key><LastModified>2009-05-07T18:27:10.000Z</LastModified><ETag>&quot;8cd06eca6e819a927b07a285d750b100&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/8</Key><LastModified>2009-05-07T18:27:10.000Z</LastModified><ETag>&quot;174495094d0633b92cbe46603eee6bad&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/9</Key><LastModified>2009-05-07T18:27:10.000Z</LastModified><ETag>&quot;cd8a19b26fea8a827276df0ad11c580d&quot;</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents></ListBucketResult>";
public static final String listBucketWithSlashDelimiterAndCommonPrefixApps = "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"> <Delimiter>/</Delimiter> <CommonPrefixes><Prefix>apps/</Prefix></CommonPrefixes></ListBucketResult>";
private DateService dateService;
private static final EncryptionService encryptionService = new JCEEncryptionService();
protected volatile static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
}
}
@BeforeTest
@Override
@ -58,64 +68,50 @@ public class ListBucketHandlerTest extends BaseHandlerTest {
public void testApplyInputStream() {
InputStream is = getClass().getResourceAsStream("/s3/list_bucket.xml");
CanonicalUser owner = new CanonicalUser(
"e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0", "ferncam");
ListBucketResponse expected = new ListBucketResponseImpl(
"adriancole.org.jclouds.aws.s3.amazons3testdelimiter", ImmutableList.of(
CanonicalUser owner = new CanonicalUser("e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0",
"ferncam");
ListBucketResponse expected = new ListBucketResponseImpl("adriancole.org.jclouds.aws.s3.amazons3testdelimiter",
ImmutableList
.of(
(ObjectMetadata) new BucketListObjectMetadata("apps/0", dateService
.iso8601DateParse("2009-05-07T18:27:08.000Z"),
"\"c82e6a0025c31c5de5947fda62ac51ab\"", encryptionService
.fromHex("c82e6a0025c31c5de5947fda62ac51ab"), 8, owner,
StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/1", dateService
.iso8601DateParse("2009-05-07T18:27:09.000Z"),
.iso8601DateParse("2009-05-07T18:27:08.000Z"), "\"c82e6a0025c31c5de5947fda62ac51ab\"",
encryptionService.fromHex("c82e6a0025c31c5de5947fda62ac51ab"), 8, owner,
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/1",
dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"),
"\"944fab2c5a9a6bacf07db5e688310d7a\"", encryptionService
.fromHex("944fab2c5a9a6bacf07db5e688310d7a"), 8, owner,
StorageClass.STANDARD),
.fromHex("944fab2c5a9a6bacf07db5e688310d7a"), 8, owner, StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/2", dateService
.iso8601DateParse("2009-05-07T18:27:09.000Z"),
"\"a227b8888045c8fd159fb495214000f0\"", encryptionService
.fromHex("a227b8888045c8fd159fb495214000f0"), 8, owner,
StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/3", dateService
.iso8601DateParse("2009-05-07T18:27:09.000Z"),
.iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"a227b8888045c8fd159fb495214000f0\"",
encryptionService.fromHex("a227b8888045c8fd159fb495214000f0"), 8, owner,
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/3",
dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"),
"\"c9caa76c3dec53e2a192608ce73eef03\"", encryptionService
.fromHex("c9caa76c3dec53e2a192608ce73eef03"), 8, owner,
StorageClass.STANDARD),
.fromHex("c9caa76c3dec53e2a192608ce73eef03"), 8, owner, StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/4", dateService
.iso8601DateParse("2009-05-07T18:27:09.000Z"),
"\"1ce5d0dcc6154a647ea90c7bdf82a224\"", encryptionService
.fromHex("1ce5d0dcc6154a647ea90c7bdf82a224"), 8, owner,
StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/5", dateService
.iso8601DateParse("2009-05-07T18:27:09.000Z"),
.iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"1ce5d0dcc6154a647ea90c7bdf82a224\"",
encryptionService.fromHex("1ce5d0dcc6154a647ea90c7bdf82a224"), 8, owner,
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/5",
dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"),
"\"79433524d87462ee05708a8ef894ed55\"", encryptionService
.fromHex("79433524d87462ee05708a8ef894ed55"), 8, owner,
StorageClass.STANDARD),
.fromHex("79433524d87462ee05708a8ef894ed55"), 8, owner, StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/6", dateService
.iso8601DateParse("2009-05-07T18:27:10.000Z"),
"\"dd00a060b28ddca8bc5a21a49e306f67\"", encryptionService
.fromHex("dd00a060b28ddca8bc5a21a49e306f67"), 8, owner,
StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/7", dateService
.iso8601DateParse("2009-05-07T18:27:10.000Z"),
.iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"dd00a060b28ddca8bc5a21a49e306f67\"",
encryptionService.fromHex("dd00a060b28ddca8bc5a21a49e306f67"), 8, owner,
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/7",
dateService.iso8601DateParse("2009-05-07T18:27:10.000Z"),
"\"8cd06eca6e819a927b07a285d750b100\"", encryptionService
.fromHex("8cd06eca6e819a927b07a285d750b100"), 8, owner,
StorageClass.STANDARD),
.fromHex("8cd06eca6e819a927b07a285d750b100"), 8, owner, StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/8", dateService
.iso8601DateParse("2009-05-07T18:27:10.000Z"),
"\"174495094d0633b92cbe46603eee6bad\"", encryptionService
.fromHex("174495094d0633b92cbe46603eee6bad"), 8, owner,
StorageClass.STANDARD),
(ObjectMetadata) new BucketListObjectMetadata("apps/9", dateService
.iso8601DateParse("2009-05-07T18:27:10.000Z"),
.iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"174495094d0633b92cbe46603eee6bad\"",
encryptionService.fromHex("174495094d0633b92cbe46603eee6bad"), 8, owner,
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata("apps/9",
dateService.iso8601DateParse("2009-05-07T18:27:10.000Z"),
"\"cd8a19b26fea8a827276df0ad11c580d\"", encryptionService
.fromHex("cd8a19b26fea8a827276df0ad11c580d"), 8, owner,
StorageClass.STANDARD)), "apps/", null, null, 1000, null, false,
new TreeSet<String>());
.fromHex("cd8a19b26fea8a827276df0ad11c580d"), 8, owner, StorageClass.STANDARD)),
"apps/", null, null, 1000, null, false, new TreeSet<String>());
ListBucketResponse result = (ListBucketResponse) factory.create(
injector.getInstance(ListBucketHandler.class)).parse(is);
ListBucketResponse result = (ListBucketResponse) factory.create(injector.getInstance(ListBucketHandler.class))
.parse(is);
assertEquals(result, expected);
}
@ -139,8 +135,7 @@ public class ListBucketHandlerTest extends BaseHandlerTest {
@Test
public void testListMyBucketsWithPrefixAppsSlash() throws HttpException {
ListBucketResponse bucket = createParser().parse(
Utils.toInputStream(listBucketWithPrefixAppsSlash));
ListBucketResponse bucket = createParser().parse(Utils.toInputStream(listBucketWithPrefixAppsSlash));
assertEquals(bucket.getPrefix(), "apps/");
assertEquals(bucket.getMaxKeys(), 1000);
assert bucket.getMarker() == null;

View File

@ -24,6 +24,7 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Set;
import java.util.SortedSet;
@ -40,6 +41,7 @@ import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.inject.Module;
@ -57,7 +59,15 @@ public class SQSClientLiveTest {
private RestContext<SQSClient, SQSAsyncClient> context;
private EncryptionService encryptionService = new JCEEncryptionService();
protected volatile static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
}
}
private Set<Queue> queues = Sets.newHashSet();
@BeforeGroups(groups = { "live" })
@ -147,8 +157,9 @@ public class SQSClientLiveTest {
private static final int INCONSISTENCY_WINDOW = 10000;
/**
* Due to eventual consistency, container commands may not return correctly immediately. Hence,
* we will try up to the inconsistency window to see if the assertion completes.
* Due to eventual consistency, container commands may not return correctly
* immediately. Hence, we will try up to the inconsistency window to see if
* the assertion completes.
*/
protected static void assertEventually(Runnable assertion) throws InterruptedException {
long start = System.currentTimeMillis();

View File

@ -35,6 +35,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Map;
import java.util.zip.GZIPInputStream;
@ -47,6 +48,7 @@ import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.encryption.internal.JCEEncryptionService;
import org.jclouds.http.BaseJettyTest;
import org.jclouds.http.HttpResponseException;
@ -61,6 +63,7 @@ import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
@ -79,8 +82,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
@Override
public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception {
super.setUpResourcesOnThisThread(testContext);
Payload result = context.utils().encryption().generatePayloadWithMD5For(
getTestDataSupplier().getInput());
Payload result = context.utils().encryption().generatePayloadWithMD5For(getTestDataSupplier().getInput());
oneHundredOneConstitutions = (byte[]) result.getRawContent();
oneHundredOneConstitutionsMD5 = result.getContentMD5();
}
@ -89,8 +91,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
public static InputSupplier<InputStream> getTestDataSupplier() throws IOException {
byte[] oneConstitution = ByteStreams.toByteArray(new GZIPInputStream(BaseJettyTest.class
.getResourceAsStream("/const.txt.gz")));
InputSupplier<ByteArrayInputStream> constitutionSupplier = ByteStreams
.newInputStreamSupplier(oneConstitution);
InputSupplier<ByteArrayInputStream> constitutionSupplier = ByteStreams.newInputStreamSupplier(oneConstitution);
InputSupplier<InputStream> temp = ByteStreams.join(constitutionSupplier);
@ -110,8 +111,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
Map<Integer, ListenableFuture<?>> responses = Maps.newHashMap();
for (int i = 0; i < 10; i++) {
responses.put(i, Futures.compose(context.getAsyncBlobStore()
.getBlob(containerName, key), new Function<Blob, Void>() {
responses.put(i, Futures.compose(context.getAsyncBlobStore().getBlob(containerName, key),
new Function<Blob, Void>() {
@Override
public Void apply(Blob from) {
@ -122,8 +123,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
}));
}
Map<Integer, Exception> exceptions = awaitCompletion(responses, exec, 30000l,
Logger.CONSOLE, "get constitution");
Map<Integer, Exception> exceptions = awaitCompletion(responses, exec, 30000l, Logger.CONSOLE,
"get constitution");
assert exceptions.size() == 0 : exceptions;
} finally {
@ -250,10 +251,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
Blob blob1 = context.getBlobStore().getBlob(containerName, key, range(0, 5));
assertEquals(getContentAsStringOrNullAndClose(blob1), TEST_STRING.substring(0, 6));
Blob blob2 = context.getBlobStore().getBlob(containerName, key,
range(6, TEST_STRING.length()));
assertEquals(getContentAsStringOrNullAndClose(blob2), TEST_STRING.substring(6, TEST_STRING
.length()));
Blob blob2 = context.getBlobStore().getBlob(containerName, key, range(6, TEST_STRING.length()));
assertEquals(getContentAsStringOrNullAndClose(blob2), TEST_STRING.substring(6, TEST_STRING.length()));
} finally {
returnContainer(containerName);
}
@ -267,8 +266,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
String key = "apples";
addObjectAndValidateContent(containerName, key);
Blob blob = context.getBlobStore().getBlob(containerName, key,
range(0, 5).range(6, TEST_STRING.length()));
Blob blob = context.getBlobStore().getBlob(containerName, key, range(0, 5).range(6, TEST_STRING.length()));
assertEquals(getContentAsStringOrNullAndClose(blob), TEST_STRING);
} finally {
@ -277,7 +275,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
}
// @Test(groups = { "integration", "live" })
// public void testGetTail() throws InterruptedException, ExecutionException, TimeoutException,
// public void testGetTail() throws InterruptedException, ExecutionException,
// TimeoutException,
// IOException {
// String containerName = getContainerName();
// try {
@ -285,7 +284,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
// String key = "apples";
//
// addObjectAndValidateContent(containerName, key);
// Blob blob = context.getBlobStore().getBlob(containerName, key, tail(5)).get(30,
// Blob blob = context.getBlobStore().getBlob(containerName, key,
// tail(5)).get(30,
// TimeUnit.SECONDS);
// assertEquals(BlobStoreUtils.getContentAsStringAndClose(blob), TEST_STRING
// .substring(TEST_STRING.length() - 5));
@ -297,7 +297,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
// }
// @Test(groups = { "integration", "live" })
// public void testGetStartAt() throws InterruptedException, ExecutionException,
// public void testGetStartAt() throws InterruptedException,
// ExecutionException,
// TimeoutException,
// IOException {
// String containerName = getContainerName();
@ -305,9 +306,11 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
// String key = "apples";
//
// addObjectAndValidateContent(containerName, key);
// Blob blob = context.getBlobStore().getBlob(containerName, key, startAt(5)).get(30,
// Blob blob = context.getBlobStore().getBlob(containerName, key,
// startAt(5)).get(30,
// TimeUnit.SECONDS);
// assertEquals(BlobStoreUtils.getContentAsStringAndClose(blob), TEST_STRING.substring(5,
// assertEquals(BlobStoreUtils.getContentAsStringAndClose(blob),
// TEST_STRING.substring(5,
// TEST_STRING.length()));
// assertEquals(blob.getContentLength(), TEST_STRING.length() - 5);
// assertEquals(blob.getMetadata().getSize(), TEST_STRING.length());
@ -316,8 +319,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
// }
// }
private String addObjectAndValidateContent(String sourcecontainerName, String sourceKey)
throws InterruptedException {
private String addObjectAndValidateContent(String sourcecontainerName, String sourceKey) throws InterruptedException {
String eTag = addBlobToContainer(sourcecontainerName, sourceKey);
validateContent(sourcecontainerName, sourceKey);
return eTag;
@ -347,9 +349,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
@DataProvider(name = "delete")
public Object[][] createData() {
return new Object[][] { { "normal" }, { "sp ace" }, { "qu?stion" }, { "unic₪de" },
{ "path/foo" }, { "colon:" }, { "asteri*k" }, { "quote\"" }, { "{great<r}" },
{ "lesst>en" }, { "p|pe" } };
return new Object[][] { { "normal" }, { "sp ace" }, { "qu?stion" }, { "unic₪de" }, { "path/foo" },
{ "colon:" }, { "asteri*k" }, { "quote\"" }, { "{great<r}" }, { "lesst>en" }, { "p|pe" } };
}
@Test(groups = { "integration", "live" }, dataProvider = "delete")
@ -365,8 +366,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
}
private void assertContainerEmptyDeleting(String containerName, String key) {
Iterable<? extends StorageMetadata> listing = Iterables.filter(context.getBlobStore().list(
containerName), new Predicate<StorageMetadata>() {
Iterable<? extends StorageMetadata> listing = Iterables.filter(context.getBlobStore().list(containerName),
new Predicate<StorageMetadata>() {
@Override
public boolean apply(StorageMetadata input) {
@ -401,8 +402,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
}
@Test(groups = { "integration", "live" }, dataProvider = "putTests")
public void testPutObject(String key, String type, Object content, Object realObject)
throws InterruptedException, IOException {
public void testPutObject(String key, String type, Object content, Object realObject) throws InterruptedException,
IOException {
Blob blob = context.getBlobStore().newBlob(key);
blob.getMetadata().setContentType(type);
blob.setPayload(Payloads.newPayload(content));
@ -422,6 +423,15 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
}
}
protected volatile static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
}
}
@Test(groups = { "integration", "live" })
public void testMetadata() throws InterruptedException {
String key = "hello";
@ -430,11 +440,11 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
blob.setPayload(TEST_STRING);
blob.getMetadata().setContentType(MediaType.TEXT_PLAIN);
blob.getMetadata().setSize(new Long(TEST_STRING.length()));
// NOTE all metadata in jclouds comes out as lowercase, in an effort to normalize the
// NOTE all metadata in jclouds comes out as lowercase, in an effort to
// normalize the
// providers.
blob.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
blob.getMetadata().setContentMD5(
new JCEEncryptionService().md5(Utils.toInputStream(TEST_STRING)));
blob.getMetadata().setContentMD5(encryptionService.md5(Utils.toInputStream(TEST_STRING)));
String containerName = getContainerName();
try {
assertNull(context.getBlobStore().blobMetadata(containerName, "powderpuff"));
@ -447,7 +457,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
validateMetadata(metadata);
validateMetadata(context.getBlobStore().blobMetadata(containerName, key));
// write 2 items with the same key to ensure that provider doesn't accept dupes
// write 2 items with the same key to ensure that provider doesn't
// accept dupes
blob.getMetadata().getUserMetadata().put("Adrian", "wonderpuff");
blob.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
@ -463,8 +474,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType();
assertEquals(metadata.getSize(), new Long(TEST_STRING.length()));
assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
assertEquals(metadata.getContentMD5(), new JCEEncryptionService().md5(Utils
.toInputStream(TEST_STRING)));
assertEquals(metadata.getContentMD5(), encryptionService.md5(Utils.toInputStream(TEST_STRING)));
}
}

View File

@ -23,6 +23,7 @@ import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNull
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
@ -39,6 +40,8 @@ import org.jclouds.blobstore.attr.ConsistencyModel;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.encryption.internal.JCEEncryptionService;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.testng.ITestContext;
import org.testng.annotations.AfterClass;
@ -58,16 +61,14 @@ public class BaseBlobStoreIntegrationTest {
protected static final String XML_STRING_FORMAT = "<apples><apple name=\"%s\"></apple> </apples>";
protected static final String TEST_STRING = String.format(XML_STRING_FORMAT, "apple");
protected Map<String, String> fiveStrings = ImmutableMap.of("one", String.format(
XML_STRING_FORMAT, "apple"), "two", String.format(XML_STRING_FORMAT, "bear"), "three",
String.format(XML_STRING_FORMAT, "candy"), "four", String.format(XML_STRING_FORMAT,
"dogma"), "five", String.format(XML_STRING_FORMAT, "emma"));
protected Map<String, String> fiveStrings = ImmutableMap.of("one", String.format(XML_STRING_FORMAT, "apple"), "two",
String.format(XML_STRING_FORMAT, "bear"), "three", String.format(XML_STRING_FORMAT, "candy"), "four", String
.format(XML_STRING_FORMAT, "dogma"), "five", String.format(XML_STRING_FORMAT, "emma"));
protected Map<String, String> fiveStringsUnderPath = ImmutableMap.of("path/1", String.format(
XML_STRING_FORMAT, "apple"), "path/2", String.format(XML_STRING_FORMAT, "bear"),
"path/3", String.format(XML_STRING_FORMAT, "candy"), "path/4", String.format(
XML_STRING_FORMAT, "dogma"), "path/5", String
.format(XML_STRING_FORMAT, "emma"));
protected Map<String, String> fiveStringsUnderPath = ImmutableMap.of("path/1", String.format(XML_STRING_FORMAT,
"apple"), "path/2", String.format(XML_STRING_FORMAT, "bear"), "path/3", String.format(XML_STRING_FORMAT,
"candy"), "path/4", String.format(XML_STRING_FORMAT, "dogma"), "path/5", String.format(XML_STRING_FORMAT,
"emma"));
public static long INCONSISTENCY_WINDOW = 10000;
protected static volatile AtomicInteger containerIndex = new AtomicInteger(0);
@ -78,11 +79,20 @@ public class BaseBlobStoreIntegrationTest {
/**
* two test groups integration and live.
*/
private volatile static BlockingQueue<String> containerNames = new ArrayBlockingQueue<String>(
containerCount);
private volatile static BlockingQueue<String> containerNames = new ArrayBlockingQueue<String>(containerCount);
protected volatile static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
}
}
/**
* There are a lot of retries here mainly from experience running inside amazon EC2.
* There are a lot of retries here mainly from experience running inside
* amazon EC2.
*/
@BeforeSuite
public void setUpResourcesForAllThreads(ITestContext testContext) throws Exception {
@ -91,13 +101,10 @@ public class BaseBlobStoreIntegrationTest {
}
@SuppressWarnings("unchecked")
private BlobStoreContext getCloudResources(ITestContext testContext)
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
Exception {
String initializerClass = checkNotNull(System.getProperty("jclouds.test.initializer"),
"jclouds.test.initializer");
Class<BaseTestInitializer> clazz = (Class<BaseTestInitializer>) Class
.forName(initializerClass);
private BlobStoreContext getCloudResources(ITestContext testContext) throws ClassNotFoundException,
InstantiationException, IllegalAccessException, Exception {
String initializerClass = checkNotNull(System.getProperty("jclouds.test.initializer"), "jclouds.test.initializer");
Class<BaseTestInitializer> clazz = (Class<BaseTestInitializer>) Class.forName(initializerClass);
BaseTestInitializer initializer = clazz.newInstance();
return initializer.init(createHttpModule(), testContext);
}
@ -105,10 +112,11 @@ public class BaseBlobStoreIntegrationTest {
protected ExecutorService exec;
/**
* we are doing this at a class level, as the context.getBlobStore() object is going to be shared
* for all methods in the class. We don't want to do this for group, as some test classes may
* want to have a different implementation of context.getBlobStore(). For example, one class may
* want non-blocking i/o and another class google appengine.
* we are doing this at a class level, as the context.getBlobStore() object
* is going to be shared for all methods in the class. We don't want to do
* this for group, as some test classes may want to have a different
* implementation of context.getBlobStore(). For example, one class may want
* non-blocking i/o and another class google appengine.
*/
@BeforeClass(groups = { "integration", "live" })
public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception {
@ -127,8 +135,8 @@ public class BaseBlobStoreIntegrationTest {
private static volatile boolean initialized = false;
protected void createContainersSharedByAllThreads(BlobStoreContext context,
ITestContext testContext) throws Exception {
protected void createContainersSharedByAllThreads(BlobStoreContext context, ITestContext testContext)
throws Exception {
while (!initialized) {
synchronized (BaseBlobStoreIntegrationTest.class) {
if (!initialized) {
@ -143,7 +151,8 @@ public class BaseBlobStoreIntegrationTest {
containerNames.put(containerName);
} catch (Throwable e) {
e.printStackTrace();
// throw away the container and try again with the next index
// throw away the container and try again with the next
// index
deleteContainerOrWarnIfUnable(context, containerName);
containerCount++;
}
@ -177,8 +186,8 @@ public class BaseBlobStoreIntegrationTest {
protected static void deleteEverything(final BlobStoreContext context) throws Exception {
try {
for (int i = 0; i < 2; i++) {
Iterable<? extends StorageMetadata> testContainers = Iterables.filter(context
.getBlobStore().list(), new Predicate<StorageMetadata>() {
Iterable<? extends StorageMetadata> testContainers = Iterables.filter(context.getBlobStore().list(),
new Predicate<StorageMetadata>() {
public boolean apply(StorageMetadata input) {
return (input.getType() == StorageType.CONTAINER || input.getType() == StorageType.FOLDER)
&& input.getName().startsWith(CONTAINER_PREFIX.toLowerCase());
@ -200,8 +209,9 @@ public class BaseBlobStoreIntegrationTest {
public static boolean SANITY_CHECK_RETURNED_BUCKET_NAME = false;
/**
* Due to eventual consistency, container commands may not return correctly immediately. Hence,
* we will try up to the inconsistency window to see if the assertion completes.
* Due to eventual consistency, container commands may not return correctly
* immediately. Hence, we will try up to the inconsistency window to see if
* the assertion completes.
*/
protected static void assertConsistencyAware(BlobStoreContext context, Runnable assertion)
throws InterruptedException {
@ -229,8 +239,8 @@ public class BaseBlobStoreIntegrationTest {
assertConsistencyAware(context, assertion);
}
protected static void createContainerAndEnsureEmpty(BlobStoreContext context,
final String containerName) throws InterruptedException {
protected static void createContainerAndEnsureEmpty(BlobStoreContext context, final String containerName)
throws InterruptedException {
context.getBlobStore().createContainerInLocation(null, containerName);
if (context.getConsistencyModel() == ConsistencyModel.EVENTUAL)
Thread.sleep(1000);
@ -249,8 +259,7 @@ public class BaseBlobStoreIntegrationTest {
}
protected void add5BlobsUnderPathAnd5UnderRootToContainer(String sourceContainer) {
for (Entry<String, String> entry : Iterables.concat(fiveStrings.entrySet(),
fiveStringsUnderPath.entrySet())) {
for (Entry<String, String> entry : Iterables.concat(fiveStrings.entrySet(), fiveStringsUnderPath.entrySet())) {
Blob sourceObject = context.getBlobStore().newBlob(entry.getKey());
sourceObject.getMetadata().setContentType("text/xml");
sourceObject.setPayload(entry.getValue());
@ -280,9 +289,8 @@ public class BaseBlobStoreIntegrationTest {
public void run() {
try {
assert context.getBlobStore().countBlobs(containerName) == count : String.format(
"expected only %d values in %s: %s", count, containerName, Sets
.newHashSet(Iterables.transform(context.getBlobStore().list(
containerName), new Function<StorageMetadata, String>() {
"expected only %d values in %s: %s", count, containerName, Sets.newHashSet(Iterables.transform(
context.getBlobStore().list(containerName), new Function<StorageMetadata, String>() {
public String apply(StorageMetadata from) {
return from.getName();
@ -304,9 +312,9 @@ public class BaseBlobStoreIntegrationTest {
}
/**
* requestor will create a container using the name returned from this. This method will take
* care not to exceed the maximum containers permitted by a provider by deleting an existing
* container first.
* requestor will create a container using the name returned from this. This
* method will take care not to exceed the maximum containers permitted by a
* provider by deleting an existing container first.
*
* @throws InterruptedException
*/
@ -318,12 +326,13 @@ public class BaseBlobStoreIntegrationTest {
if (containerName != null) {
containerNames.add(containerName);
/*
* Ensure that any returned container name actually exists on the server. Return of a
* non-existent container introduces subtle testing bugs, where later unrelated tests will
* fail.
* Ensure that any returned container name actually exists on the
* server. Return of a non-existent container introduces subtle testing
* bugs, where later unrelated tests will fail.
*
* NOTE: This sanity check should only be run for Stub-based Integration testing -- it will
* *substantially* slow down tests on a real server over a network.
* NOTE: This sanity check should only be run for Stub-based
* Integration testing -- it will *substantially* slow down tests on a
* real server over a network.
*/
if (SANITY_CHECK_RETURNED_BUCKET_NAME) {
if (!Iterables.any(context.getBlobStore().list(), new Predicate<StorageMetadata>() {
@ -331,15 +340,15 @@ public class BaseBlobStoreIntegrationTest {
return containerName.equals(md.getName());
}
})) {
throw new IllegalStateException(
"Test returned the name of a non-existent container: " + containerName);
throw new IllegalStateException("Test returned the name of a non-existent container: " + containerName);
}
}
}
}
/**
* abandon old container name instead of waiting for the container to be created.
* abandon old container name instead of waiting for the container to be
* created.
*
* @throws InterruptedException
*/

View File

@ -37,7 +37,6 @@ import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.encryption.internal.JCEEncryptionService;
import org.testng.annotations.Test;
/**
@ -48,8 +47,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
@Test(groups = { "integration", "live" })
public void containerDoesntExist() {
assert !context.getBlobStore().containerExists("forgetaboutit");
assert !context.getBlobStore().containerExists(
"cloudcachestorefunctionalintegrationtest-first");
assert !context.getBlobStore().containerExists("cloudcachestorefunctionalintegrationtest-first");
}
@Test(groups = { "integration", "live" })
@ -77,11 +75,11 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
object.setPayload(TEST_STRING);
object.getMetadata().setContentType(MediaType.TEXT_PLAIN);
object.getMetadata().setSize(new Long(TEST_STRING.length()));
// NOTE all metadata in jclouds comes out as lowercase, in an effort to normalize the
// NOTE all metadata in jclouds comes out as lowercase, in an effort to
// normalize the
// providers.
object.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
object.getMetadata()
.setContentMD5(new JCEEncryptionService().md5(toInputStream(TEST_STRING)));
object.getMetadata().setContentMD5(encryptionService.md5(toInputStream(TEST_STRING)));
String containerName = getContainerName();
try {
addBlobToContainer(containerName, object);
@ -95,8 +93,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType();
assertEquals(metadata.getSize(), new Long(TEST_STRING.length()));
assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
assertEquals(metadata.getContentMD5(), new JCEEncryptionService()
.md5(toInputStream(TEST_STRING)));
assertEquals(metadata.getContentMD5(), encryptionService.md5(toInputStream(TEST_STRING)));
} finally {
returnContainer(containerName);
}
@ -119,8 +116,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
String containerName = getContainerName();
try {
addAlphabetUnderRoot(containerName);
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName,
maxResults(1));
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName, maxResults(1));
assert container.getNextMarker() != null;
assertEquals(container.size(), 1);
@ -128,8 +124,8 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
container = context.getBlobStore().list(containerName, afterMarker(marker));
assertEquals(container.getNextMarker(), null);
assert container.size() == 25 : String.format("size should have been 25, but was %d: %s",
container.size(), container);
assert container.size() == 25 : String.format("size should have been 25, but was %d: %s", container.size(),
container);
assert container.getNextMarker() == null;
} finally {
@ -138,8 +134,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
}
@Test(groups = { "integration", "live" })
public void testListRootUsesDelimiter() throws InterruptedException,
UnsupportedEncodingException {
public void testListRootUsesDelimiter() throws InterruptedException, UnsupportedEncodingException {
String containerName = getContainerName();
try {
String prefix = "rootdelimeter";
@ -239,8 +234,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
addTenObjectsUnderPrefix(containerName, prefix);
add15UnderRoot(containerName);
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName,
inDirectory(prefix));
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName, inDirectory(prefix));
assert container.getNextMarker() == null;
assertEquals(container.size(), 10);
} finally {
@ -250,13 +244,11 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
}
@Test(groups = { "integration", "live" })
public void testListContainerMaxResults() throws InterruptedException,
UnsupportedEncodingException {
public void testListContainerMaxResults() throws InterruptedException, UnsupportedEncodingException {
String containerName = getContainerName();
try {
addAlphabetUnderRoot(containerName);
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName,
maxResults(5));
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName, maxResults(5));
assertEquals(container.size(), 5);
assert container.getNextMarker() != null;
} finally {
@ -302,8 +294,8 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
assertConsistencyAware(new Runnable() {
public void run() {
try {
assert !context.getBlobStore().containerExists(containerName) : "container "
+ containerName + " still exists";
assert !context.getBlobStore().containerExists(containerName) : "container " + containerName
+ " still exists";
} catch (Exception e) {
propagateIfPossible(e);
}
@ -312,8 +304,8 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
}
@Test(groups = { "integration", "live" })
public void testListContainer() throws InterruptedException, ExecutionException,
TimeoutException, UnsupportedEncodingException {
public void testListContainer() throws InterruptedException, ExecutionException, TimeoutException,
UnsupportedEncodingException {
String containerName = getContainerName();
try {
add15UnderRoot(containerName);
@ -341,8 +333,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
}
}
protected void addTenObjectsUnderPrefix(String containerName, String prefix)
throws InterruptedException {
protected void addTenObjectsUnderPrefix(String containerName, String prefix) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Blob blob = context.getBlobStore().newBlob(prefix + "/" + i);
blob.setPayload(i + "content");

View File

@ -1,31 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Cloud Conscious, LLC <info@cloudconscious.com>
$HeadURL$ $Revision$ $Date$ Copyright (C) 2009 Cloud Conscious, LLC
<info@cloudconscious.com>
====================================================================
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
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.html
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.
http://www.apache.org/licenses/LICENSE-2.0.html 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.
====================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jclouds</groupId>
@ -69,11 +64,6 @@
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15</artifactId>
<version>1.44</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-core</artifactId>

View File

@ -44,24 +44,22 @@ package org.jclouds.chef.config;
import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.io.IOException;
import java.io.StringReader;
import java.security.KeyPair;
import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;
import java.security.Security;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.jclouds.chef.handlers.ChefClientErrorRetryHandler;
import org.jclouds.chef.handlers.ChefErrorHandler;
import org.jclouds.concurrent.ExpirableSupplier;
import org.jclouds.date.DateService;
import org.jclouds.date.TimeStamp;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.RequiresHttp;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
@ -103,8 +101,7 @@ public class BaseChefRestClientModule<S, A> extends RestClientModule<S, A> {
*/
@Provides
@TimeStamp
Supplier<String> provideTimeStampCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
final DateService dateService) {
Supplier<String> provideTimeStampCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final DateService dateService) {
return new ExpirableSupplier<String>(new Supplier<String>() {
public String get() {
return dateService.iso8601SecondsDateFormat();
@ -114,11 +111,9 @@ public class BaseChefRestClientModule<S, A> extends RestClientModule<S, A> {
@Provides
@Singleton
public PrivateKey provideKey(@Named(PROPERTY_CREDENTIAL) String key) throws IOException {
// TODO do this without adding a provider
Security.addProvider(new BouncyCastleProvider());
KeyPair pair = KeyPair.class.cast(new PEMReader(new StringReader(key)).readObject());
return pair.getPrivate();
public PrivateKey provideKey(EncryptionService encryptionService, @Named(PROPERTY_CREDENTIAL) String pem)
throws UnsupportedEncodingException {
return encryptionService.readPrivateKeyFromPEM(pem.getBytes("UTF-8"));
}
@Override
@ -128,6 +123,11 @@ public class BaseChefRestClientModule<S, A> extends RestClientModule<S, A> {
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ChefErrorHandler.class);
}
@Override
protected void bindRetryHandlers() {
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(ChefClientErrorRetryHandler.class);
}
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);

View File

@ -0,0 +1,72 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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 org.jclouds.chef.handlers;
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
import javax.annotation.Resource;
import javax.inject.Named;
import org.jclouds.Constants;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.logging.Logger;
import com.google.inject.Inject;
/**
* Allow for eventual consistency on sandbox requests.
*
* @author Adrian Cole
*/
public class ChefClientErrorRetryHandler implements HttpRetryHandler {
@Inject(optional = true)
@Named(Constants.PROPERTY_MAX_RETRIES)
private int retryCountLimit = 5;
@Resource
protected Logger logger = Logger.NULL;
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
@Inject
ChefClientErrorRetryHandler(BackoffLimitedRetryHandler backoffLimitedRetryHandler) {
this.backoffLimitedRetryHandler = backoffLimitedRetryHandler;
}
public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
if (command.getFailureCount() > retryCountLimit)
return false;
if (response.getStatusCode() == 400 && command.getRequest().getMethod().equals("PUT")
&& command.getRequest().getEndpoint().getPath().indexOf("sandboxes") != -1) {
if (response.getPayload() != null) {
String error = new String(closeClientButKeepContentStream(response));
if (error != null && error.indexOf("was not uploaded") != -1) {
return backoffLimitedRetryHandler.shouldRetryRequest(command, response);
}
}
}
return false;
}
}

View File

@ -106,6 +106,7 @@ public class ChefClientLiveTest {
// define the file you want in the cookbook
FilePayload content = Payloads.newFilePayload(new File(System.getProperty("user.dir"), "pom.xml"));
content.setContentType("application/x-binary");
// get an md5 so that you can see if the server already has it or not
adminConnection.utils().encryption().generateMD5BufferingIfNotRepeatable(content);
@ -126,7 +127,6 @@ public class ChefClientLiveTest {
adminConnection.utils().http().put(status.getUrl(), content);
}
// if we were able to get here, close the sandbox
adminConnection.getApi().commitSandbox(site.getSandboxId(), true);
} catch (RuntimeException e) {

View File

@ -26,19 +26,14 @@ package org.jclouds.chef.filters;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;
import java.util.Properties;
import javax.inject.Provider;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpUtils;
@ -86,50 +81,38 @@ public class SignedHeaderAuthTest {
"NMzYZgyooSvU85qkIUmKuCqgG2AIlvYa2Q/2ctrMhoaHhLOCWWoqYNMaEqPc",
"3tKHE+CfvP+WuPdWk4jv4wpIkAz6ZLxToxcGhXmZbXpk56YTmqgBW2cbbw4O",
"IWPZDHSiPcw//AYNgW1CCDptt+UFuaFYbtqZegcBd2n/jzcWODA7zL4KWEUy",
"9q4rlh/+1tBReg60QdsmDRsw/cdO1GZrKtuCwbuD4+nbRdVBKv72rqHX9cu0",
"utju9jzczCyB+sSAQWrxSsXB/b8vV2qs0l4VD2ML+w==" };
"9q4rlh/+1tBReg60QdsmDRsw/cdO1GZrKtuCwbuD4+nbRdVBKv72rqHX9cu0", "utju9jzczCyB+sSAQWrxSsXB/b8vV2qs0l4VD2ML+w==" };
// We expect Mixlib::Authentication::SignedHeaderAuth//sign to return this
// if passed the BODY above.
public static final Multimap<String, String> EXPECTED_SIGN_RESULT = ImmutableMultimap
.<String, String> builder().put("X-Ops-Content-Hash", X_OPS_CONTENT_HASH).put(
"X-Ops-Userid", USER_ID).put("X-Ops-Sign", "version=1.0").put(
"X-Ops-Authorization-1", X_OPS_AUTHORIZATION_LINES[0]).put(
"X-Ops-Authorization-2", X_OPS_AUTHORIZATION_LINES[1]).put(
"X-Ops-Authorization-3", X_OPS_AUTHORIZATION_LINES[2]).put(
"X-Ops-Authorization-4", X_OPS_AUTHORIZATION_LINES[3]).put(
"X-Ops-Authorization-5", X_OPS_AUTHORIZATION_LINES[4]).put(
"X-Ops-Authorization-6", X_OPS_AUTHORIZATION_LINES[5]).put("X-Ops-Timestamp",
TIMESTAMP_ISO8601).build();
public static final Multimap<String, String> EXPECTED_SIGN_RESULT = ImmutableMultimap.<String, String> builder()
.put("X-Ops-Content-Hash", X_OPS_CONTENT_HASH).put("X-Ops-Userid", USER_ID).put("X-Ops-Sign", "version=1.0")
.put("X-Ops-Authorization-1", X_OPS_AUTHORIZATION_LINES[0]).put("X-Ops-Authorization-2",
X_OPS_AUTHORIZATION_LINES[1]).put("X-Ops-Authorization-3", X_OPS_AUTHORIZATION_LINES[2]).put(
"X-Ops-Authorization-4", X_OPS_AUTHORIZATION_LINES[3]).put("X-Ops-Authorization-5",
X_OPS_AUTHORIZATION_LINES[4]).put("X-Ops-Authorization-6", X_OPS_AUTHORIZATION_LINES[5]).put(
"X-Ops-Timestamp", TIMESTAMP_ISO8601).build();
// Content hash for empty string
public static final String X_OPS_CONTENT_HASH_EMPTY = "2jmj7l5rSw0yVb/vlWAYkK/YBwk=";
public static final Multimap<String, String> EXPECTED_SIGN_RESULT_EMPTY = ImmutableMultimap
.<String, String> builder().put("X-Ops-Content-Hash", X_OPS_CONTENT_HASH_EMPTY).put(
"X-Ops-Userid", USER_ID).put("X-Ops-Sign", "version=1.0").put(
"X-Ops-Authorization-1",
"N6U75kopDK64cEFqrB6vw+PnubnXr0w5LQeXnIGNGLRP2LvifwIeisk7QxEx").put(
"X-Ops-Authorization-2",
"mtpQOWAw8HvnWErjzuk9AvUsqVmWpv14ficvkaD79qsPMvbje+aLcIrCGT1P").put(
"X-Ops-Authorization-3",
"3d2uvf4w7iqwzrIscPnkxLR6o6pymR90gvJXDPzV7Le0jbfD8kmZ8AAK0sGG").put(
"X-Ops-Authorization-4",
"09F1ftW80bLatJTA66Cw2wBz261r6x/abZhIKFJFDWLzyQGJ8ZNOkUrDDtgI").put(
"X-Ops-Authorization-5",
"svLVXpOJKZZfKunsElpWjjsyNt3k8vpI1Y4ANO8Eg2bmeCPeEK+YriGm5fbC").put(
"X-Ops-Authorization-6", "DzWNPylHJqMeGKVYwGQKpg62QDfe5yXh3wZLiQcXow==").put(
"X-Ops-Timestamp", TIMESTAMP_ISO8601).build();
.<String, String> builder().put("X-Ops-Content-Hash", X_OPS_CONTENT_HASH_EMPTY).put("X-Ops-Userid", USER_ID)
.put("X-Ops-Sign", "version=1.0").put("X-Ops-Authorization-1",
"N6U75kopDK64cEFqrB6vw+PnubnXr0w5LQeXnIGNGLRP2LvifwIeisk7QxEx").put("X-Ops-Authorization-2",
"mtpQOWAw8HvnWErjzuk9AvUsqVmWpv14ficvkaD79qsPMvbje+aLcIrCGT1P").put("X-Ops-Authorization-3",
"3d2uvf4w7iqwzrIscPnkxLR6o6pymR90gvJXDPzV7Le0jbfD8kmZ8AAK0sGG").put("X-Ops-Authorization-4",
"09F1ftW80bLatJTA66Cw2wBz261r6x/abZhIKFJFDWLzyQGJ8ZNOkUrDDtgI").put("X-Ops-Authorization-5",
"svLVXpOJKZZfKunsElpWjjsyNt3k8vpI1Y4ANO8Eg2bmeCPeEK+YriGm5fbC").put("X-Ops-Authorization-6",
"DzWNPylHJqMeGKVYwGQKpg62QDfe5yXh3wZLiQcXow==").put("X-Ops-Timestamp", TIMESTAMP_ISO8601).build();
public static String PUBLIC_KEY;
public static String PRIVATE_KEY;
static {
try {
PUBLIC_KEY = Utils.toStringAndClose(SignedHeaderAuthTest.class
.getResourceAsStream("/pubkey.txt"));
PUBLIC_KEY = Utils.toStringAndClose(SignedHeaderAuthTest.class.getResourceAsStream("/pubkey.txt"));
PRIVATE_KEY = Utils.toStringAndClose(SignedHeaderAuthTest.class
.getResourceAsStream("/privkey.txt"));
PRIVATE_KEY = Utils.toStringAndClose(SignedHeaderAuthTest.class.getResourceAsStream("/privkey.txt"));
} catch (IOException e) {
Throwables.propagate(e);
}
@ -152,20 +135,17 @@ public class SignedHeaderAuthTest {
HttpRequest request = new HttpRequest(HttpMethod.POST, host);
request.setPayload(BODY);
String expected_string_to_sign = new StringBuilder().append("Method:POST").append("\n")
.append("Hashed Path:").append(HASHED_CANONICAL_PATH).append("\n").append(
"X-Ops-Content-Hash:").append(HASHED_BODY).append("\n").append(
"X-Ops-Timestamp:").append(TIMESTAMP_ISO8601).append("\n").append(
"X-Ops-UserId:").append(USER_ID).toString();
String expected_string_to_sign = new StringBuilder().append("Method:POST").append("\n").append("Hashed Path:")
.append(HASHED_CANONICAL_PATH).append("\n").append("X-Ops-Content-Hash:").append(HASHED_BODY).append("\n")
.append("X-Ops-Timestamp:").append(TIMESTAMP_ISO8601).append("\n").append("X-Ops-UserId:").append(USER_ID)
.toString();
assertEquals(signing_obj.createStringToSign("POST", HASHED_CANONICAL_PATH, HASHED_BODY,
TIMESTAMP_ISO8601), expected_string_to_sign);
assertEquals(signing_obj.sign(expected_string_to_sign), Joiner.on("").join(
X_OPS_AUTHORIZATION_LINES));
assertEquals(signing_obj.createStringToSign("POST", HASHED_CANONICAL_PATH, HASHED_BODY, TIMESTAMP_ISO8601),
expected_string_to_sign);
assertEquals(signing_obj.sign(expected_string_to_sign), Joiner.on("").join(X_OPS_AUTHORIZATION_LINES));
signing_obj.filter(request);
Multimap<String, String> headersWithoutContentLength = LinkedHashMultimap.create(request
.getHeaders());
Multimap<String, String> headersWithoutContentLength = LinkedHashMultimap.create(request.getHeaders());
headersWithoutContentLength.removeAll(HttpHeaders.CONTENT_LENGTH);
assertEquals(headersWithoutContentLength.values(), EXPECTED_SIGN_RESULT.values());
}
@ -177,8 +157,7 @@ public class SignedHeaderAuthTest {
HttpRequest request = new HttpRequest(HttpMethod.DELETE, host);
signing_obj.filter(request);
Multimap<String, String> headersWithoutContentLength = LinkedHashMultimap.create(request
.getHeaders());
Multimap<String, String> headersWithoutContentLength = LinkedHashMultimap.create(request.getHeaders());
assertEquals(headersWithoutContentLength.entries(), EXPECTED_SIGN_RESULT_EMPTY.entries());
}
@ -196,12 +175,6 @@ public class SignedHeaderAuthTest {
private SignedHeaderAuth signing_obj;
private EncryptionService encryptionService;
@Test(enabled = false)
void canParseKeyFromCreateClient() throws IOException {
String key = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA50Iwgq8OIfm5vY9Gwfb6UBt17D7V4djyFSLJ1AbCU/o8Zlrr\nW73JqaK5dC3IO6Dcu+/qPYGtBUWvhFAXrsFOooz0mTod/LtBN1YVurJ60goJrR6w\nKhUYC9H45OW/qcdIM7kdDwiyMZfbHqW6fo0xPqjvgxtZoI+v7pgThacOG6pw7PO6\nGgnJa3MGK3xEbzlI6+EBJWG3EiwexguwOpTD4a4TDIAqKrlVDPeUpU7rFbsBPRS8\nkypR3lU58+WRz/zi9fiH/Sy2X+ChefyZg14HiutJjxc8zJsazF3eDxyLGPQmhv3Mso\nA0wbjGusbe6hPdDkzh/B2KO9u96QCdlGu/rc6QIDAQABAoIBAA/7OgD9+fsNF/Hq\nodgrqja4/xg5a2x1Ip2lTs9RPEKza1Mje1pWrkYD0c8ejtTYFAkE1mozuPJBU5TQ\nOCLChx2iohCovIPHqQUa9Nt3FBfJy8tj8Ian+IZwl0OyQOGJvQqeA00Tq8TTmrfu\negne1gVfhVXJIROAeocBiW/WEJqGti0OE5zQQMld3cJ5viTdEsaWYCu2HaEoblKB\nH6KfRGM2N3L3KjKFGtEg+cX1UdaMlzmp+O5/yvjBykZy6cuUOIsgz2e5nQV4hYEq\ntJ/+6E0QVTXfnVZi4IxKlkVMhyonqOxAOKGG+dWeWh3DqPJFzjmp3kcbRN9E3u+2\nqKU5gpECgYEA+a/i5z2jFCJ8rMpoCPPxm2eiIYZVs3LE33WU5FNNieBRC+KqO06h\nMB3rQ3k8KJDNJYWD5UwIrgjCD5mgkmcSDI6SbOn6PA1Mtw6qZlbeg17V9L9ryXxt\nSfC5AC+qVWd6unrLh0LgkvLS8rgG4GjLZY0HDDMrJWodcc+uWVk3Mo0CgYEA7RsG\nC9gOcHWi6WJ2uEDvLj4IkSkB4IFipEVcKl7VVDSnUgxaBZm3o0DLYDYhIOw7XcQL\n6vpxbRZdlApGyu1ahfMbk3+quFNMQuGxZcv9EhHz7ASnXK6mlrWkJzCGjLz6/MdI\nU0VGbtkBtOY/GaLXdTkQksWowVNoedISfQV9as0CgYEA0Tj1JVecw05yskeZDYd8\nOQCJ9xWd0pSlK6pXbUvweUwiHZd9ldy5bJxle1CnfEZ54KsUbptb2pk0I+ZTitob\nYbJGOEWHjbKHSg1b9A1uvx5EoqWUKG2/FmpEW0eVr6LaUFB9I4aCsCARa5mRCZJG\nfX3DHhHyYZOdwLSKIAyGGDECgYALEwkMQpIiFIyAZBXxcy74tPMHfKfWyZRG4ep1\nHCrQnQj3nxYRTuWx3VPicYTImeAH+CEqX3ouwy2pvXUjA0UIHpu6HutlYpacRRhZ\nDdcLIgWHj4wVmx6yyVcacXzHVAhRCCnLod+xS7d1sI9f7igsFHc+s7a3GOM3VWWB\nq2D5PQKBgQDY9eSb5pc5EYbPy0a/wKFLMNCVLXlDT8jRSC2UnhmcHbhi1IdUbn1j\nR+SuUgrAVNAKzJRY9wmF6Zt0pJ2YLFX7L8HaGyfzkib8kli2sXFonlQ4d0dTdcJo\nVGR1jTxfZQicdPcDPOLPpQz/rP31ZqdHtfaegTOxHebX7W2E5QvPZg==\n-----END RSA PRIVATE KEY-----\n";
KeyPair.class.cast(new PEMReader(new StringReader(key)).readObject());
}
/**
* before class, as we need to ensure that the filter is threadsafe.
*
@ -211,21 +184,15 @@ public class SignedHeaderAuthTest {
@BeforeClass
protected void createFilter() throws IOException {
Injector injector = new RestContextFactory().createContextBuilder("chef", USER_ID,
PRIVATE_KEY, ImmutableSet.<Module> of(new MockModule(), new NullLoggingModule()),
new Properties()).buildInjector();
Injector injector = new RestContextFactory().createContextBuilder("chef", USER_ID, PRIVATE_KEY,
ImmutableSet.<Module> of(new MockModule(), new NullLoggingModule()), new Properties()).buildInjector();
encryptionService = injector.getInstance(EncryptionService.class);
HttpUtils utils = injector.getInstance(HttpUtils.class);
Security.addProvider(new BouncyCastleProvider());
PrivateKey privateKey = injector.getInstance(PrivateKey.class);
KeyPair pair = KeyPair.class.cast(new PEMReader(new StringReader(PRIVATE_KEY)).readObject());
PrivateKey privateKey = pair.getPrivate();
signing_obj = new SignedHeaderAuth(new SignatureWire(), USER_ID, privateKey,
new Provider<String>() {
signing_obj = new SignedHeaderAuth(new SignatureWire(), USER_ID, privateKey, new Provider<String>() {
@Override
public String get() {

View File

@ -0,0 +1,101 @@
package org.jclouds.chef.handlers;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import java.net.URI;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.io.Payloads;
import org.testng.annotations.Test;
/**
* Tests behavior of {@code ChefClientErrorRetryHandler}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "chef.ChefClientErrorRetryHandlerTest")
public class ChefClientErrorRetryHandlerTest {
@Test
public void test401DoesNotRetry() {
HttpCommand command = createMock(HttpCommand.class);
HttpResponse response = createMock(HttpResponse.class);
BackoffLimitedRetryHandler retry = createMock(BackoffLimitedRetryHandler.class);
expect(command.getFailureCount()).andReturn(0);
expect(response.getStatusCode()).andReturn(401).atLeastOnce();
replay(response);
replay(retry);
replay(command);
ChefClientErrorRetryHandler handler = new ChefClientErrorRetryHandler(retry);
assert !handler.shouldRetryRequest(command, response);
verify(retry);
verify(command);
verify(response);
}
@Test
public void test400DoesNotRetry() {
HttpCommand command = createMock(HttpCommand.class);
HttpResponse response = createMock(HttpResponse.class);
BackoffLimitedRetryHandler retry = createMock(BackoffLimitedRetryHandler.class);
expect(command.getFailureCount()).andReturn(0);
expect(response.getStatusCode()).andReturn(401).atLeastOnce();
replay(response);
replay(retry);
replay(command);
ChefClientErrorRetryHandler handler = new ChefClientErrorRetryHandler(retry);
assert !handler.shouldRetryRequest(command, response);
verify(retry);
verify(command);
verify(response);
}
@Test
public void testRetryOn400PutSandbox() {
HttpCommand command = createMock(HttpCommand.class);
BackoffLimitedRetryHandler retry = createMock(BackoffLimitedRetryHandler.class);
HttpRequest request = new HttpRequest("PUT", URI
.create("https://api.opscode.com/organizations/jclouds/sandboxes/bfd68d4052f44053b2e593a33b5e1cd5"));
HttpResponse response = new HttpResponse(
400,
"400 Bad Request",
Payloads
.newStringPayload("{\"error\":[\"Cannot update sandbox bfd68d4052f44053b2e593a33b5e1cd5: checksum 9b7c23369f4b576451216c39f214af6c was not uploaded\"]}"));
expect(command.getFailureCount()).andReturn(0);
expect(command.getRequest()).andReturn(request).atLeastOnce();
expect(retry.shouldRetryRequest(command, response)).andReturn(true);
replay(retry);
replay(command);
ChefClientErrorRetryHandler handler = new ChefClientErrorRetryHandler(retry);
assert handler.shouldRetryRequest(command, response);
verify(retry);
verify(command);
}
}

View File

@ -45,10 +45,21 @@
<id>gson</id>
<url>http://google-gson.googlecode.com/svn/mavenrepo</url>
</repository>
<repository>
<id>oauth</id>
<name>OAuth Repository</name>
<url>http://oauth.googlecode.com/svn/code/maven</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>net.oauth.core</groupId>
<artifactId>oauth</artifactId>
<version>20100527</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>

View File

@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.PrivateKey;
import org.jclouds.encryption.internal.JCEEncryptionService;
import org.jclouds.http.PayloadEnclosing;
@ -73,6 +74,8 @@ public interface EncryptionService {
MD5OutputStream md5OutputStream(OutputStream out);
PrivateKey readPrivateKeyFromPEM(byte [] pem);
public static abstract class MD5OutputStream extends FilterOutputStream {
public MD5OutputStream(OutputStream out) {
super(out);

View File

@ -21,15 +21,25 @@ package org.jclouds.encryption.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.annotation.Resource;
import net.oauth.signature.pem.PEMReader;
import net.oauth.signature.pem.PKCS1EncodedKeySpec;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.http.PayloadEnclosing;
import org.jclouds.io.Payload;
import org.jclouds.logging.Logger;
import com.google.common.base.Throwables;
/**
*
* @author Adrian Cole
@ -41,10 +51,14 @@ public abstract class BaseEncryptionService implements EncryptionService {
protected static final int BUF_SIZE = 0x2000; // 8
final byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
(byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
final byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
(byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b',
(byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
private final KeyFactory rsaKeyFactory;
public BaseEncryptionService(KeyFactory rsaKeyFactory) {
this.rsaKeyFactory = rsaKeyFactory;
}
@Override
public String hex(byte[] raw) {
@ -90,7 +104,6 @@ public abstract class BaseEncryptionService implements EncryptionService {
/**
* {@inheritDoc}
* @
*/
@Override
public <T extends PayloadEnclosing> T generateMD5BufferingIfNotRepeatable(T payloadEnclosing) {
@ -100,4 +113,30 @@ public abstract class BaseEncryptionService implements EncryptionService {
payloadEnclosing.setPayload(newPayload);
return payloadEnclosing;
}
/**
* {@inheritDoc}
*/
@Override
public PrivateKey readPrivateKeyFromPEM(byte[] pem) {
PEMReader reader;
try {
reader = new PEMReader(pem);
byte[] bytes = reader.getDerBytes();
KeySpec keySpec;
if (PEMReader.PRIVATE_PKCS1_MARKER.equals(reader.getBeginMarker())) {
keySpec = (new PKCS1EncodedKeySpec(bytes)).getKeySpec();
} else if (PEMReader.PRIVATE_PKCS8_MARKER.equals(reader.getBeginMarker())) {
keySpec = new PKCS8EncodedKeySpec(bytes);
} else {
throw new IOException("Invalid PEM file: Unknown marker " + "for private key " + reader.getBeginMarker());
}
return rsaKeyFactory.generatePrivate(keySpec);
} catch (Exception e) {
Throwables.propagate(e);
return null;
}
}
}

View File

@ -28,6 +28,7 @@ import java.io.OutputStream;
import java.security.DigestOutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -43,6 +44,14 @@ import org.jclouds.io.payloads.ByteArrayPayload;
*/
public class JCEEncryptionService extends BaseEncryptionService {
public JCEEncryptionService(KeyFactory rsaKeyFactory) {
super(rsaKeyFactory);
}
public JCEEncryptionService() throws NoSuchAlgorithmException {
super(KeyFactory.getInstance("RSA"));
}
@Override
public byte[] hmacSha256(String toEncode, byte[] key) {
return hmac(toEncode, key, "HmacSHA256");

View File

@ -24,6 +24,7 @@ import static org.jclouds.rest.RestContextFactory.createContextBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
@ -85,12 +86,11 @@ public abstract class BaseJettyTest {
encryptionService = Guice.createInjector().getInstance(EncryptionService.class);
md5 = encryptionService
.base64(encryptionService.md5(oneHundredOneConstitutions.getInput()));
md5 = encryptionService.base64(encryptionService.md5(oneHundredOneConstitutions.getInput()));
Handler server1Handler = new AbstractHandler() {
public void handle(String target, HttpServletRequest request,
HttpServletResponse response, int dispatch) throws IOException, ServletException {
public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
throws IOException, ServletException {
if (failIfNoContentLength(request, response)) {
return;
} else if (target.indexOf("redirect") > 0) {
@ -103,8 +103,7 @@ public abstract class BaseJettyTest {
} else if (request.getMethod().equals("PUT")) {
if (request.getContentLength() > 0) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(
Utils.toStringAndClose(request.getInputStream()) + "PUT");
response.getWriter().println(Utils.toStringAndClose(request.getInputStream()) + "PUT");
} else {
response.sendError(500, "no content");
}
@ -116,8 +115,13 @@ public abstract class BaseJettyTest {
if (request.getContentLength() > 0) {
if (request.getHeader("Content-MD5") != null) {
String expectedMd5 = request.getHeader("Content-MD5");
String realMd5FromRequest = Base64.encodeBytes(new JCEEncryptionService()
String realMd5FromRequest;
try {
realMd5FromRequest = Base64.encodeBytes(new JCEEncryptionService()
.md5(request.getInputStream()));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
boolean matched = expectedMd5.equals(realMd5FromRequest);
if (matched) {
response.setContentType("text/xml");
@ -126,8 +130,7 @@ public abstract class BaseJettyTest {
}
} else {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(
Utils.toStringAndClose(request.getInputStream()) + "POST");
response.getWriter().println(Utils.toStringAndClose(request.getInputStream()) + "POST");
}
} else {
handleAction(request, response);
@ -140,7 +143,8 @@ public abstract class BaseJettyTest {
response.getWriter().println("test");
} else if (request.getMethod().equals("HEAD")) {
/*
* NOTE: by HTML specification, HEAD response MUST NOT include a body
* NOTE: by HTML specification, HEAD response MUST NOT include a
* body
*/
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
@ -161,20 +165,24 @@ public abstract class BaseJettyTest {
server.start();
Handler server2Handler = new AbstractHandler() {
public void handle(String target, HttpServletRequest request,
HttpServletResponse response, int dispatch) throws IOException, ServletException {
public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
throws IOException, ServletException {
if (request.getMethod().equals("PUT")) {
if (request.getContentLength() > 0) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(
Utils.toStringAndClose(request.getInputStream()) + "PUTREDIRECT");
response.getWriter().println(Utils.toStringAndClose(request.getInputStream()) + "PUTREDIRECT");
}
} else if (request.getMethod().equals("POST")) {
if (request.getContentLength() > 0) {
if (request.getHeader("Content-MD5") != null) {
String expectedMd5 = request.getHeader("Content-MD5");
String realMd5FromRequest = Base64.encodeBytes(new JCEEncryptionService()
String realMd5FromRequest;
try {
realMd5FromRequest = Base64.encodeBytes(new JCEEncryptionService()
.md5(request.getInputStream()));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
boolean matched = expectedMd5.equals(realMd5FromRequest);
if (matched) {
response.setContentType("text/xml");
@ -183,15 +191,15 @@ public abstract class BaseJettyTest {
}
} else {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(
Utils.toStringAndClose(request.getInputStream()) + "POST");
response.getWriter().println(Utils.toStringAndClose(request.getInputStream()) + "POST");
}
} else {
handleAction(request, response);
}
} else if (request.getMethod().equals("HEAD")) {
/*
* NOTE: by HTML specification, HEAD response MUST NOT include a body
* NOTE: by HTML specification, HEAD response MUST NOT include a
* body
*/
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_OK);
@ -221,8 +229,7 @@ public abstract class BaseJettyTest {
public static InputSupplier<InputStream> getTestDataSupplier() throws IOException {
byte[] oneConstitution = ByteStreams.toByteArray(new GZIPInputStream(BaseJettyTest.class
.getResourceAsStream("/const.txt.gz")));
InputSupplier<ByteArrayInputStream> constitutionSupplier = ByteStreams
.newInputStreamSupplier(oneConstitution);
InputSupplier<ByteArrayInputStream> constitutionSupplier = ByteStreams.newInputStreamSupplier(oneConstitution);
InputSupplier<InputStream> temp = ByteStreams.join(constitutionSupplier);
@ -232,15 +239,14 @@ public abstract class BaseJettyTest {
return temp;
}
public static RestContextBuilder<IntegrationTestClient, IntegrationTestAsyncClient> newBuilder(
int testPort, Properties properties, Module... connectionModules) {
public static RestContextBuilder<IntegrationTestClient, IntegrationTestAsyncClient> newBuilder(int testPort,
Properties properties, Module... connectionModules) {
ContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec(
"test", "http://localhost:" + testPort, "1", "identity", null,
IntegrationTestClient.class, IntegrationTestAsyncClient.class);
ContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec("test",
"http://localhost:" + testPort, "1", "identity", null, IntegrationTestClient.class,
IntegrationTestAsyncClient.class);
return createContextBuilder(contextSpec, ImmutableSet.<Module> copyOf(connectionModules),
properties);
return createContextBuilder(contextSpec, ImmutableSet.<Module> copyOf(connectionModules), properties);
}
@AfterTest
@ -262,8 +268,7 @@ public abstract class BaseJettyTest {
* @return
* @throws IOException
*/
protected boolean failEveryTenRequests(HttpServletRequest request, HttpServletResponse response)
throws IOException {
protected boolean failEveryTenRequests(HttpServletRequest request, HttpServletResponse response) throws IOException {
if (cycle.incrementAndGet() % 10 == 0) {
response.sendError(500);
((Request) request).setHandled(true);
@ -272,8 +277,8 @@ public abstract class BaseJettyTest {
return false;
}
protected boolean redirectEveryTwentyRequests(HttpServletRequest request,
HttpServletResponse response) throws IOException {
protected boolean redirectEveryTwentyRequests(HttpServletRequest request, HttpServletResponse response)
throws IOException {
if (cycle.incrementAndGet() % 20 == 0) {
response.sendRedirect("http://localhost:" + (testPort + 1) + "/");
((Request) request).setHandled(true);
@ -282,8 +287,7 @@ public abstract class BaseJettyTest {
return false;
}
protected boolean failIfNoContentLength(HttpServletRequest request, HttpServletResponse response)
throws IOException {
protected boolean failIfNoContentLength(HttpServletRequest request, HttpServletResponse response) throws IOException {
if (request.getHeader(HttpHeaders.CONTENT_LENGTH) == null) {
response.sendError(500);
((Request) request).setHandled(true);
@ -292,8 +296,7 @@ public abstract class BaseJettyTest {
return false;
}
private void handleAction(HttpServletRequest request, HttpServletResponse response)
throws IOException {
private void handleAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
final Matcher matcher = actionPattern.matcher(request.getRequestURI());
boolean matchFound = matcher.find();
if (matchFound) {

View File

@ -22,6 +22,7 @@ import static org.testng.Assert.assertEquals;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import javax.ws.rs.core.HttpHeaders;
@ -39,13 +40,11 @@ public class BasicAuthenticationTest {
private static final String USER = "Aladdin";
private static final String PASSWORD = "open sesame";
public void testAuth() throws UnsupportedEncodingException {
public void testAuth() throws UnsupportedEncodingException, NoSuchAlgorithmException {
BasicAuthentication filter = new BasicAuthentication(USER, PASSWORD, new JCEEncryptionService());
HttpRequest request = new HttpRequest("GET", URI.create("http://localhost"));
filter.filter(request);
assertEquals(request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION),
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
assertEquals(request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION), "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
}
}

View File

@ -27,6 +27,7 @@ import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@ -35,6 +36,7 @@ import org.jclouds.encryption.internal.JCEEncryptionService;
import org.jclouds.logging.Logger;
import org.testng.annotations.Test;
import com.google.common.base.Throwables;
import com.google.common.io.ByteStreams;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@ -49,7 +51,16 @@ public class WireLiveTest {
private static final String sysHttpStreamUrl = System.getProperty("jclouds.wire.httpstream.url");
private static final String sysHttpStreamMd5 = System.getProperty("jclouds.wire.httpstream.md5");
private static final EncryptionService encryptionService = new JCEEncryptionService();
private static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
encryptionService = null;
}
}
private static class ConnectionTester implements Callable<Void> {
private final InputStream fromServer;
@ -65,8 +76,7 @@ public class WireLiveTest {
ByteStreams.copy(in, out);
byte[] compare = encryptionService.md5(new ByteArrayInputStream(out.toByteArray()));
Thread.sleep(100);
assertEquals(encryptionService.hex(compare), checkNotNull(sysHttpStreamMd5,
sysHttpStreamMd5));
assertEquals(encryptionService.hex(compare), checkNotNull(sysHttpStreamMd5, sysHttpStreamMd5));
assertEquals(((BufferLogger) wire.getWireLog()).buff.toString().getBytes().length, 3331484);
return null;
}
@ -145,8 +155,7 @@ public class WireLiveTest {
InputStream in = wire.input(connection.getInputStream());
byte[] compare = encryptionService.md5(in);
Thread.sleep(100);
assertEquals(encryptionService.hex(compare), checkNotNull(sysHttpStreamMd5,
sysHttpStreamMd5));
assertEquals(encryptionService.hex(compare), checkNotNull(sysHttpStreamMd5, sysHttpStreamMd5));
assertEquals(((BufferLogger) wire.getWireLog()).buff.toString().getBytes().length, 3331484);
}
@ -155,8 +164,7 @@ public class WireLiveTest {
URL url = new URL(checkNotNull(sysHttpStreamUrl, "sysHttpStreamUrl"));
URLConnection connection = url.openConnection();
Callable<Void> callable = new ConnectionTester(connection.getInputStream());
ListenableFuture<Void> result = Futures
.makeListenable(newCachedThreadPool().submit(callable));
ListenableFuture<Void> result = Futures.makeListenable(newCachedThreadPool().submit(callable));
result.get(30, TimeUnit.SECONDS);
}
@ -168,8 +176,7 @@ public class WireLiveTest {
InputStream in = wire.input(connection.getInputStream());
byte[] compare = encryptionService.md5(in);
Thread.sleep(100);
assertEquals(encryptionService.hex(compare), checkNotNull(sysHttpStreamMd5,
sysHttpStreamMd5));
assertEquals(encryptionService.hex(compare), checkNotNull(sysHttpStreamMd5, sysHttpStreamMd5));
assertEquals(((BufferLogger) wire.getWireLog()).buff.toString().getBytes().length, 3331484);
}

View File

@ -27,6 +27,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.inject.Singleton;
@ -48,6 +50,13 @@ import org.jclouds.io.payloads.ByteArrayPayload;
*/
@Singleton
public class BouncyCastleEncryptionService extends BaseEncryptionService {
public BouncyCastleEncryptionService(KeyFactory rsaKeyFactory) {
super(rsaKeyFactory);
}
public BouncyCastleEncryptionService() throws NoSuchAlgorithmException {
super(KeyFactory.getInstance("RSA"));
}
@Override
public byte[] hmacSha256(String toEncode, byte[] key) {

View File

@ -24,11 +24,13 @@ import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.encryption.internal.JCEEncryptionService;
import org.jclouds.http.HttpRequest;
import org.jclouds.io.Payloads;
@ -40,6 +42,7 @@ import org.testng.annotations.Test;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.repackaged.com.google.common.base.Charsets;
import com.google.common.base.Throwables;
import com.google.common.io.Files;
/**
@ -51,10 +54,19 @@ public class ConvertToGaeRequestTest {
ConvertToGaeRequest req;
URI endPoint;
protected volatile static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
}
}
@BeforeTest
void setupClient() throws MalformedURLException {
endPoint = URI.create("http://localhost:80/foo");
req = new ConvertToGaeRequest(new JCEEncryptionService());
req = new ConvertToGaeRequest(encryptionService);
}
@Test

View File

@ -26,11 +26,13 @@ import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.encryption.internal.JCEEncryptionService;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpUtils;
@ -40,6 +42,7 @@ import org.testng.annotations.Test;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.common.base.Throwables;
/**
*
@ -50,10 +53,19 @@ public class ConvertToJcloudsResponseTest {
ConvertToJcloudsResponse req;
URI endPoint;
protected volatile static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
}
}
@BeforeTest
void setupClient() throws MalformedURLException {
endPoint = URI.create("http://localhost:80/foo");
req = new ConvertToJcloudsResponse(new HttpUtils(new JCEEncryptionService(), 0, 0, 0, 0));
req = new ConvertToJcloudsResponse(new HttpUtils(encryptionService, 0, 0, 0, 0));
}
@Test

View File

@ -27,6 +27,7 @@ import static org.testng.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@ -51,6 +52,7 @@ import org.jclouds.util.Utils;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
@ -61,7 +63,15 @@ import com.google.common.collect.Maps;
*/
@Test(groups = "live", testName = "cloudfiles.CloudFilesClientLiveTest")
public class CloudFilesClientLiveTest extends BaseBlobStoreIntegrationTest {
private static final EncryptionService encryptionService = new JCEEncryptionService();
protected volatile static EncryptionService encryptionService;
static {
try {
encryptionService = new JCEEncryptionService();
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
}
}
public CloudFilesClient getApi() {
return (CloudFilesClient) context.getProviderSpecificContext().getApi();