Issue 257 naming constraints on s3 buckets

This commit is contained in:
Adrian Cole 2010-05-19 11:27:51 -07:00
parent ef1dec6651
commit dfdbc44700
3 changed files with 61 additions and 26 deletions

View File

@ -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);
} }

View File

@ -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);

View File

@ -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,