mirror of https://github.com/apache/jclouds.git
Issue 257 naming constraints on s3 buckets
This commit is contained in:
parent
ef1dec6651
commit
dfdbc44700
|
@ -54,6 +54,7 @@ import org.jclouds.aws.s3.options.CopyObjectOptions;
|
||||||
import org.jclouds.aws.s3.options.ListBucketOptions;
|
import org.jclouds.aws.s3.options.ListBucketOptions;
|
||||||
import org.jclouds.aws.s3.options.PutBucketOptions;
|
import org.jclouds.aws.s3.options.PutBucketOptions;
|
||||||
import org.jclouds.aws.s3.options.PutObjectOptions;
|
import org.jclouds.aws.s3.options.PutObjectOptions;
|
||||||
|
import org.jclouds.aws.s3.predicates.validators.BucketNameValidator;
|
||||||
import org.jclouds.aws.s3.xml.AccessControlListHandler;
|
import org.jclouds.aws.s3.xml.AccessControlListHandler;
|
||||||
import org.jclouds.aws.s3.xml.BucketLoggingHandler;
|
import org.jclouds.aws.s3.xml.BucketLoggingHandler;
|
||||||
import org.jclouds.aws.s3.xml.CopyObjectHandler;
|
import org.jclouds.aws.s3.xml.CopyObjectHandler;
|
||||||
|
@ -75,6 +76,7 @@ import org.jclouds.rest.annotations.ExceptionParser;
|
||||||
import org.jclouds.rest.annotations.Headers;
|
import org.jclouds.rest.annotations.Headers;
|
||||||
import org.jclouds.rest.annotations.HostPrefixParam;
|
import org.jclouds.rest.annotations.HostPrefixParam;
|
||||||
import org.jclouds.rest.annotations.ParamParser;
|
import org.jclouds.rest.annotations.ParamParser;
|
||||||
|
import org.jclouds.rest.annotations.ParamValidators;
|
||||||
import org.jclouds.rest.annotations.QueryParams;
|
import org.jclouds.rest.annotations.QueryParams;
|
||||||
import org.jclouds.rest.annotations.RequestFilters;
|
import org.jclouds.rest.annotations.RequestFilters;
|
||||||
import org.jclouds.rest.annotations.ResponseParser;
|
import org.jclouds.rest.annotations.ResponseParser;
|
||||||
|
@ -116,7 +118,8 @@ public interface S3AsyncClient {
|
||||||
@Path("{key}")
|
@Path("{key}")
|
||||||
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
||||||
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
|
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
|
||||||
ListenableFuture<S3Object> getObject(@HostPrefixParam String bucketName,
|
ListenableFuture<S3Object> getObject(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@PathParam("key") String key, GetOptions... options);
|
@PathParam("key") String key, GetOptions... options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,7 +129,8 @@ public interface S3AsyncClient {
|
||||||
@Path("{key}")
|
@Path("{key}")
|
||||||
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
||||||
@ResponseParser(ParseObjectMetadataFromHeaders.class)
|
@ResponseParser(ParseObjectMetadataFromHeaders.class)
|
||||||
ListenableFuture<ObjectMetadata> headObject(@HostPrefixParam String bucketName,
|
ListenableFuture<ObjectMetadata> headObject(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@PathParam("key") String key);
|
@PathParam("key") String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,7 +139,8 @@ public interface S3AsyncClient {
|
||||||
@HEAD
|
@HEAD
|
||||||
@Path("{key}")
|
@Path("{key}")
|
||||||
@ExceptionParser(ReturnFalseOnKeyNotFound.class)
|
@ExceptionParser(ReturnFalseOnKeyNotFound.class)
|
||||||
ListenableFuture<Boolean> objectExists(@HostPrefixParam String bucketName,
|
ListenableFuture<Boolean> objectExists(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@PathParam("key") String key);
|
@PathParam("key") String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,7 +149,8 @@ public interface S3AsyncClient {
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("{key}")
|
@Path("{key}")
|
||||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||||
ListenableFuture<Void> deleteObject(@HostPrefixParam String bucketName,
|
ListenableFuture<Void> deleteObject(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@PathParam("key") String key);
|
@PathParam("key") String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,7 +160,7 @@ public interface S3AsyncClient {
|
||||||
@Path("{key}")
|
@Path("{key}")
|
||||||
@ResponseParser(ParseETagHeader.class)
|
@ResponseParser(ParseETagHeader.class)
|
||||||
ListenableFuture<String> putObject(
|
ListenableFuture<String> putObject(
|
||||||
@HostPrefixParam String bucketName,
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@PathParam("key") @ParamParser(ObjectKey.class) @BinderParam(BindS3ObjectToPayload.class) S3Object object,
|
@PathParam("key") @ParamParser(ObjectKey.class) @BinderParam(BindS3ObjectToPayload.class) S3Object object,
|
||||||
PutObjectOptions... options);
|
PutObjectOptions... options);
|
||||||
|
|
||||||
|
@ -167,7 +173,8 @@ public interface S3AsyncClient {
|
||||||
ListenableFuture<Boolean> putBucketInRegion(
|
ListenableFuture<Boolean> putBucketInRegion(
|
||||||
// TODO endpoint based on region
|
// TODO endpoint based on region
|
||||||
@BinderParam(BindRegionToXmlPayload.class) @Nullable String region,
|
@BinderParam(BindRegionToXmlPayload.class) @Nullable String region,
|
||||||
@HostPrefixParam String bucketName, PutBucketOptions... options);
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
|
PutBucketOptions... options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see S3Client#deleteBucketIfEmpty
|
* @see S3Client#deleteBucketIfEmpty
|
||||||
|
@ -175,7 +182,8 @@ public interface S3AsyncClient {
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("/")
|
@Path("/")
|
||||||
@ExceptionParser(ReturnTrueOn404OrNotFoundFalseIfNotEmpty.class)
|
@ExceptionParser(ReturnTrueOn404OrNotFoundFalseIfNotEmpty.class)
|
||||||
ListenableFuture<Boolean> deleteBucketIfEmpty(@HostPrefixParam String bucketName);
|
ListenableFuture<Boolean> deleteBucketIfEmpty(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see S3Client#bucketExists
|
* @see S3Client#bucketExists
|
||||||
|
@ -184,7 +192,8 @@ public interface S3AsyncClient {
|
||||||
@Path("/")
|
@Path("/")
|
||||||
@QueryParams(keys = "max-keys", values = "0")
|
@QueryParams(keys = "max-keys", values = "0")
|
||||||
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
|
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
|
||||||
ListenableFuture<Boolean> bucketExists(@HostPrefixParam String bucketName);
|
ListenableFuture<Boolean> bucketExists(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see S3Client#getBucketLocation
|
* @see S3Client#getBucketLocation
|
||||||
|
@ -193,7 +202,8 @@ public interface S3AsyncClient {
|
||||||
@QueryParams(keys = "location")
|
@QueryParams(keys = "location")
|
||||||
@Path("/")
|
@Path("/")
|
||||||
@XMLResponseParser(LocationConstraintHandler.class)
|
@XMLResponseParser(LocationConstraintHandler.class)
|
||||||
ListenableFuture<String> getBucketLocation(@HostPrefixParam String bucketName);
|
ListenableFuture<String> getBucketLocation(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see S3Client#getBucketPayer
|
* @see S3Client#getBucketPayer
|
||||||
|
@ -202,7 +212,8 @@ public interface S3AsyncClient {
|
||||||
@QueryParams(keys = "requestPayment")
|
@QueryParams(keys = "requestPayment")
|
||||||
@Path("/")
|
@Path("/")
|
||||||
@XMLResponseParser(PayerHandler.class)
|
@XMLResponseParser(PayerHandler.class)
|
||||||
ListenableFuture<Payer> getBucketPayer(@HostPrefixParam String bucketName);
|
ListenableFuture<Payer> getBucketPayer(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see S3Client#setBucketPayer
|
* @see S3Client#setBucketPayer
|
||||||
|
@ -210,7 +221,8 @@ public interface S3AsyncClient {
|
||||||
@PUT
|
@PUT
|
||||||
@QueryParams(keys = "requestPayment")
|
@QueryParams(keys = "requestPayment")
|
||||||
@Path("/")
|
@Path("/")
|
||||||
ListenableFuture<Void> setBucketPayer(@HostPrefixParam String bucketName,
|
ListenableFuture<Void> setBucketPayer(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@BinderParam(BindPayerToXmlPayload.class) Payer payer);
|
@BinderParam(BindPayerToXmlPayload.class) Payer payer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -219,7 +231,8 @@ public interface S3AsyncClient {
|
||||||
@GET
|
@GET
|
||||||
@Path("/")
|
@Path("/")
|
||||||
@XMLResponseParser(ListBucketHandler.class)
|
@XMLResponseParser(ListBucketHandler.class)
|
||||||
ListenableFuture<ListBucketResponse> listBucket(@HostPrefixParam String bucketName,
|
ListenableFuture<ListBucketResponse> listBucket(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
ListBucketOptions... options);
|
ListBucketOptions... options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,9 +250,10 @@ public interface S3AsyncClient {
|
||||||
@Path("{destinationObject}")
|
@Path("{destinationObject}")
|
||||||
@Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}")
|
@Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}")
|
||||||
@XMLResponseParser(CopyObjectHandler.class)
|
@XMLResponseParser(CopyObjectHandler.class)
|
||||||
ListenableFuture<ObjectMetadata> copyObject(@PathParam("sourceBucket") String sourceBucket,
|
ListenableFuture<ObjectMetadata> copyObject(
|
||||||
|
@PathParam("sourceBucket") String sourceBucket,
|
||||||
@PathParam("sourceObject") String sourceObject,
|
@PathParam("sourceObject") String sourceObject,
|
||||||
@HostPrefixParam String destinationBucket,
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String destinationBucket,
|
||||||
@PathParam("destinationObject") String destinationObject, CopyObjectOptions... options);
|
@PathParam("destinationObject") String destinationObject, CopyObjectOptions... options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -250,7 +264,8 @@ public interface S3AsyncClient {
|
||||||
@XMLResponseParser(AccessControlListHandler.class)
|
@XMLResponseParser(AccessControlListHandler.class)
|
||||||
@ExceptionParser(ThrowContainerNotFoundOn404.class)
|
@ExceptionParser(ThrowContainerNotFoundOn404.class)
|
||||||
@Path("/")
|
@Path("/")
|
||||||
ListenableFuture<AccessControlList> getBucketACL(@HostPrefixParam String bucketName);
|
ListenableFuture<AccessControlList> getBucketACL(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see S3Client#putBucketACL
|
* @see S3Client#putBucketACL
|
||||||
|
@ -258,7 +273,8 @@ public interface S3AsyncClient {
|
||||||
@PUT
|
@PUT
|
||||||
@Path("/")
|
@Path("/")
|
||||||
@QueryParams(keys = "acl")
|
@QueryParams(keys = "acl")
|
||||||
ListenableFuture<Boolean> putBucketACL(@HostPrefixParam String bucketName,
|
ListenableFuture<Boolean> putBucketACL(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
|
@BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,7 +285,8 @@ public interface S3AsyncClient {
|
||||||
@Path("{key}")
|
@Path("{key}")
|
||||||
@XMLResponseParser(AccessControlListHandler.class)
|
@XMLResponseParser(AccessControlListHandler.class)
|
||||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||||
ListenableFuture<AccessControlList> getObjectACL(@HostPrefixParam String bucketName,
|
ListenableFuture<AccessControlList> getObjectACL(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@PathParam("key") String key);
|
@PathParam("key") String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -278,7 +295,8 @@ public interface S3AsyncClient {
|
||||||
@PUT
|
@PUT
|
||||||
@QueryParams(keys = "acl")
|
@QueryParams(keys = "acl")
|
||||||
@Path("{key}")
|
@Path("{key}")
|
||||||
ListenableFuture<Boolean> putObjectACL(@HostPrefixParam String bucketName,
|
ListenableFuture<Boolean> putObjectACL(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@PathParam("key") String key,
|
@PathParam("key") String key,
|
||||||
@BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
|
@BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
|
||||||
|
|
||||||
|
@ -290,7 +308,8 @@ public interface S3AsyncClient {
|
||||||
@XMLResponseParser(BucketLoggingHandler.class)
|
@XMLResponseParser(BucketLoggingHandler.class)
|
||||||
@ExceptionParser(ThrowContainerNotFoundOn404.class)
|
@ExceptionParser(ThrowContainerNotFoundOn404.class)
|
||||||
@Path("/")
|
@Path("/")
|
||||||
ListenableFuture<BucketLogging> getBucketLogging(@HostPrefixParam String bucketName);
|
ListenableFuture<BucketLogging> getBucketLogging(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see S3Client#enableBucketLogging
|
* @see S3Client#enableBucketLogging
|
||||||
|
@ -298,7 +317,8 @@ public interface S3AsyncClient {
|
||||||
@PUT
|
@PUT
|
||||||
@Path("/")
|
@Path("/")
|
||||||
@QueryParams(keys = "logging")
|
@QueryParams(keys = "logging")
|
||||||
ListenableFuture<Void> enableBucketLogging(@HostPrefixParam String bucketName,
|
ListenableFuture<Void> enableBucketLogging(
|
||||||
|
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
|
||||||
@BinderParam(BindBucketLoggingToXmlPayload.class) BucketLogging logging);
|
@BinderParam(BindBucketLoggingToXmlPayload.class) BucketLogging logging);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -308,6 +328,6 @@ public interface S3AsyncClient {
|
||||||
@Path("/")
|
@Path("/")
|
||||||
@QueryParams(keys = "logging")
|
@QueryParams(keys = "logging")
|
||||||
ListenableFuture<Void> disableBucketLogging(
|
ListenableFuture<Void> disableBucketLogging(
|
||||||
@BinderParam(BindNoBucketLoggingToXmlPayload.class) @HostPrefixParam String bucketName);
|
@BinderParam(BindNoBucketLoggingToXmlPayload.class) @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,18 +199,29 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
|
||||||
checkFilters(httpMethod);
|
checkFilters(httpMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||||
|
public void testCopyObjectInvalidName() throws ArrayIndexOutOfBoundsException,
|
||||||
|
SecurityException, IllegalArgumentException, NoSuchMethodException, IOException {
|
||||||
|
Method method = S3AsyncClient.class
|
||||||
|
.getMethod("copyObject", String.class, String.class, String.class, String.class,
|
||||||
|
Array.newInstance(CopyObjectOptions.class, 0).getClass());
|
||||||
|
processor.createRequest(method, "sourceBucket", "sourceObject", "destinationBucket",
|
||||||
|
"destinationObject");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void testCopyObject() throws ArrayIndexOutOfBoundsException, SecurityException,
|
public void testCopyObject() throws ArrayIndexOutOfBoundsException, SecurityException,
|
||||||
IllegalArgumentException, NoSuchMethodException, IOException {
|
IllegalArgumentException, NoSuchMethodException, IOException {
|
||||||
Method method = S3AsyncClient.class
|
Method method = S3AsyncClient.class
|
||||||
.getMethod("copyObject", String.class, String.class, String.class, String.class,
|
.getMethod("copyObject", String.class, String.class, String.class, String.class,
|
||||||
Array.newInstance(CopyObjectOptions.class, 0).getClass());
|
Array.newInstance(CopyObjectOptions.class, 0).getClass());
|
||||||
GeneratedHttpRequest<S3AsyncClient> httpMethod = processor.createRequest(method,
|
GeneratedHttpRequest<S3AsyncClient> httpMethod = processor.createRequest(method,
|
||||||
"sourceBucket", "sourceObject", "destinationBucket", "destinationObject");
|
"sourceBucket", "sourceObject", "destinationbucket", "destinationObject");
|
||||||
|
|
||||||
assertRequestLineEquals(httpMethod,
|
assertRequestLineEquals(httpMethod,
|
||||||
"PUT http://destinationBucket.stub:8080/destinationObject HTTP/1.1");
|
"PUT http://destinationbucket.stub:8080/destinationObject HTTP/1.1");
|
||||||
assertHeadersEqual(httpMethod,
|
assertHeadersEqual(httpMethod,
|
||||||
"Content-Length: 0\nHost: destinationBucket.stub\nx-amz-copy-source: /sourceBucket/sourceObject\n");
|
"Content-Length: 0\nHost: destinationbucket.stub\nx-amz-copy-source: /sourceBucket/sourceObject\n");
|
||||||
assertPayloadEquals(httpMethod, null);
|
assertPayloadEquals(httpMethod, null);
|
||||||
|
|
||||||
assertResponseParserClassEquals(method, httpMethod, ParseSax.class);
|
assertResponseParserClassEquals(method, httpMethod, ParseSax.class);
|
||||||
|
|
|
@ -65,11 +65,15 @@ public class DnsNameValidator extends Validator<String> {
|
||||||
* digits 0 through 9". From Azure: Every Dash (-) Must Be Immediately Preceded and Followed
|
* digits 0 through 9". From Azure: Every Dash (-) Must Be Immediately Preceded and Followed
|
||||||
* by a Letter or Number.
|
* by a Letter or Number.
|
||||||
*/
|
*/
|
||||||
CharMatcher lettersNumbersOrDashes = inRange('a', 'z').or(inRange('0', '9').or(is('-')));
|
CharMatcher range = getAcceptableRange();
|
||||||
if (!lettersNumbersOrDashes.matchesAllOf(name))
|
if (!range.matchesAllOf(name))
|
||||||
throw exception(name, "Should have lowercase ASCII letters, " + "numbers, or dashes");
|
throw exception(name, "Should have lowercase ASCII letters, " + "numbers, or dashes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected CharMatcher getAcceptableRange() {
|
||||||
|
return inRange('a', 'z').or(inRange('0', '9').or(is('-')));
|
||||||
|
}
|
||||||
|
|
||||||
protected IllegalArgumentException exception(String vAppName, String reason) {
|
protected IllegalArgumentException exception(String vAppName, String reason) {
|
||||||
return new IllegalArgumentException(String.format(
|
return new IllegalArgumentException(String.format(
|
||||||
"Object '%s' doesn't match dns naming constraints. " + "Reason: %s.", vAppName,
|
"Object '%s' doesn't match dns naming constraints. " + "Reason: %s.", vAppName,
|
||||||
|
|
Loading…
Reference in New Issue