diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindS3UploadPolicyAndSignature.java b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindS3UploadPolicyAndSignature.java index bc5b9fdaca..b49c9c4b9a 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindS3UploadPolicyAndSignature.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindS3UploadPolicyAndSignature.java @@ -21,21 +21,20 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.io.BaseEncoding.base64; import javax.inject.Inject; -import javax.inject.Singleton; -import org.jclouds.aws.filters.FormSigner; +import org.jclouds.aws.filters.FormSigner.FormSignerV2; import org.jclouds.http.HttpRequest; import org.jclouds.rest.Binder; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; -@Singleton +// TODO: see if this still exists in V4 public class BindS3UploadPolicyAndSignature implements Binder { - private final FormSigner signer; + private final FormSignerV2 signer; @Inject - BindS3UploadPolicyAndSignature(FormSigner signer) { + BindS3UploadPolicyAndSignature(FormSignerV2 signer) { this.signer = signer; } @@ -49,5 +48,4 @@ public class BindS3UploadPolicyAndSignature implements Binder { builder.put("Storage.S3.UploadPolicySignature", signature); return (R) request.toBuilder().replaceFormParams(builder.build()).build(); } - } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/features/BaseEC2ApiTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/features/BaseEC2ApiTest.java index e70f760e10..be2a732ed1 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/features/BaseEC2ApiTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/features/BaseEC2ApiTest.java @@ -28,7 +28,7 @@ import javax.inject.Singleton; import org.jclouds.apis.ApiMetadata; import org.jclouds.aws.domain.Region; -import org.jclouds.aws.filters.FormSigner; +import org.jclouds.aws.filters.FormSigner.FormSignerV2; import org.jclouds.compute.domain.Image; import org.jclouds.date.DateService; import org.jclouds.ec2.EC2ApiMetadata; @@ -115,19 +115,19 @@ public abstract class BaseEC2ApiTest extends BaseRestAnnotationProcessingTest } } - protected FormSigner filter; + protected FormSignerV2 filter; @Override protected void checkFilters(HttpRequest request) { assertEquals(request.getFilters().size(), 1); - assertEquals(request.getFilters().get(0).getClass(), FormSigner.class); + assertEquals(request.getFilters().get(0).getClass(), FormSignerV2.class); } @Override @BeforeTest protected void setupFactory() throws IOException { super.setupFactory(); - this.filter = injector.getInstance(FormSigner.class); + this.filter = injector.getInstance(FormSignerV2.class); } @Override diff --git a/apis/sts/src/main/java/org/jclouds/aws/config/FormSigningHttpApiModule.java b/apis/sts/src/main/java/org/jclouds/aws/config/FormSigningHttpApiModule.java index 57d695a93a..5989a29b7e 100644 --- a/apis/sts/src/main/java/org/jclouds/aws/config/FormSigningHttpApiModule.java +++ b/apis/sts/src/main/java/org/jclouds/aws/config/FormSigningHttpApiModule.java @@ -23,6 +23,7 @@ import javax.inject.Singleton; import org.jclouds.aws.filters.FormSigner; import org.jclouds.date.DateService; import org.jclouds.date.TimeStamp; +import org.jclouds.http.HttpRequest; import org.jclouds.rest.ConfiguresHttpApi; import org.jclouds.rest.RequestSigner; @@ -51,7 +52,18 @@ public abstract class FormSigningHttpApiModule extends AWSHttpApiModule { @Provides @Singleton RequestSigner provideRequestSigner(FormSigner in) { - return in; + if (in instanceof RequestSigner) { + return (RequestSigner) in; + } + return new RequestSigner() { + @Override public String createStringToSign(HttpRequest input) { + return null; + } + + @Override public String sign(String toSign) { + return null; + } + }; } } diff --git a/apis/sts/src/main/java/org/jclouds/aws/filters/FormSigner.java b/apis/sts/src/main/java/org/jclouds/aws/filters/FormSigner.java index f9aee46e42..64c3842922 100644 --- a/apis/sts/src/main/java/org/jclouds/aws/filters/FormSigner.java +++ b/apis/sts/src/main/java/org/jclouds/aws/filters/FormSigner.java @@ -42,7 +42,6 @@ import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; -import javax.inject.Singleton; import org.jclouds.Constants; import org.jclouds.aws.domain.SessionCredentials; @@ -66,154 +65,145 @@ import com.google.common.collect.Multimap; import com.google.common.collect.TreeMultimap; import com.google.common.io.ByteProcessor; import com.google.common.net.HttpHeaders; +import com.google.inject.ImplementedBy; -/** - * - * @see - */ -@Singleton -public class FormSigner implements HttpRequestFilter, RequestSigner { +@ImplementedBy(FormSigner.FormSignerV2.class) +public interface FormSigner extends HttpRequestFilter { - public static final Set mandatoryParametersForSignature = ImmutableSet.of(ACTION, SIGNATURE_METHOD, - SIGNATURE_VERSION, VERSION); + static final class FormSignerV2 implements FormSigner, RequestSigner { - private final SignatureWire signatureWire; - private final String apiVersion; - private final Supplier creds; - private final Provider dateService; - private final Crypto crypto; - private final HttpUtils utils; + public static final Set mandatoryParametersForSignature = ImmutableSet + .of(ACTION, SIGNATURE_METHOD, SIGNATURE_VERSION, VERSION); - @Resource - @Named(Constants.LOGGER_SIGNATURE) - private Logger signatureLog = Logger.NULL; + private final SignatureWire signatureWire; + private final String apiVersion; + private final Supplier creds; + private final Provider dateService; + private final Crypto crypto; + private final HttpUtils utils; - @Inject - public FormSigner(SignatureWire signatureWire, @ApiVersion String apiVersion, - @org.jclouds.location.Provider Supplier creds, @TimeStamp Provider dateService, - Crypto crypto, HttpUtils utils) { - this.signatureWire = signatureWire; - this.apiVersion = apiVersion; - this.creds = creds; - this.dateService = dateService; - this.crypto = crypto; - this.utils = utils; - } + @Resource @Named(Constants.LOGGER_SIGNATURE) + private Logger signatureLog = Logger.NULL; - public HttpRequest filter(HttpRequest request) throws HttpException { - checkNotNull(request.getFirstHeaderOrNull(HttpHeaders.HOST), "request is not ready to sign; host not present"); - Multimap decodedParams = queryParser().apply(request.getPayload().getRawContent().toString()); - decodedParams.replaceValues(VERSION, ImmutableSet.of(apiVersion)); - addSigningParams(decodedParams); - validateParams(decodedParams); - String stringToSign = createStringToSign(request, decodedParams); - String signature = sign(stringToSign); - addSignature(decodedParams, signature); - request = setPayload(request, decodedParams); - utils.logRequest(signatureLog, request, "<<"); - return request; - } - - HttpRequest setPayload(HttpRequest request, Multimap decodedParams) { - String queryLine = buildQueryLine(decodedParams); - request.setPayload(queryLine); - request.getPayload().getContentMetadata().setContentType("application/x-www-form-urlencoded"); - return request; - } - - private static final Comparator actionFirstAccessKeyLast = new Comparator() { - static final int LEFT_IS_GREATER = 1; - static final int RIGHT_IS_GREATER = -1; - - @Override - public int compare(String left, String right) { - if (left == right) { - return 0; - } - if ("Action".equals(right) || "AWSAccessKeyId".equals(left)) { - return LEFT_IS_GREATER; - } - if ("Action".equals(left) || "AWSAccessKeyId".equals(right)) { - return RIGHT_IS_GREATER; - } - return natural().compare(left, right); + @Inject FormSignerV2(SignatureWire signatureWire, @ApiVersion String apiVersion, + @org.jclouds.location.Provider Supplier creds, @TimeStamp Provider dateService, + Crypto crypto, HttpUtils utils) { + this.signatureWire = signatureWire; + this.apiVersion = apiVersion; + this.creds = creds; + this.dateService = dateService; + this.crypto = crypto; + this.utils = utils; } - }; - private static String buildQueryLine(Multimap decodedParams) { - Multimap sortedParams = TreeMultimap.create(actionFirstAccessKeyLast, natural()); - sortedParams.putAll(decodedParams); - return encodeQueryLine(sortedParams); - } - - @VisibleForTesting - void validateParams(Multimap params) { - for (String parameter : mandatoryParametersForSignature) { - checkState(params.containsKey(parameter), "parameter " + parameter + " is required for signature"); + public HttpRequest filter(HttpRequest request) throws HttpException { + checkNotNull(request.getFirstHeaderOrNull(HttpHeaders.HOST), "request is not ready to sign; host not present"); + Multimap decodedParams = queryParser().apply(request.getPayload().getRawContent().toString()); + decodedParams.replaceValues(VERSION, ImmutableSet.of(apiVersion)); + addSigningParams(decodedParams); + validateParams(decodedParams); + String stringToSign = createStringToSign(request, decodedParams); + String signature = sign(stringToSign); + addSignature(decodedParams, signature); + request = setPayload(request, decodedParams); + utils.logRequest(signatureLog, request, "<<"); + return request; } - } - @VisibleForTesting - void addSignature(Multimap params, String signature) { - params.replaceValues(SIGNATURE, ImmutableList.of(signature)); - } + HttpRequest setPayload(HttpRequest request, Multimap decodedParams) { + String queryLine = buildQueryLine(decodedParams); + request.setPayload(queryLine); + request.getPayload().getContentMetadata().setContentType("application/x-www-form-urlencoded"); + return request; + } - @VisibleForTesting - public String sign(String toSign) { - String signature; - try { - ByteProcessor hmacSHA256 = asByteProcessor(crypto.hmacSHA256(creds.get().credential.getBytes(UTF_8))); - signature = base64().encode(readBytes(toInputStream(toSign), hmacSHA256)); + private static final Comparator actionFirstAccessKeyLast = new Comparator() { + static final int LEFT_IS_GREATER = 1; + static final int RIGHT_IS_GREATER = -1; + + @Override + public int compare(String left, String right) { + if (left == right) { + return 0; + } + if ("Action".equals(right) || "AWSAccessKeyId".equals(left)) { + return LEFT_IS_GREATER; + } + if ("Action".equals(left) || "AWSAccessKeyId".equals(right)) { + return RIGHT_IS_GREATER; + } + return natural().compare(left, right); + } + }; + + private static String buildQueryLine(Multimap decodedParams) { + Multimap sortedParams = TreeMultimap.create(actionFirstAccessKeyLast, natural()); + sortedParams.putAll(decodedParams); + return encodeQueryLine(sortedParams); + } + + @VisibleForTesting void validateParams(Multimap params) { + for (String parameter : mandatoryParametersForSignature) { + checkState(params.containsKey(parameter), "parameter " + parameter + " is required for signature"); + } + } + + @VisibleForTesting void addSignature(Multimap params, String signature) { + params.replaceValues(SIGNATURE, ImmutableList.of(signature)); + } + + @VisibleForTesting + public String sign(String toSign) { + String signature; + try { + ByteProcessor hmacSHA256 = asByteProcessor( + crypto.hmacSHA256(creds.get().credential.getBytes(UTF_8))); + signature = base64().encode(readBytes(toInputStream(toSign), hmacSHA256)); + if (signatureWire.enabled()) + signatureWire.input(toInputStream(signature)); + } catch (Exception e) { + throw new HttpException("error signing request", e); + } + return signature; + } + + @VisibleForTesting + public String createStringToSign(HttpRequest request, Multimap decodedParams) { + utils.logRequest(signatureLog, request, ">>"); + StringBuilder stringToSign = new StringBuilder(); + // StringToSign = HTTPVerb + "\n" + + stringToSign.append(request.getMethod()).append("\n"); + // ValueOfHostHeaderInLowercase + "\n" + + stringToSign.append(request.getFirstHeaderOrNull(HttpHeaders.HOST).toLowerCase()).append("\n"); + // HTTPRequestURI + "\n" + + stringToSign.append(request.getEndpoint().getPath()).append("\n"); + // CanonicalizedFormString + stringToSign.append(buildCanonicalizedString(decodedParams)); if (signatureWire.enabled()) - signatureWire.input(toInputStream(signature)); - } catch (Exception e) { - throw new HttpException("error signing request", e); + signatureWire.output(stringToSign.toString()); + return stringToSign.toString(); } - return signature; - } - @VisibleForTesting - public String createStringToSign(HttpRequest request, Multimap decodedParams) { - utils.logRequest(signatureLog, request, ">>"); - StringBuilder stringToSign = new StringBuilder(); - // StringToSign = HTTPVerb + "\n" + - stringToSign.append(request.getMethod()).append("\n"); - // ValueOfHostHeaderInLowercase + "\n" + - stringToSign.append(request.getFirstHeaderOrNull(HttpHeaders.HOST).toLowerCase()).append("\n"); - // HTTPRequestURI + "\n" + - stringToSign.append(request.getEndpoint().getPath()).append("\n"); - // CanonicalizedFormString - stringToSign.append(buildCanonicalizedString(decodedParams)); - if (signatureWire.enabled()) - signatureWire.output(stringToSign.toString()); - return stringToSign.toString(); - } - - @VisibleForTesting - String buildCanonicalizedString(Multimap decodedParams) { - // note that aws wants to percent encode the canonicalized string without skipping '/' and '?' - return encodeQueryLine(TreeMultimap.create(decodedParams), ImmutableList. of()); - } - - - @VisibleForTesting - void addSigningParams(Multimap params) { - params.removeAll(SIGNATURE); - params.removeAll(SECURITY_TOKEN); - Credentials current = creds.get(); - if (current instanceof SessionCredentials) { - params.put(SECURITY_TOKEN, SessionCredentials.class.cast(current).getSessionToken()); + @VisibleForTesting String buildCanonicalizedString(Multimap decodedParams) { + // note that aws wants to percent encode the canonicalized string without skipping '/' and '?' + return encodeQueryLine(TreeMultimap.create(decodedParams), ImmutableList.of()); } - params.replaceValues(SIGNATURE_METHOD, ImmutableList.of("HmacSHA256")); - params.replaceValues(SIGNATURE_VERSION, ImmutableList.of("2")); - params.replaceValues(TIMESTAMP, ImmutableList.of(dateService.get())); - params.replaceValues(AWS_ACCESS_KEY_ID, ImmutableList.of(creds.get().identity)); - } - public String createStringToSign(HttpRequest input) { - return createStringToSign(input, queryParser().apply(input.getPayload().getRawContent().toString())); - } + @VisibleForTesting void addSigningParams(Multimap params) { + params.removeAll(SIGNATURE); + params.removeAll(SECURITY_TOKEN); + Credentials current = creds.get(); + if (current instanceof SessionCredentials) { + params.put(SECURITY_TOKEN, SessionCredentials.class.cast(current).getSessionToken()); + } + params.replaceValues(SIGNATURE_METHOD, ImmutableList.of("HmacSHA256")); + params.replaceValues(SIGNATURE_VERSION, ImmutableList.of("2")); + params.replaceValues(TIMESTAMP, ImmutableList.of(dateService.get())); + params.replaceValues(AWS_ACCESS_KEY_ID, ImmutableList.of(creds.get().identity)); + } + public String createStringToSign(HttpRequest input) { + return createStringToSign(input, queryParser().apply(input.getPayload().getRawContent().toString())); + } + } } diff --git a/apis/sts/src/main/java/org/jclouds/aws/filters/FormSignerV4.java b/apis/sts/src/main/java/org/jclouds/aws/filters/FormSignerV4.java new file mode 100644 index 0000000000..2743c3d62e --- /dev/null +++ b/apis/sts/src/main/java/org/jclouds/aws/filters/FormSignerV4.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.filters; + +import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.hash.Hashing.sha256; +import static com.google.common.io.BaseEncoding.base16; +import static com.google.common.net.HttpHeaders.AUTHORIZATION; +import static com.google.common.net.HttpHeaders.HOST; +import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; +import static org.jclouds.aws.reference.FormParameters.ACTION; +import static org.jclouds.aws.reference.FormParameters.VERSION; + +import java.net.URI; +import java.security.GeneralSecurityException; +import java.util.List; +import java.util.Map; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.inject.Inject; + +import org.jclouds.aws.domain.SessionCredentials; +import org.jclouds.date.TimeStamp; +import org.jclouds.domain.Credentials; +import org.jclouds.http.HttpException; +import org.jclouds.http.HttpRequest; +import org.jclouds.io.Payload; +import org.jclouds.io.Payloads; +import org.jclouds.location.Provider; +import org.jclouds.providers.ProviderMetadata; +import org.jclouds.rest.annotations.ApiVersion; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import com.google.inject.ImplementedBy; + +public final class FormSignerV4 implements FormSigner { + + // Specifying a default for how to parse the service and region in this way allows + // tests or other downstream services to not have to use guice overrides. + @ImplementedBy(ServiceAndRegion.AWSServiceAndRegion.class) + public interface ServiceAndRegion { + String service(); + String region(String host); + + static final class AWSServiceAndRegion implements ServiceAndRegion { + private final String service; + + @Inject AWSServiceAndRegion(ProviderMetadata provider) { + this(provider.getEndpoint()); + } + + AWSServiceAndRegion(String endpoint) { + this.service = parseServiceAndRegion(URI.create(checkNotNull(endpoint, "endpoint")).getHost()).get(0); + } + + @Override public String service() { + return service; + } + + @Override public String region(String host) { + return parseServiceAndRegion(host).get(1); + } + + /** This will only work for amazon deployments, and perhaps not all of them. */ + private static List parseServiceAndRegion(String host) { + checkArgument(host.endsWith(".amazonaws.com"), "Only AWS endpoints currently supported %s", host); + return Splitter.on('.').splitToList(host); + } + } + } + + private final String apiVersion; + private final Supplier creds; + private final javax.inject.Provider iso8601Timestamp; + private final ServiceAndRegion serviceAndRegion; + + @Inject FormSignerV4(@ApiVersion String apiVersion, @Provider Supplier creds, + @TimeStamp javax.inject.Provider iso8601Timestamp, ServiceAndRegion serviceAndRegion) { + this.apiVersion = apiVersion; + this.creds = creds; + this.iso8601Timestamp = iso8601Timestamp; + this.serviceAndRegion = serviceAndRegion; + } + + @Override public HttpRequest filter(HttpRequest request) throws HttpException { + checkArgument(request.getHeaders().containsKey(HOST), "request is not ready to sign; host not present"); + String host = request.getFirstHeaderOrNull(HOST); + String form = request.getPayload().getRawContent().toString(); + checkArgument(form.indexOf(ACTION) != -1, "request is not ready to sign; Action not present %s", form); + + String timestamp = iso8601Timestamp.get(); + String datestamp = timestamp.substring(0, 8); + + String service = serviceAndRegion.service(); + String region = serviceAndRegion.region(host); + String credentialScope = Joiner.on('/').join(datestamp, region, service, "aws4_request"); + + // content-type is not a required signing param. However, examples use this, so we include it to ease testing. + ImmutableMap.Builder signedHeadersBuilder = ImmutableMap. builder() // + .put("content-type", request.getPayload().getContentMetadata().getContentType()) // + .put("host", host) // + .put("x-amz-date", timestamp); + + HttpRequest.Builder requestBuilder = request.toBuilder() // + .removeHeader(AUTHORIZATION) // + .replaceHeader("X-Amz-Date", timestamp); + + if (form.indexOf(VERSION) == -1) { + requestBuilder.addFormParam("Version", apiVersion); + } + + Credentials credentials = creds.get(); + + if (credentials instanceof SessionCredentials) { + String token = SessionCredentials.class.cast(credentials).getSessionToken(); + requestBuilder.replaceHeader("X-Amz-Security-Token", token); + signedHeadersBuilder.put("x-amz-security-token", token); + } + + ImmutableMap signedHeaders = signedHeadersBuilder.build(); + + String stringToSign = createStringToSign(requestBuilder.build(), signedHeaders, credentialScope); + byte[] signatureKey = signatureKey(credentials.credential, datestamp, region, service); + String signature = base16().lowerCase().encode(hmacSHA256(stringToSign, signatureKey)); + + StringBuilder authorization = new StringBuilder("AWS4-HMAC-SHA256 "); + authorization.append("Credential=").append(credentials.identity).append('/').append(credentialScope).append(", "); + authorization.append("SignedHeaders=").append(Joiner.on(';').join(signedHeaders.keySet())).append(", "); + authorization.append("Signature=").append(signature); + + return requestBuilder.addHeader(AUTHORIZATION, authorization.toString()).build(); + } + + // TODO: change EC2 apis to add this themselves with @FormParams + private Payload addVersionIfNecessary(Payload payload, String form) { + if (form.indexOf(VERSION) == -1) { + form += "&Version=" + apiVersion; + payload = Payloads.newStringPayload(form); + payload.getContentMetadata().setContentType(APPLICATION_FORM_URLENCODED); + payload.getContentMetadata().setContentLength((long) form.length()); + } + return payload; + } + + static byte[] signatureKey(String secretKey, String datestamp, String region, String service) { + byte[] kSecret = ("AWS4" + secretKey).getBytes(UTF_8); + byte[] kDate = hmacSHA256(datestamp, kSecret); + byte[] kRegion = hmacSHA256(region, kDate); + byte[] kService = hmacSHA256(service, kRegion); + byte[] kSigning = hmacSHA256("aws4_request", kService); + return kSigning; + } + + static byte[] hmacSHA256(String data, byte[] key) { + try { + String algorithm = "HmacSHA256"; + Mac mac = Mac.getInstance(algorithm); + mac.init(new SecretKeySpec(key, algorithm)); + return mac.doFinal(data.getBytes(UTF_8)); + } catch (GeneralSecurityException e) { + throw new HttpException(e); + } + } + + static String createStringToSign(HttpRequest request, Map signedHeaders, String credentialScope) { + StringBuilder canonicalRequest = new StringBuilder(); + // HTTPRequestMethod + '\n' + + canonicalRequest.append(request.getMethod()).append("\n"); + // CanonicalURI + '\n' + + canonicalRequest.append(request.getEndpoint().getPath()).append("\n"); + // CanonicalQueryString + '\n' + + checkArgument(request.getEndpoint().getQuery() == null, "Query parameters not yet supported %s", request); + canonicalRequest.append("\n"); + // CanonicalHeaders + '\n' + + for (Map.Entry entry : signedHeaders.entrySet()) { + canonicalRequest.append(entry.getKey()).append(':').append(entry.getValue()).append('\n'); + } + canonicalRequest.append("\n"); + + // SignedHeaders + '\n' + + canonicalRequest.append(Joiner.on(';').join(signedHeaders.keySet())).append('\n'); + + // HexEncode(Hash(Payload)) + String payload = request.getPayload().getRawContent().toString(); + canonicalRequest.append(base16().lowerCase().encode(sha256().hashString(payload, UTF_8).asBytes())); + + StringBuilder toSign = new StringBuilder(); + // Algorithm + '\n' + + toSign.append("AWS4-HMAC-SHA256").append('\n'); + // RequestDate + '\n' + + toSign.append(signedHeaders.get("x-amz-date")).append('\n'); + // CredentialScope + '\n' + + toSign.append(credentialScope).append('\n'); + // HexEncode(Hash(CanonicalRequest)) + toSign.append(base16().lowerCase().encode(sha256().hashString(canonicalRequest.toString(), UTF_8).asBytes())); + + return toSign.toString(); + } +} diff --git a/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerTest.java b/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV2Test.java similarity index 90% rename from apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerTest.java rename to apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV2Test.java index 2325751bcf..71fa8f96fc 100644 --- a/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerTest.java +++ b/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV2Test.java @@ -22,6 +22,7 @@ import static org.jclouds.providers.AnonymousProviderMetadata.forApiOnEndpoint; import static org.testng.Assert.assertEquals; import org.jclouds.ContextBuilder; +import org.jclouds.aws.filters.FormSigner.FormSignerV2; import org.jclouds.aws.xml.SessionCredentialsHandlerTest; import org.jclouds.date.TimeStamp; import org.jclouds.domain.Credentials; @@ -40,13 +41,9 @@ import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.name.Names; -/** - * Tests behavior of {@code FormSigner} - */ -// NOTE:without testName, this will not call @Before* and fail w/NPE during -// surefire -@Test(groups = "unit", singleThreaded = true, testName = "FormSignerTest") -public class FormSignerTest { + +@Test(groups = "unit", singleThreaded = true, testName = "FormSignerV2Test") +public class FormSignerV2Test { public static Injector injector(Credentials creds) { return ContextBuilder .newBuilder(forApiOnEndpoint(IntegrationTestClient.class, "http://localhost")) @@ -54,7 +51,7 @@ public class FormSignerTest { .modules(ImmutableList. of(new MockModule(), new NullLoggingModule(), new AbstractModule() { @Override protected void configure() { - bind(RequestSigner.class).to(FormSigner.class); + bind(RequestSigner.class).to(FormSignerV2.class); bind(String.class).annotatedWith(Names.named(PROPERTY_HEADER_TAG)).toInstance("amz"); bind(String.class).annotatedWith(TimeStamp.class).toInstance("2009-11-08T15:54:08.897Z"); } @@ -62,11 +59,11 @@ public class FormSignerTest { })).buildInjector(); } - public static FormSigner filter(Credentials creds) { - return injector(creds).getInstance(FormSigner.class); + public static FormSignerV2 filter(Credentials creds) { + return injector(creds).getInstance(FormSignerV2.class); } - public static FormSigner staticCredentialsFilter = filter(new Credentials("identity", "credential")); + public static FormSignerV2 staticCredentialsFilter = filter(new Credentials("identity", "credential")); HttpRequest request = HttpRequest.builder().method(GET) .endpoint("http://localhost") @@ -98,5 +95,4 @@ public class FormSignerTest { .put("Version", "2010-06-15").build()), "AWSAccessKeyId=foo&Action=DescribeImages&Expires=2008-02-10T12%3A00%3A00Z&ImageId.1=ami-2bb65342&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2010-06-15"); } - } diff --git a/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV4LiveTest.java b/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV4LiveTest.java new file mode 100644 index 0000000000..bd697bd2e8 --- /dev/null +++ b/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV4LiveTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.filters; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.jclouds.aws.filters.FormSignerV4.ServiceAndRegion; +import static org.jclouds.aws.filters.FormSignerV4.ServiceAndRegion.AWSServiceAndRegion; +import static org.jclouds.sts.options.SessionCredentialsOptions.Builder.durationSeconds; +import static org.testng.Assert.assertEquals; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import javax.inject.Provider; + +import org.jclouds.apis.BaseApiLiveTest; +import org.jclouds.aws.domain.SessionCredentials; +import org.jclouds.domain.Credentials; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.ApiContext; +import org.jclouds.sts.STSApi; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +@Test(groups = "live", singleThreaded = true, testName = "FormSignerV4LiveTest") +public class FormSignerV4LiveTest extends BaseApiLiveTest> { + + /** Example request, which hopefully the test user's account has access to! */ + private final HttpRequest sampleRequest = HttpRequest.builder() // + .method("POST") // + .endpoint("https://ec2.us-east-1.amazonaws.com/") // + .addHeader("Host", "ec2.us-east-1.amazonaws.com") // + .addFormParam("Action", "DescribeRegions") // + .addFormParam("Version", "2010-08-31") // + .build(); + + /** Provides the expected iso8601 timestamp format for signature v4. */ + private final Provider timestamp = new Provider() { + SimpleDateFormat iso8601 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); + + @Override public String get() { + iso8601.setTimeZone(TimeZone.getTimeZone("GMT")); + return iso8601.format(new Date()); + } + }; + + /** Test how we parse the AWS service and region based on an endpoint. */ + private final ServiceAndRegion serviceAndRegion = new AWSServiceAndRegion(sampleRequest.getEndpoint().toString()); + + public FormSignerV4LiveTest() { + provider = "sts"; + } + + public void signatureV4() { + Supplier accessAndSecretKey = Suppliers.ofInstance(new Credentials(identity, credential)); + + FormSignerV4 filter = new FormSignerV4(apiVersion, accessAndSecretKey, timestamp, serviceAndRegion); + + HttpRequest request = filter.filter(sampleRequest); + + assertEquals(api.utils().http().invoke(request).getStatusCode(), 200); + } + + public void signatureV4_session() { + SessionCredentials creds = api.getApi().createTemporaryCredentials(durationSeconds(MINUTES.toSeconds(15))); + Supplier sessionToken = Suppliers.ofInstance(creds); + + FormSignerV4 filter = new FormSignerV4(apiVersion, sessionToken, timestamp, serviceAndRegion); + + HttpRequest request = filter.filter(sampleRequest); + + assertEquals(api.utils().http().invoke(request).getStatusCode(), 200); + } +} diff --git a/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV4Test.java b/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV4Test.java new file mode 100644 index 0000000000..2f94c26b7f --- /dev/null +++ b/apis/sts/src/test/java/org/jclouds/aws/filters/FormSignerV4Test.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.filters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; + +import javax.inject.Provider; + +import org.jclouds.aws.domain.SessionCredentials; +import org.jclouds.aws.filters.FormSignerV4.ServiceAndRegion; +import org.jclouds.aws.xml.SessionCredentialsHandlerTest; +import org.jclouds.domain.Credentials; +import org.jclouds.http.HttpRequest; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +/** + * Using samples from Amazon + * docs + */ +@Test +public class FormSignerV4Test { + + String apiVersion = "2010-05-08"; + + Supplier accessAndSecretKey = Suppliers + .ofInstance(new Credentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY")); + + Provider timestamp = new Provider() { + @Override public String get() { + return "20110909T233600Z"; + } + }; + + ServiceAndRegion serviceAndRegion = new ServiceAndRegion() { + @Override public String service() { + return "iam"; + } + + @Override public String region(String host) { + return "us-east-1"; + } + }; + + public void signSampleRequest() { + HttpRequest request = HttpRequest.builder() // + .method("POST") // + .endpoint("https://iam.amazonaws.com/") // + .addHeader("Host", "iam.amazonaws.com") // + .payload("Action=ListUsers&Version=2010-05-08") + .build(); + + request.getPayload().getContentMetadata().setContentType("application/x-www-form-urlencoded; charset=utf-8"); + + FormSignerV4 filter = new FormSignerV4(apiVersion, accessAndSecretKey, timestamp, serviceAndRegion); + + HttpRequest filtered = filter.filter(request); + + assertEquals(filtered.getFirstHeaderOrNull("X-Amz-Date"), timestamp.get()); + + String sampleSignature = "ced6826de92d2bdeed8f846f0bf508e8559e98e4b0199114b84c54174deb456c"; + + assertThat(filtered.getFirstHeaderOrNull("Authorization")).endsWith("Signature=" + sampleSignature); + } + + public void sessionTokenRequest() { + HttpRequest request = HttpRequest.builder() // + .method("POST") // + .endpoint("https://iam.amazonaws.com/") // + .addHeader("Host", "iam.amazonaws.com") // + .payload("Action=ListUsers&Version=2010-05-08").build(); + + request.getPayload().getContentMetadata().setContentType("application/x-www-form-urlencoded; charset=utf-8"); + + SessionCredentials sessionCredentials = new SessionCredentialsHandlerTest().expected(); + + FormSignerV4 filter = new FormSignerV4(apiVersion, Suppliers.ofInstance(sessionCredentials), + timestamp, serviceAndRegion); + + HttpRequest filtered = filter.filter(request); + + assertEquals(filtered.getFirstHeaderOrNull("X-Amz-Date"), timestamp.get()); + assertEquals(filtered.getFirstHeaderOrNull("X-Amz-Security-Token"), sessionCredentials.getSessionToken()); + } +} diff --git a/apis/sts/src/test/java/org/jclouds/aws/util/AWSUtilsTest.java b/apis/sts/src/test/java/org/jclouds/aws/util/AWSUtilsTest.java index 8c80c32dd9..33014d45ee 100644 --- a/apis/sts/src/test/java/org/jclouds/aws/util/AWSUtilsTest.java +++ b/apis/sts/src/test/java/org/jclouds/aws/util/AWSUtilsTest.java @@ -27,7 +27,7 @@ import java.io.IOException; import java.io.InputStream; import org.jclouds.aws.domain.AWSError; -import org.jclouds.aws.filters.FormSignerTest; +import org.jclouds.aws.filters.FormSignerV2Test; import org.jclouds.domain.Credentials; import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpRequest; @@ -47,7 +47,7 @@ public class AWSUtilsTest { @BeforeTest protected void setUpInjector() throws IOException { - utils = FormSignerTest.injector(new Credentials("identity", "credential")).getInstance(AWSUtils.class); + utils = FormSignerV2Test.injector(new Credentials("identity", "credential")).getInstance(AWSUtils.class); command = createMock(HttpCommand.class); expect(command.getCurrentRequest()).andReturn(createMock(HttpRequest.class)).atLeastOnce(); diff --git a/apis/sts/src/test/java/org/jclouds/aws/xml/SessionCredentialsHandlerTest.java b/apis/sts/src/test/java/org/jclouds/aws/xml/SessionCredentialsHandlerTest.java index a615046589..cac60a4011 100644 --- a/apis/sts/src/test/java/org/jclouds/aws/xml/SessionCredentialsHandlerTest.java +++ b/apis/sts/src/test/java/org/jclouds/aws/xml/SessionCredentialsHandlerTest.java @@ -51,5 +51,4 @@ public class SessionCredentialsHandlerTest extends BaseHandlerTest { .sessionToken("AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT") .expiration(new SimpleDateFormatDateService().iso8601DateParse("2011-07-11T19:55:29.611Z")).build(); } - } diff --git a/core/src/main/java/org/jclouds/rest/RequestSigner.java b/core/src/main/java/org/jclouds/rest/RequestSigner.java index 56736847c9..7bff9e028c 100644 --- a/core/src/main/java/org/jclouds/rest/RequestSigner.java +++ b/core/src/main/java/org/jclouds/rest/RequestSigner.java @@ -18,6 +18,11 @@ package org.jclouds.rest; import org.jclouds.http.HttpRequest; +/** + * @deprecated This is an internal interface historically used to debug signature logic. It currently is a broken + * abstraction as AWS Signature v4 requires multiple parameters to sign a request. + */ +@Deprecated public interface RequestSigner { String createStringToSign(HttpRequest input); diff --git a/core/src/main/java/org/jclouds/rest/config/RestModule.java b/core/src/main/java/org/jclouds/rest/config/RestModule.java index 354430037d..f2b428ecbe 100644 --- a/core/src/main/java/org/jclouds/rest/config/RestModule.java +++ b/core/src/main/java/org/jclouds/rest/config/RestModule.java @@ -40,16 +40,11 @@ import org.jclouds.rest.internal.TransformerForRequest; import com.google.common.base.Function; import com.google.common.base.Predicate; -import com.google.common.base.Supplier; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; import com.google.inject.assistedinject.FactoryModuleBuilder; public class RestModule extends AbstractModule { - - public static final TypeLiteral> URI_SUPPLIER_TYPE = new TypeLiteral>() { - }; - protected final AtomicReference authException = newReference(); protected void installLocations() { diff --git a/providers/aws-ec2/pom.xml b/providers/aws-ec2/pom.xml index 616f4eb210..a3a80ce35c 100644 --- a/providers/aws-ec2/pom.xml +++ b/providers/aws-ec2/pom.xml @@ -101,6 +101,18 @@ ${project.version} test + + com.squareup.okhttp + mockwebserver + test + + + + org.bouncycastle + bcprov-jdk15on + + + diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2HttpApiModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2HttpApiModule.java index 9ba5af803d..d025fc7627 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2HttpApiModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2HttpApiModule.java @@ -16,6 +16,9 @@ */ package org.jclouds.aws.ec2.config; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; import javax.inject.Singleton; @@ -24,12 +27,15 @@ import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.aws.ec2.domain.SpotInstanceRequest; import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance; import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; +import org.jclouds.aws.filters.FormSigner; +import org.jclouds.aws.filters.FormSignerV4; +import org.jclouds.date.DateService; import org.jclouds.ec2.EC2Api; import org.jclouds.ec2.config.BaseEC2HttpApiModule; -import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.ec2.features.AMIApi; import org.jclouds.ec2.features.InstanceApi; import org.jclouds.ec2.features.SecurityGroupApi; +import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.rest.ConfiguresHttpApi; import com.google.common.base.Function; @@ -42,8 +48,11 @@ import com.google.inject.TypeLiteral; @ConfiguresHttpApi public class AWSEC2HttpApiModule extends BaseEC2HttpApiModule { + private final SimpleDateFormat iso8601 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); + public AWSEC2HttpApiModule() { super(AWSEC2Api.class); + iso8601.setTimeZone(TimeZone.getTimeZone("GMT")); } @Singleton @@ -72,9 +81,15 @@ public class AWSEC2HttpApiModule extends BaseEC2HttpApiModule { @Override protected void configure() { + bind(FormSigner.class).to(FormSignerV4.class); bind(RunInstancesOptions.class).to(AWSRunInstancesOptions.class); bind(new TypeLiteral>() { }).to(SpotInstanceRequestToAWSRunningInstance.class); super.configure(); } + + @Override protected String provideTimeStamp(DateService dateService) { + // 20120416T155408Z not 2012-04-16T15:54:08Z + return iso8601.format(new Date()); + } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceApiMockTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceApiMockTest.java new file mode 100644 index 0000000000..6782373fde --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceApiMockTest.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.ec2.compute; + +import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.blockUntilRunning; +import static org.testng.Assert.assertEquals; + +import org.jclouds.aws.ec2.internal.BaseAWSEC2ApiMockTest; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.predicates.NodePredicates; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; +import com.squareup.okhttp.mockwebserver.MockResponse; + +@Test(groups = "unit", testName = "AWSEC2ComputeServiceMockTest", singleThreaded = true) +public class AWSEC2ComputeServiceApiMockTest extends BaseAWSEC2ApiMockTest { + + public void launchVPCSpotInstanceSubnetId() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images_cc.xml"); + enqueueXml(DEFAULT_REGION, "/request_spot_instances-ebs.xml"); + enqueueXml(DEFAULT_REGION, "/request_spot_instances-ebs.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images_ebs.xml"); + enqueue(DEFAULT_REGION, new MockResponse()); // create tags + + ComputeService computeService = computeService(); + + Template template = computeService.templateBuilder().locationId("us-east-1a").build(); + + template.getOptions().as(AWSEC2TemplateOptions.class) + .spotPrice(1f).subnetId("subnet-xyz").keyPair("Demo").blockUntilRunning(false); + + NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test", 1, template)); + assertEquals(node.getId(), "us-east-1/sir-228e6406"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=owner-id&Filter.1.Value.1=137112412989&Filter.1.Value.2=801119661308&Filter.1.Value.3=063491364108&Filter.1.Value.4=099720109477&Filter.1.Value.5=411009282317&Filter.2.Name=state&Filter.2.Value.1=available&Filter.3.Name=image-type&Filter.3.Value.1=machine"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=virtualization-type&Filter.1.Value.1=hvm&Filter.2.Name=architecture&Filter.2.Value.1=x86_64&Filter.3.Name=owner-id&Filter.3.Value.1=137112412989&Filter.3.Value.2=099720109477&Filter.4.Name=hypervisor&Filter.4.Value.1=xen&Filter.5.Name=state&Filter.5.Value.1=available&Filter.6.Name=image-type&Filter.6.Value.1=machine&Filter.7.Name=root-device-type&Filter.7.Value.1=ebs"); + assertPosted(DEFAULT_REGION, "Action=RequestSpotInstances&SpotPrice=1.0&InstanceCount=1&LaunchSpecification.ImageId=ami-be3adfd7&LaunchSpecification.Placement.AvailabilityZone=us-east-1a&LaunchSpecification.InstanceType=m1.small&LaunchSpecification.SubnetId=subnet-xyz&LaunchSpecification.KeyName=Demo&LaunchSpecification.UserData=I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotInstanceRequests&SpotInstanceRequestId.1=sir-228e6406"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&ImageId.1=ami-595a0a1c"); + assertPosted(DEFAULT_REGION, "Action=CreateTags&Tag.1.Key=Name&Tag.1.Value=test-228e6406&ResourceId.1=sir-228e6406"); + } + + String iamInstanceProfileArn = "arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"; + + public void launchSpotInstanceIAMInstanceProfileArn() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images_cc.xml"); + enqueueXml(DEFAULT_REGION, "/created_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/authorize_securitygroup_ingress_response.xml"); + enqueueXml(DEFAULT_REGION, "/request_spot_instances-ebs.xml"); + enqueueXml(DEFAULT_REGION, "/request_spot_instances-ebs.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images_ebs.xml"); + enqueue(DEFAULT_REGION, new MockResponse()); // create tags + + ComputeService computeService = computeService(); + + Template template = computeService.templateBuilder().locationId("us-east-1a").build(); + + template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(1f).iamInstanceProfileArn(iamInstanceProfileArn) + .noKeyPair().blockUntilRunning(false); + + NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test", 1, template)); + assertEquals(node.getId(), "us-east-1/sir-228e6406"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=owner-id&Filter.1.Value.1=137112412989&Filter.1.Value.2=801119661308&Filter.1.Value.3=063491364108&Filter.1.Value.4=099720109477&Filter.1.Value.5=411009282317&Filter.2.Name=state&Filter.2.Value.1=available&Filter.3.Name=image-type&Filter.3.Value.1=machine"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=virtualization-type&Filter.1.Value.1=hvm&Filter.2.Name=architecture&Filter.2.Value.1=x86_64&Filter.3.Name=owner-id&Filter.3.Value.1=137112412989&Filter.3.Value.2=099720109477&Filter.4.Name=hypervisor&Filter.4.Value.1=xen&Filter.5.Name=state&Filter.5.Value.1=available&Filter.6.Name=image-type&Filter.6.Value.1=machine&Filter.7.Name=root-device-type&Filter.7.Value.1=ebs"); + assertPosted(DEFAULT_REGION, "Action=CreateSecurityGroup&GroupName=jclouds%23test&GroupDescription=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=AuthorizeSecurityGroupIngress&GroupId=sg-3c6ef654&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=22&IpPermissions.0.ToPort=22&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0/0&IpPermissions.1.IpProtocol=tcp&IpPermissions.1.FromPort=0&IpPermissions.1.ToPort=65535&IpPermissions.1.Groups.0.UserId=993194456877&IpPermissions.1.Groups.0.GroupId=sg-3c6ef654&IpPermissions.2.IpProtocol=udp&IpPermissions.2.FromPort=0&IpPermissions.2.ToPort=65535&IpPermissions.2.Groups.0.UserId=993194456877&IpPermissions.2.Groups.0.GroupId=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=RequestSpotInstances&SpotPrice=1.0&InstanceCount=1&LaunchSpecification.ImageId=ami-be3adfd7&LaunchSpecification.Placement.AvailabilityZone=us-east-1a&LaunchSpecification.SecurityGroup.1=jclouds%23test&LaunchSpecification.InstanceType=m1.small&LaunchSpecification.UserData=I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK&LaunchSpecification.IamInstanceProfile.Arn=arn%3Aaws%3Aiam%3A%3A123456789012%3Ainstance-profile/application_abc/component_xyz/Webserver"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotInstanceRequests&SpotInstanceRequestId.1=sir-228e6406"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&ImageId.1=ami-595a0a1c"); + assertPosted(DEFAULT_REGION, "Action=CreateTags&Tag.1.Key=Name&Tag.1.Value=test-228e6406&ResourceId.1=sir-228e6406"); + } + + public void launchSpotInstanceIAMInstanceProfileName() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images_cc.xml"); + enqueueXml(DEFAULT_REGION, "/created_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/authorize_securitygroup_ingress_response.xml"); + enqueueXml(DEFAULT_REGION, "/request_spot_instances-ebs.xml"); + enqueueXml(DEFAULT_REGION, "/request_spot_instances-ebs.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images_ebs.xml"); + enqueue(DEFAULT_REGION, new MockResponse()); // create tags + + ComputeService computeService = computeService(); + + Template template = computeService.templateBuilder().locationId("us-east-1a").build(); + + template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(1f).iamInstanceProfileName("Webserver") + .noKeyPair().blockUntilRunning(false); + + NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test", 1, template)); + assertEquals(node.getId(), "us-east-1/sir-228e6406"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=owner-id&Filter.1.Value.1=137112412989&Filter.1.Value.2=801119661308&Filter.1.Value.3=063491364108&Filter.1.Value.4=099720109477&Filter.1.Value.5=411009282317&Filter.2.Name=state&Filter.2.Value.1=available&Filter.3.Name=image-type&Filter.3.Value.1=machine"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=virtualization-type&Filter.1.Value.1=hvm&Filter.2.Name=architecture&Filter.2.Value.1=x86_64&Filter.3.Name=owner-id&Filter.3.Value.1=137112412989&Filter.3.Value.2=099720109477&Filter.4.Name=hypervisor&Filter.4.Value.1=xen&Filter.5.Name=state&Filter.5.Value.1=available&Filter.6.Name=image-type&Filter.6.Value.1=machine&Filter.7.Name=root-device-type&Filter.7.Value.1=ebs"); + assertPosted(DEFAULT_REGION, "Action=CreateSecurityGroup&GroupName=jclouds%23test&GroupDescription=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=AuthorizeSecurityGroupIngress&GroupId=sg-3c6ef654&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=22&IpPermissions.0.ToPort=22&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0/0&IpPermissions.1.IpProtocol=tcp&IpPermissions.1.FromPort=0&IpPermissions.1.ToPort=65535&IpPermissions.1.Groups.0.UserId=993194456877&IpPermissions.1.Groups.0.GroupId=sg-3c6ef654&IpPermissions.2.IpProtocol=udp&IpPermissions.2.FromPort=0&IpPermissions.2.ToPort=65535&IpPermissions.2.Groups.0.UserId=993194456877&IpPermissions.2.Groups.0.GroupId=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=RequestSpotInstances&SpotPrice=1.0&InstanceCount=1&LaunchSpecification.ImageId=ami-be3adfd7&LaunchSpecification.Placement.AvailabilityZone=us-east-1a&LaunchSpecification.SecurityGroup.1=jclouds%23test&LaunchSpecification.InstanceType=m1.small&LaunchSpecification.UserData=I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK&LaunchSpecification.IamInstanceProfile.Name=Webserver"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotInstanceRequests&SpotInstanceRequestId.1=sir-228e6406"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&ImageId.1=ami-595a0a1c"); + assertPosted(DEFAULT_REGION, "Action=CreateTags&Tag.1.Key=Name&Tag.1.Value=test-228e6406&ResourceId.1=sir-228e6406"); + } + + public void createNodeWithIAMInstanceProfileArn() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/amzn_images.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images_cc.xml"); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + enqueueXml(DEFAULT_REGION, "/created_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/authorize_securitygroup_ingress_response.xml"); + enqueueXml(DEFAULT_REGION, "/new_instance.xml"); + enqueueXml(DEFAULT_REGION, "/describe_instances_running-1.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images.xml"); + enqueue(DEFAULT_REGION, new MockResponse()); // create tags + + ComputeService computeService = computeService(); + + NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test", 1, + blockUntilRunning(false).iamInstanceProfileArn(iamInstanceProfileArn).noKeyPair())); + assertEquals(node.getId(), "us-east-1/i-2baa5550"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=owner-id&Filter.1.Value.1=137112412989&Filter.1.Value.2=801119661308&Filter.1.Value.3=063491364108&Filter.1.Value.4=099720109477&Filter.1.Value.5=411009282317&Filter.2.Name=state&Filter.2.Value.1=available&Filter.3.Name=image-type&Filter.3.Value.1=machine"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=virtualization-type&Filter.1.Value.1=hvm&Filter.2.Name=architecture&Filter.2.Value.1=x86_64&Filter.3.Name=owner-id&Filter.3.Value.1=137112412989&Filter.3.Value.2=099720109477&Filter.4.Name=hypervisor&Filter.4.Value.1=xen&Filter.5.Name=state&Filter.5.Value.1=available&Filter.6.Name=image-type&Filter.6.Value.1=machine&Filter.7.Name=root-device-type&Filter.7.Value.1=ebs"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + assertPosted(DEFAULT_REGION, "Action=CreateSecurityGroup&GroupName=jclouds%23test&GroupDescription=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=AuthorizeSecurityGroupIngress&GroupId=sg-3c6ef654&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=22&IpPermissions.0.ToPort=22&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0/0&IpPermissions.1.IpProtocol=tcp&IpPermissions.1.FromPort=0&IpPermissions.1.ToPort=65535&IpPermissions.1.Groups.0.UserId=993194456877&IpPermissions.1.Groups.0.GroupId=sg-3c6ef654&IpPermissions.2.IpProtocol=udp&IpPermissions.2.FromPort=0&IpPermissions.2.ToPort=65535&IpPermissions.2.Groups.0.UserId=993194456877&IpPermissions.2.Groups.0.GroupId=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=RunInstances&ImageId=ami-8ce4b5c9&MinCount=1&MaxCount=1&InstanceType=t1.micro&SecurityGroup.1=jclouds%23test&UserData=I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK&IamInstanceProfile.Arn=arn%3Aaws%3Aiam%3A%3A123456789012%3Ainstance-profile/application_abc/component_xyz/Webserver"); + assertPosted(DEFAULT_REGION, "Action=DescribeInstances&InstanceId.1=i-2baa5550"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&ImageId.1=ami-aecd60c7"); + assertPosted(DEFAULT_REGION, "Action=CreateTags&Tag.1.Key=Name&Tag.1.Value=test-2baa5550&ResourceId.1=i-2baa5550"); + } + + public void createNodeWithIAMInstanceProfileName() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/amzn_images.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images_cc.xml"); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + enqueueXml(DEFAULT_REGION, "/created_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/new_securitygroup.xml"); + enqueueXml(DEFAULT_REGION, "/authorize_securitygroup_ingress_response.xml"); + enqueueXml(DEFAULT_REGION, "/new_instance.xml"); + enqueueXml(DEFAULT_REGION, "/describe_instances_running-1.xml"); + enqueueXml(DEFAULT_REGION, "/describe_images.xml"); + enqueue(DEFAULT_REGION, new MockResponse()); // create tags + + ComputeService computeService = computeService(); + + NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test", 1, + blockUntilRunning(false).iamInstanceProfileName("Webserver").noKeyPair())); + assertEquals(node.getId(), "us-east-1/i-2baa5550"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=owner-id&Filter.1.Value.1=137112412989&Filter.1.Value.2=801119661308&Filter.1.Value.3=063491364108&Filter.1.Value.4=099720109477&Filter.1.Value.5=411009282317&Filter.2.Name=state&Filter.2.Value.1=available&Filter.3.Name=image-type&Filter.3.Value.1=machine"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&Filter.1.Name=virtualization-type&Filter.1.Value.1=hvm&Filter.2.Name=architecture&Filter.2.Value.1=x86_64&Filter.3.Name=owner-id&Filter.3.Value.1=137112412989&Filter.3.Value.2=099720109477&Filter.4.Name=hypervisor&Filter.4.Value.1=xen&Filter.5.Name=state&Filter.5.Value.1=available&Filter.6.Name=image-type&Filter.6.Value.1=machine&Filter.7.Name=root-device-type&Filter.7.Value.1=ebs"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + assertPosted(DEFAULT_REGION, "Action=CreateSecurityGroup&GroupName=jclouds%23test&GroupDescription=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23test"); + assertPosted(DEFAULT_REGION, "Action=AuthorizeSecurityGroupIngress&GroupId=sg-3c6ef654&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=22&IpPermissions.0.ToPort=22&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0/0&IpPermissions.1.IpProtocol=tcp&IpPermissions.1.FromPort=0&IpPermissions.1.ToPort=65535&IpPermissions.1.Groups.0.UserId=993194456877&IpPermissions.1.Groups.0.GroupId=sg-3c6ef654&IpPermissions.2.IpProtocol=udp&IpPermissions.2.FromPort=0&IpPermissions.2.ToPort=65535&IpPermissions.2.Groups.0.UserId=993194456877&IpPermissions.2.Groups.0.GroupId=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=RunInstances&ImageId=ami-8ce4b5c9&MinCount=1&MaxCount=1&InstanceType=t1.micro&SecurityGroup.1=jclouds%23test&UserData=I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK&IamInstanceProfile.Name=Webserver"); + assertPosted(DEFAULT_REGION, "Action=DescribeInstances&InstanceId.1=i-2baa5550"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&ImageId.1=ami-aecd60c7"); + assertPosted(DEFAULT_REGION, "Action=CreateTags&Tag.1.Key=Name&Tag.1.Value=test-2baa5550&ResourceId.1=i-2baa5550"); + } + + public void listNodesWhereImageDoesntExist() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/describe_instances_running-1.xml"); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + enqueue(DEFAULT_REGION, new MockResponse().setBody("")); + enqueue(DEFAULT_REGION, new MockResponse().setBody("")); + + ComputeService computeService = computeService(); + + NodeMetadata node = Iterables.getOnlyElement(computeService.listNodesDetailsMatching(NodePredicates.all())); + assertEquals(node.getId(), "us-east-1/i-2baa5550"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeInstances"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&ImageId.1=ami-aecd60c7"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotInstanceRequests"); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceExpectTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceExpectTest.java deleted file mode 100644 index da2f898995..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceExpectTest.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.compute; - -import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.blockUntilRunning; -import static org.testng.Assert.assertEquals; - -import javax.ws.rs.core.MediaType; - -import org.jclouds.aws.ec2.compute.internal.BaseAWSEC2ComputeServiceExpectTest; -import org.jclouds.compute.ComputeService; -import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.domain.Template; -import org.jclouds.compute.predicates.NodePredicates; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpResponse; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; -import com.google.common.collect.Iterables; - -/** - * Tests the compute service abstraction of the EC2 api. - */ -@Test(groups = "unit", testName = "AWSEC2ComputeServiceExpectTest") -public class AWSEC2ComputeServiceExpectTest extends BaseAWSEC2ComputeServiceExpectTest { - - private HttpResponse requestSpotInstancesResponse; - private HttpRequest describeSpotInstanceRequest; - private HttpResponse describeSpotInstanceResponse; - - @BeforeClass - @Override - protected void setupDefaultRequests() { - super.setupDefaultRequests(); - requestSpotInstancesResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/request_spot_instances-ebs.xml", MediaType.APPLICATION_XML)).build(); - - describeSpotInstanceRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSpotInstanceRequests") - .addFormParam("SpotInstanceRequestId.1", "sir-228e6406").build()); - - describeSpotInstanceResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/request_spot_instances-ebs.xml", MediaType.APPLICATION_XML)).build(); - } - - public void testLaunchVPCSpotInstanceSubnetId() throws Exception { - HttpRequest requestSpotInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "RequestSpotInstances") - .addFormParam("InstanceCount", "1") - .addFormParam("LaunchSpecification.ImageId", "ami-be3adfd7") - .addFormParam("LaunchSpecification.InstanceType", "m1.small") - .addFormParam("LaunchSpecification.KeyName", "Demo") - .addFormParam("LaunchSpecification.Placement.AvailabilityZone", "us-east-1a") - .addFormParam("LaunchSpecification.SubnetId", "subnet-xyz") - .addFormParam("LaunchSpecification.UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK") - .addFormParam("SpotPrice", "1.0").build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeImagesRequest, describeImagesResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - requestResponseMap.put(describeSecurityGroupRequest, describeSecurityGroupResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(requestSpotInstancesRequest, requestSpotInstancesResponse); - requestResponseMap.put(describeSpotInstanceRequest, describeSpotInstanceResponse); - - ComputeService createsVPCSpotInstance = requestsSendResponses(requestResponseMap.build()); - - Template template = createsVPCSpotInstance.templateBuilder().locationId("us-east-1a").build(); - - template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(1f).subnetId("subnet-xyz").keyPair("Demo").blockUntilRunning(false); - - NodeMetadata node = Iterables.getOnlyElement(createsVPCSpotInstance.createNodesInGroup("test", 1, template)); - assertEquals(node.getId(), "us-east-1/sir-228e6406"); - } - - String iamInstanceProfileArn = "arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver"; - - public void testLaunchSpotInstanceIAMInstanceProfileArn() throws Exception { - HttpRequest requestSpotInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "RequestSpotInstances") - .addFormParam("InstanceCount", "1") - .addFormParam("LaunchSpecification.IamInstanceProfile.Arn", iamInstanceProfileArn) - .addFormParam("LaunchSpecification.ImageId", "ami-be3adfd7") - .addFormParam("LaunchSpecification.InstanceType", "m1.small") - .addFormParam("LaunchSpecification.Placement.AvailabilityZone", "us-east-1a") - .addFormParam("LaunchSpecification.SecurityGroup.1", "jclouds#test") - .addFormParam("LaunchSpecification.UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK") - .addFormParam("SpotPrice", "1.0").build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeImagesRequest, describeImagesResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - requestResponseMap.put(describeSecurityGroupRequest, describeSecurityGroupResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(requestSpotInstancesRequest, requestSpotInstancesResponse); - requestResponseMap.put(describeSpotInstanceRequest, describeSpotInstanceResponse); - - ComputeService createsSpotInstance = requestsSendResponses(requestResponseMap.build()); - - Template template = createsSpotInstance.templateBuilder().locationId("us-east-1a").build(); - - template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(1f).iamInstanceProfileArn(iamInstanceProfileArn) - .noKeyPair().blockUntilRunning(false); - - NodeMetadata node = Iterables.getOnlyElement(createsSpotInstance.createNodesInGroup("test", 1, template)); - assertEquals(node.getId(), "us-east-1/sir-228e6406"); - } - - public void testLaunchSpotInstanceIAMInstanceProfileName() throws Exception { - HttpRequest requestSpotInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "RequestSpotInstances") - .addFormParam("InstanceCount", "1") - .addFormParam("LaunchSpecification.IamInstanceProfile.Name", "Webserver") - .addFormParam("LaunchSpecification.ImageId", "ami-be3adfd7") - .addFormParam("LaunchSpecification.InstanceType", "m1.small") - .addFormParam("LaunchSpecification.Placement.AvailabilityZone", "us-east-1a") - .addFormParam("LaunchSpecification.SecurityGroup.1", "jclouds#test") - .addFormParam("LaunchSpecification.UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK") - .addFormParam("SpotPrice", "1.0").build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeImagesRequest, describeImagesResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - requestResponseMap.put(describeSecurityGroupRequest, describeSecurityGroupResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(requestSpotInstancesRequest, requestSpotInstancesResponse); - requestResponseMap.put(describeSpotInstanceRequest, describeSpotInstanceResponse); - - ComputeService createsSpotInstance = requestsSendResponses(requestResponseMap.build()); - - Template template = createsSpotInstance.templateBuilder().locationId("us-east-1a").build(); - - template.getOptions().as(AWSEC2TemplateOptions.class).spotPrice(1f).iamInstanceProfileName("Webserver") - .noKeyPair().blockUntilRunning(false); - - NodeMetadata node = Iterables.getOnlyElement(createsSpotInstance.createNodesInGroup("test", 1, template)); - assertEquals(node.getId(), "us-east-1/sir-228e6406"); - } - - public void testCreateNodeWithIAMInstanceProfileArn() throws Exception { - HttpRequest runInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "RunInstances") - .addFormParam("IamInstanceProfile.Arn", iamInstanceProfileArn) - .addFormParam("ImageId", "ami-be3adfd7") - .addFormParam("InstanceType", "m1.small") - .addFormParam("MaxCount", "1") - .addFormParam("MinCount", "1") - .addFormParam("SecurityGroup.1", "jclouds#test") - .addFormParam("UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK").build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeImagesRequest, describeImagesResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - requestResponseMap.put(describeSecurityGroupRequest, describeSecurityGroupResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(runInstancesRequest, runInstancesResponse); - requestResponseMap.put(describeInstanceRequest, describeInstanceResponse); - requestResponseMap.put(describeImageRequest, describeImagesResponse); - - ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build()); - - NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1, - blockUntilRunning(false).iamInstanceProfileArn(iamInstanceProfileArn).noKeyPair())); - assertEquals(node.getId(), "us-east-1/i-2baa5550"); - } - - public void testCreateNodeWithIAMInstanceProfileName() throws Exception { - HttpRequest runInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "RunInstances") - .addFormParam("IamInstanceProfile.Name", "Webserver") - .addFormParam("ImageId", "ami-be3adfd7") - .addFormParam("InstanceType", "m1.small") - .addFormParam("MaxCount", "1") - .addFormParam("MinCount", "1") - .addFormParam("SecurityGroup.1", "jclouds#test") - .addFormParam("UserData", "I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK").build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeImagesRequest, describeImagesResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - requestResponseMap.put(describeSecurityGroupRequest, describeSecurityGroupResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(runInstancesRequest, runInstancesResponse); - requestResponseMap.put(describeInstanceRequest, describeInstanceResponse); - requestResponseMap.put(describeImageRequest, describeImagesResponse); - - ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build()); - - NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1, - blockUntilRunning(false).iamInstanceProfileName("Webserver").noKeyPair())); - assertEquals(node.getId(), "us-east-1/i-2baa5550"); - } - - public void testListNodesWhereImageDoesntExist() throws Exception { - HttpRequest describeInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeInstances").build()); - - HttpRequest describeSpotInstancesRequest = formSigner.filter(HttpRequest.builder().method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSpotInstanceRequests").build()); - - HttpResponse noSpotInstancesResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromStringWithContentType( - "", - MediaType.APPLICATION_XML)).build(); - - HttpResponse noImagesResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromStringWithContentType( - "", - MediaType.APPLICATION_XML)).build(); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeImagesRequest, noImagesResponse); - requestResponseMap.put(describeInstancesRequest, describeInstanceResponse); - requestResponseMap.put(describeSpotInstancesRequest, noSpotInstancesResponse); - - ComputeService listsWithoutImages = requestsSendResponses(requestResponseMap.build()); - - NodeMetadata node = Iterables.getOnlyElement(listsWithoutImages.listNodesDetailsMatching(NodePredicates.all())); - assertEquals(node.getId(), "us-east-1/i-2baa5550"); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java index 65cbd60fbf..050b9aae58 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java @@ -29,6 +29,7 @@ import java.util.Set; import org.jclouds.aws.domain.Region; import org.jclouds.aws.ec2.AWSEC2Api; +import org.jclouds.aws.ec2.AWSEC2ProviderMetadata; import org.jclouds.aws.ec2.reference.AWSEC2Constants; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.OsFamily; @@ -46,6 +47,7 @@ import org.jclouds.http.HttpCommand; import org.jclouds.http.internal.TrackingJavaUrlHttpCommandExecutorService; import org.jclouds.location.reference.LocationConstants; import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.providers.ProviderMetadata; import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; @@ -59,6 +61,10 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { provider = "aws-ec2"; } + @Override public ProviderMetadata createProviderMetadata() { + return new AWSEC2ProviderMetadata(); + } + @Test public void testTemplateBuilderM1MEDIUMWithNegativeLookaroundDoesntMatchTestImages() { diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionApiMockTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionApiMockTest.java new file mode 100644 index 0000000000..1bee5d55c6 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionApiMockTest.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.ec2.compute.extensions; + +import static org.jclouds.domain.LocationScope.REGION; +import static org.testng.Assert.assertEquals; + +import org.jclouds.aws.ec2.internal.BaseAWSEC2ApiMockTest; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.SecurityGroupBuilder; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; + +@Test(groups = "unit", testName = "AWSEC2SecurityGroupExtensionMockTest", singleThreaded = true) +public class AWSEC2SecurityGroupExtensionApiMockTest extends BaseAWSEC2ApiMockTest { + + SecurityGroup group = new SecurityGroupBuilder() // + .id(DEFAULT_REGION + "/sg-3c6ef654") // + .providerId("sg-3c6ef654") // + .name("jclouds#some-group") // + .ownerId("993194456877") + .location(new LocationBuilder().scope(REGION).id(DEFAULT_REGION).description("").build()).build(); + + IpPermission permByCidrBlock = IpPermission.builder() // + .ipProtocol(IpProtocol.TCP) // + .fromPort(22) // + .toPort(40) // + .cidrBlock("0.0.0.0/0").build(); + + public void addIpPermissionCidrFromIpPermission() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/authorize_securitygroup_ingress_response.xml"); + enqueueXml(DEFAULT_REGION, "/describe_securitygroups_extension_cidr.xml"); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + + SecurityGroup newGroup = extension().addIpPermission(permByCidrBlock, group); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + assertEquals(newPerm, permByCidrBlock); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, + "Action=AuthorizeSecurityGroupIngress&GroupId=sg-3c6ef654&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=22&IpPermissions.0.ToPort=40&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0/0"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupId.1=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + } + + public void addIpPermissionCidrFromParams() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/authorize_securitygroup_ingress_response.xml"); + enqueueXml(DEFAULT_REGION, "/describe_securitygroups_extension_cidr.xml"); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + + SecurityGroup newGroup = extension() + .addIpPermission(permByCidrBlock.getIpProtocol(), permByCidrBlock.getFromPort(), + permByCidrBlock.getToPort(), permByCidrBlock.getTenantIdGroupNamePairs(), + permByCidrBlock.getCidrBlocks(), permByCidrBlock.getGroupIds(), group); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + assertEquals(newPerm, permByCidrBlock); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, + "Action=AuthorizeSecurityGroupIngress&GroupId=sg-3c6ef654&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=22&IpPermissions.0.ToPort=40&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0/0"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupId.1=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + } + + IpPermission permByGroup = IpPermission.builder() // + .ipProtocol(IpProtocol.TCP) // + .fromPort(22) // + .toPort(40) // + .tenantIdGroupNamePair(group.getOwnerId(), group.getProviderId()).build(); + + public void addIpPermissionGroupFromIpPermission() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/authorize_securitygroup_ingress_response.xml"); + enqueueXml(DEFAULT_REGION, "/describe_securitygroups_extension_group.xml"); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + + SecurityGroup newGroup = extension().addIpPermission(permByGroup, group); + + assertEquals(1, newGroup.getIpPermissions().size()); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + assertEquals(newPerm, permByGroup); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, + "Action=AuthorizeSecurityGroupIngress&GroupId=sg-3c6ef654&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=22&IpPermissions.0.ToPort=40&IpPermissions.0.Groups.0.UserId=993194456877&IpPermissions.0.Groups.0.GroupId=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupId.1=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + } + + public void addIpPermissionGroupFromParams() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/authorize_securitygroup_ingress_response.xml"); + enqueueXml(DEFAULT_REGION, "/describe_securitygroups_extension_group.xml"); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + + SecurityGroup newGroup = extension() + .addIpPermission(permByGroup.getIpProtocol(), permByGroup.getFromPort(), permByGroup.getToPort(), + permByGroup.getTenantIdGroupNamePairs(), permByGroup.getCidrBlocks(), permByGroup.getGroupIds(), + group); + + IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); + assertEquals(newPerm, permByGroup); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, + "Action=AuthorizeSecurityGroupIngress&GroupId=sg-3c6ef654&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=22&IpPermissions.0.ToPort=40&IpPermissions.0.Groups.0.UserId=993194456877&IpPermissions.0.Groups.0.GroupId=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupId.1=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + } + + public void createSecurityGroup() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/created_securitygroup.xml"); + // TODO: ridiculously chatty + enqueueXml(DEFAULT_REGION, "/describe_securitygroups_extension_single.xml"); + enqueueXml(DEFAULT_REGION, "/describe_securitygroups_extension_single.xml"); + enqueueXml(DEFAULT_REGION, "/describe_securitygroups_extension_single.xml"); + enqueueXml(DEFAULT_REGION, "/describe_securitygroups_extension_single.xml"); + enqueueXml(DEFAULT_REGION, "/availabilityZones.xml"); + + SecurityGroup newGroup = extension() + .createSecurityGroup(group.getName().replace("jclouds#", ""), group.getLocation()); + assertEquals(newGroup.getId(), group.getId()); + assertEquals(newGroup.getProviderId(), group.getProviderId()); + assertEquals(newGroup.getName(), group.getName()); + assertEquals(newGroup.getLocation().getId(), group.getLocation().getId()); // One from response has a parent + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, + "Action=CreateSecurityGroup&GroupName=jclouds%23some-group&GroupDescription=jclouds%23some-group"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23some-group"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23some-group"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupName.1=jclouds%23some-group"); + assertPosted(DEFAULT_REGION, "Action=DescribeSecurityGroups&GroupId.1=sg-3c6ef654"); + assertPosted(DEFAULT_REGION, "Action=DescribeAvailabilityZones"); + } + + private SecurityGroupExtension extension() { + return computeService().getSecurityGroupExtension().get(); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionExpectTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionExpectTest.java deleted file mode 100644 index 3e597940a1..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2SecurityGroupExtensionExpectTest.java +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.compute.extensions; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; - -import javax.ws.rs.core.MediaType; -import java.util.Set; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; -import org.jclouds.aws.ec2.compute.internal.BaseAWSEC2ComputeServiceExpectTest; -import org.jclouds.compute.domain.SecurityGroup; -import org.jclouds.compute.domain.SecurityGroupBuilder; -import org.jclouds.compute.extensions.SecurityGroupExtension; -import org.jclouds.domain.LocationBuilder; -import org.jclouds.domain.LocationScope; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpResponse; -import org.jclouds.net.domain.IpPermission; -import org.jclouds.net.domain.IpProtocol; -import org.testng.annotations.Test; - -@Test(groups = "unit", testName = "AWSEC2SecurityGroupExtensionExpectTest") -public class AWSEC2SecurityGroupExtensionExpectTest extends BaseAWSEC2ComputeServiceExpectTest { - - public void testAddIpPermissionCidrFromIpPermission() { - HttpRequest describeSecurityGroupsSingleRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSecurityGroups") - .addFormParam("GroupId.1", "sg-3c6ef654").build()); - - HttpResponse describeSecurityGroupsSingleResponse = - HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build(); - - - HttpRequest authorizeSecurityGroupIngressRequestRange = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "AuthorizeSecurityGroupIngress") - .addFormParam("GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.0.FromPort", "22") - .addFormParam("IpPermissions.0.IpProtocol", "tcp") - .addFormParam("IpPermissions.0.IpRanges.0.CidrIp", "0.0.0.0/0") - .addFormParam("IpPermissions.0.ToPort", "40") - .build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - - requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse); - - IpPermission.Builder builder = IpPermission.builder(); - - builder.ipProtocol(IpProtocol.TCP); - builder.fromPort(22); - builder.toPort(40); - builder.cidrBlock("0.0.0.0/0"); - - IpPermission perm = builder.build(); - - SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); - - SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); - groupBuilder.id("us-east-1/sg-3c6ef654"); - groupBuilder.providerId("sg-3c6ef654"); - groupBuilder.name("jclouds#some-group"); - groupBuilder.location(new LocationBuilder() - .scope(LocationScope.REGION) - .id(region) - .description("region") - .build()); - - SecurityGroup origGroup = groupBuilder.build(); - - SecurityGroup newGroup = extension.addIpPermission(perm, origGroup); - - assertEquals(1, newGroup.getIpPermissions().size()); - - IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); - - assertNotNull(newPerm); - assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); - assertEquals(22, newPerm.getFromPort()); - assertEquals(40, newPerm.getToPort()); - assertEquals(1, newPerm.getCidrBlocks().size()); - assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0")); - } - - public void testAddIpPermissionCidrFromParams() { - HttpRequest describeSecurityGroupsSingleRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSecurityGroups") - .addFormParam("GroupId.1", "sg-3c6ef654").build()); - - HttpResponse describeSecurityGroupsSingleResponse = - HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/describe_securitygroups_extension_cidr.xml", MediaType.APPLICATION_XML)).build(); - - - HttpRequest authorizeSecurityGroupIngressRequestRange = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "AuthorizeSecurityGroupIngress") - .addFormParam("GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.0.FromPort", "22") - .addFormParam("IpPermissions.0.IpProtocol", "tcp") - .addFormParam("IpPermissions.0.IpRanges.0.CidrIp", "0.0.0.0/0") - .addFormParam("IpPermissions.0.ToPort", "40") - .build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - - requestResponseMap.put(authorizeSecurityGroupIngressRequestRange, authorizeSecurityGroupIngressResponse); - - SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); - - SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); - groupBuilder.id("us-east-1/sg-3c6ef654"); - groupBuilder.providerId("sg-3c6ef654"); - groupBuilder.name("jclouds#some-group"); - groupBuilder.location(new LocationBuilder() - .scope(LocationScope.REGION) - .id(region) - .description("region") - .build()); - - SecurityGroup origGroup = groupBuilder.build(); - - SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP, - 22, - 40, - emptyMultimap(), - ImmutableSet.of("0.0.0.0/0"), - emptyStringSet(), - origGroup); - - assertEquals(1, newGroup.getIpPermissions().size()); - - IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); - - assertNotNull(newPerm); - assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); - assertEquals(22, newPerm.getFromPort()); - assertEquals(40, newPerm.getToPort()); - assertEquals(1, newPerm.getCidrBlocks().size()); - assertTrue(newPerm.getCidrBlocks().contains("0.0.0.0/0")); - } - - public void testAddIpPermissionGroupFromIpPermission() { - HttpRequest describeSecurityGroupsSingleRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSecurityGroups") - .addFormParam("GroupId.1", "sg-3c6ef654").build()); - - HttpResponse describeSecurityGroupsSingleResponse = - HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build(); - - - HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "AuthorizeSecurityGroupIngress") - .addFormParam("GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.0.FromPort", "22") - .addFormParam("IpPermissions.0.Groups.0.GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.0.Groups.0.UserId", "993194456877") - .addFormParam("IpPermissions.0.IpProtocol", "tcp") - .addFormParam("IpPermissions.0.ToPort", "40") - .build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - - requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse); - - IpPermission.Builder builder = IpPermission.builder(); - - builder.ipProtocol(IpProtocol.TCP); - builder.fromPort(22); - builder.toPort(40); - builder.tenantIdGroupNamePair("993194456877", "sg-3c6ef654"); - - IpPermission perm = builder.build(); - - SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); - - SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); - groupBuilder.id("us-east-1/sg-3c6ef654"); - groupBuilder.providerId("sg-3c6ef654"); - groupBuilder.name("jclouds#some-group"); - groupBuilder.location(new LocationBuilder() - .scope(LocationScope.REGION) - .id(region) - .description("region") - .build()); - groupBuilder.ownerId("993194456877"); - - SecurityGroup origGroup = groupBuilder.build(); - - SecurityGroup newGroup = extension.addIpPermission(perm, origGroup); - - assertEquals(1, newGroup.getIpPermissions().size()); - - IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); - - assertNotNull(newPerm); - assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); - assertEquals(22, newPerm.getFromPort()); - assertEquals(40, newPerm.getToPort()); - assertEquals(0, newPerm.getCidrBlocks().size()); - assertEquals(1, newPerm.getTenantIdGroupNamePairs().size()); - assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId())); - assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getProviderId())); - } - - - public void testAddIpPermissionGroupFromParams() { - HttpRequest describeSecurityGroupsSingleRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSecurityGroups") - .addFormParam("GroupId.1", "sg-3c6ef654").build()); - - HttpResponse describeSecurityGroupsSingleResponse = - HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/describe_securitygroups_extension_group.xml", MediaType.APPLICATION_XML)).build(); - - - HttpRequest authorizeSecurityGroupIngressRequestGroupTenant = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "AuthorizeSecurityGroupIngress") - .addFormParam("GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.0.FromPort", "22") - .addFormParam("IpPermissions.0.Groups.0.GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.0.Groups.0.UserId", "993194456877") - .addFormParam("IpPermissions.0.IpProtocol", "tcp") - .addFormParam("IpPermissions.0.ToPort", "40") - .build()); - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse); - - requestResponseMap.put(authorizeSecurityGroupIngressRequestGroupTenant, authorizeSecurityGroupIngressResponse); - - SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); - - SecurityGroupBuilder groupBuilder = new SecurityGroupBuilder(); - groupBuilder.id("us-east-1/sg-3c6ef654"); - groupBuilder.providerId("sg-3c6ef654"); - groupBuilder.name("jclouds#some-group"); - groupBuilder.ownerId("993194456877"); - groupBuilder.location(new LocationBuilder() - .scope(LocationScope.REGION) - .id(region) - .description("region") - .build()); - - SecurityGroup origGroup = groupBuilder.build(); - - ImmutableMultimap.Builder permBuilder = ImmutableMultimap.builder(); - permBuilder.put(origGroup.getOwnerId(), origGroup.getId()); - - SecurityGroup newGroup = extension.addIpPermission(IpProtocol.TCP, - 22, - 40, - permBuilder.build(), - emptyStringSet(), - emptyStringSet(), - origGroup); - - assertEquals(1, newGroup.getIpPermissions().size()); - - IpPermission newPerm = Iterables.getOnlyElement(newGroup.getIpPermissions()); - - assertNotNull(newPerm); - assertEquals(IpProtocol.TCP, newPerm.getIpProtocol()); - assertEquals(22, newPerm.getFromPort()); - assertEquals(40, newPerm.getToPort()); - assertEquals(0, newPerm.getCidrBlocks().size()); - assertEquals(1, newPerm.getTenantIdGroupNamePairs().size()); - assertTrue(newPerm.getTenantIdGroupNamePairs().keySet().contains(origGroup.getOwnerId())); - assertTrue(newPerm.getTenantIdGroupNamePairs().values().contains(origGroup.getProviderId())); - } - - public void testCreateSecurityGroup() { - HttpRequest createSecurityGroupExtRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "CreateSecurityGroup") - .addFormParam("GroupDescription", "jclouds#some-group") - .addFormParam("GroupName", "jclouds#some-group").build()); - - HttpRequest describeSecurityGroupsSingleRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSecurityGroups") - .addFormParam("GroupName.1", "jclouds#some-group").build()); - - HttpRequest describeSecurityGroupsByIdRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSecurityGroups") - .addFormParam("GroupId.1", "sg-3c6ef654").build()); - - HttpResponse describeSecurityGroupsSingleResponse = - HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType( - "/describe_securitygroups_extension_single.xml", MediaType.APPLICATION_XML)).build(); - - - Builder requestResponseMap = ImmutableMap. builder(); - requestResponseMap.put(describeRegionsRequest, describeRegionsResponse); - requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse); - requestResponseMap.put(describeSecurityGroupsSingleRequest, describeSecurityGroupsSingleResponse); - requestResponseMap.put(describeSecurityGroupsByIdRequest, describeSecurityGroupsSingleResponse); - requestResponseMap.put(createKeyPairRequest, createKeyPairResponse); - requestResponseMap.put(createSecurityGroupExtRequest, createSecurityGroupResponse); - - requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse); - requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse); - - - SecurityGroupExtension extension = requestsSendResponses(requestResponseMap.build()).getSecurityGroupExtension().get(); - - SecurityGroup group = extension.createSecurityGroup("some-group", new LocationBuilder() - .scope(LocationScope.REGION) - .id(region) - .description("region") - .build()); - - assertEquals("sg-3c6ef654", group.getProviderId()); - assertEquals(region + "/sg-3c6ef654", group.getId()); - } - - private Multimap emptyMultimap() { - return LinkedHashMultimap.create(); - } - - private Set emptyStringSet() { - return Sets.newLinkedHashSet(); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/internal/BaseAWSEC2ComputeServiceExpectTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/internal/BaseAWSEC2ComputeServiceExpectTest.java deleted file mode 100644 index 376b169ba4..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/internal/BaseAWSEC2ComputeServiceExpectTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.compute.internal; - -import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY; -import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_GENERATE_INSTANCE_NAMES; - -import java.util.Properties; -import java.util.concurrent.atomic.AtomicInteger; - -import org.jclouds.aws.ec2.config.AWSEC2HttpApiModule; -import org.jclouds.date.DateService; -import org.jclouds.ec2.compute.internal.BaseEC2ComputeServiceExpectTest; -import org.jclouds.http.HttpRequest; -import org.jclouds.rest.ConfiguresHttpApi; -import org.testng.annotations.BeforeClass; - -import com.google.common.base.Supplier; -import com.google.inject.Module; -import com.google.inject.Provides; -import com.google.inject.TypeLiteral; - -/** - * Tests the compute service abstraction of the EC2 api. - */ -public abstract class BaseAWSEC2ComputeServiceExpectTest extends BaseEC2ComputeServiceExpectTest { - - protected HttpRequest describeSecurityGroupByIdRequest; - - public BaseAWSEC2ComputeServiceExpectTest() { - provider = "aws-ec2"; - } - - @Override - protected Properties setupProperties() { - Properties properties = super.setupProperties(); - // zero out cluster image query for now - properties.setProperty(PROPERTY_EC2_CC_AMI_QUERY, ""); - properties.setProperty(PROPERTY_EC2_GENERATE_INSTANCE_NAMES, "false"); - return properties; - } - - @BeforeClass - @Override - protected void setupDefaultRequests() { - super.setupDefaultRequests(); - - describeSecurityGroupByIdRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeSecurityGroups") - .addFormParam("GroupId.1", "sg-3c6ef654").build()); - - authorizeSecurityGroupIngressRequestGroup = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "AuthorizeSecurityGroupIngress") - .addFormParam("SourceSecurityGroupId", "sg-3c6ef654") - .addFormParam("SourceSecurityGroupOwnerId", "993194456877") - .addFormParam("GroupName", "jclouds#test").build()); - - authorizeSecurityGroupIngressRequest22 = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "AuthorizeSecurityGroupIngress") - .addFormParam("GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.0.FromPort", "22") - .addFormParam("IpPermissions.0.ToPort", "22") - .addFormParam("IpPermissions.0.IpRanges.0.CidrIp", "0.0.0.0/0") - .addFormParam("IpPermissions.0.IpProtocol", "tcp") - .addFormParam("IpPermissions.1.FromPort", "0") - .addFormParam("IpPermissions.1.ToPort", "65535") - .addFormParam("IpPermissions.1.Groups.0.GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.1.Groups.0.UserId", "993194456877") - .addFormParam("IpPermissions.1.IpProtocol", "tcp") - .addFormParam("IpPermissions.2.FromPort", "0") - .addFormParam("IpPermissions.2.ToPort", "65535") - .addFormParam("IpPermissions.2.Groups.0.GroupId", "sg-3c6ef654") - .addFormParam("IpPermissions.2.Groups.0.UserId", "993194456877") - .addFormParam("IpPermissions.2.IpProtocol", "udp") - .build()); - - describeImagesRequest = - formSigner.filter(HttpRequest.builder() - .method("POST") - .endpoint("https://ec2." + region + ".amazonaws.com/") - .addHeader("Host", "ec2." + region + ".amazonaws.com") - .addFormParam("Action", "DescribeImages") - .addFormParam("Filter.1.Name", "owner-id") - .addFormParam("Filter.1.Value.1", "137112412989") - .addFormParam("Filter.1.Value.2", "801119661308") - .addFormParam("Filter.1.Value.3", "063491364108") - .addFormParam("Filter.1.Value.4", "099720109477") - .addFormParam("Filter.1.Value.5", "411009282317") - .addFormParam("Filter.2.Name", "state") - .addFormParam("Filter.2.Value.1", "available") - .addFormParam("Filter.3.Name", "image-type") - .addFormParam("Filter.3.Value.1", "machine").build()); - } - - @ConfiguresHttpApi - protected static class TestAWSEC2HttpApiModule extends AWSEC2HttpApiModule { - - @Override - protected void configure() { - super.configure(); - // predicatable node names - final AtomicInteger suffix = new AtomicInteger(); - bind(new TypeLiteral>() { - }).toInstance(new Supplier() { - - @Override - public String get() { - return suffix.getAndIncrement() + ""; - } - - }); - } - - @Override - @Provides - protected String provideTimeStamp(DateService dateService) { - return CONSTANT_DATE; - } - } - - @Override - protected Module createModule() { - return new TestAWSEC2HttpApiModule(); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiMockTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiMockTest.java new file mode 100644 index 0000000000..283ea2d1bf --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiMockTest.java @@ -0,0 +1,254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.ec2.features; + +import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.executableBy; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Map; +import java.util.Set; + +import org.jclouds.aws.ec2.internal.BaseAWSEC2ApiMockTest; +import org.jclouds.ec2.domain.Image; +import org.jclouds.ec2.domain.Permission; +import org.jclouds.ec2.options.CreateImageOptions; +import org.jclouds.ec2.options.RegisterImageBackedByEbsOptions; +import org.jclouds.ec2.options.RegisterImageOptions; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.squareup.okhttp.mockwebserver.MockResponse; + +@Test(groups = "unit", testName = "AWSAMIApiMockTest", singleThreaded = true) +public class AWSAMIApiMockTest extends BaseAWSEC2ApiMockTest { + + public void describeImagesInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/amzn_images.xml"); + + Set result = amiApi().describeImagesInRegion(DEFAULT_REGION); + + assertFalse(result.isEmpty()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages"); + } + + public void describeImagesInRegion_options() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/amzn_images.xml"); + + Set result = amiApi() + .describeImagesInRegion(DEFAULT_REGION, executableBy("me").ownedBy("fred", "nancy").imageIds("1", "2")); + + assertFalse(result.isEmpty()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages&ExecutableBy=me&Owner.1=fred&Owner.2=nancy&ImageId.1=1&ImageId.2=2"); + } + + public void describeImagesInRegion_404() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setResponseCode(404)); + + Set result = amiApi().describeImagesInRegion(DEFAULT_REGION); + + assertTrue(result.isEmpty()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeImages"); + } + + public void createImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setBody("ami-246f8d4d")); + + String result = amiApi().createImageInRegion(DEFAULT_REGION, "name", "instanceId"); + assertEquals(result, "ami-246f8d4d"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=CreateImage&Name=name&InstanceId=instanceId"); + } + + public void createImageInRegion_options() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, + new MockResponse().setBody("ami-246f8d4d")); + + String result = amiApi().createImageInRegion(DEFAULT_REGION, "name", "instanceId", + new CreateImageOptions().withDescription("description").noReboot()); + assertEquals(result, "ami-246f8d4d"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=CreateImage&Name=name&InstanceId=instanceId&Description=description&NoReboot=true"); + } + + public void registerImageFromManifestInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setBody("ami-246f8d4d")); + + String result = amiApi().registerImageFromManifestInRegion(DEFAULT_REGION, "name", "pathToManifest"); + assertEquals(result, "ami-246f8d4d"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=RegisterImage&Name=name&ImageLocation=pathToManifest"); + } + + public void registerImageFromManifestInRegion_options() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setBody("ami-246f8d4d")); + + String result = amiApi().registerImageFromManifestInRegion(DEFAULT_REGION, "name", "pathToManifest", + new RegisterImageOptions().withDescription("description")); + assertEquals(result, "ami-246f8d4d"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=RegisterImage&Name=name&ImageLocation=pathToManifest&Description=description"); + } + + public void registerUnixImageBackedByEbsInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse() + .setBody("ami-246f8d4d")); + + String result = amiApi().registerUnixImageBackedByEbsInRegion(DEFAULT_REGION, "imageName", "snapshotId"); + assertEquals(result, "ami-246f8d4d"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=RegisterImage&RootDeviceName=/dev/sda1&BlockDeviceMapping.0.DeviceName=/dev/sda1&Name=imageName&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId"); + } + + public void registerUnixImageBackedByEbsInRegion_options() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setBody("ami-246f8d4d")); + + String result = amiApi().registerUnixImageBackedByEbsInRegion(DEFAULT_REGION, "imageName", "snapshotId", + new RegisterImageBackedByEbsOptions().withDescription("description") + .addBlockDeviceFromSnapshot("/dev/device", null, "snapshot", false, "gp2", null, false) + .addNewBlockDevice("/dev/newdevice", "newblock", 100)); + assertEquals(result, "ami-246f8d4d"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=RegisterImage&RootDeviceName=/dev/sda1&BlockDeviceMapping.0.DeviceName=/dev/sda1&Name=imageName&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Description=description&BlockDeviceMapping.1.Ebs.DeleteOnTermination=false&BlockDeviceMapping.1.Ebs.VolumeType=gp2&BlockDeviceMapping.1.DeviceName=/dev/device&BlockDeviceMapping.1.Ebs.SnapshotId=snapshot&BlockDeviceMapping.2.Ebs.DeleteOnTermination=false&BlockDeviceMapping.2.DeviceName=/dev/newdevice&BlockDeviceMapping.2.VirtualName=newblock&BlockDeviceMapping.2.Ebs.VolumeSize=100"); + } + + public void deregisterImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + amiApi().deregisterImageInRegion(DEFAULT_REGION, "ami-246f8d4d"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DeregisterImage&ImageId=ami-246f8d4d"); + } + + public void getBlockDeviceMappingsForImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/describe_image_attribute_blockDeviceMapping.xml"); + + Map result = amiApi() + .getBlockDeviceMappingsForImageInRegion(DEFAULT_REGION, "ami-246f8d4d"); + + assertFalse(result.isEmpty()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeImageAttribute&Attribute=blockDeviceMapping&ImageId=ami-246f8d4d"); + } + + public void getLaunchPermissionForImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/describe_image_attribute_launchPermission.xml"); + + Permission result = amiApi().getLaunchPermissionForImageInRegion(DEFAULT_REGION, "ami-246f8d4d"); + + assertNotNull(result); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeImageAttribute&Attribute=launchPermission&ImageId=ami-246f8d4d"); + } + + public void addLaunchPermissionsToImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + amiApi().addLaunchPermissionsToImageInRegion(DEFAULT_REGION, ImmutableList.of("bob", "sue"), + ImmutableList.of("all"), "imageId"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=ModifyImageAttribute&OperationType=add&Attribute=launchPermission&ImageId=imageId&UserId.1=bob&UserId.2=sue&UserGroup.1=all"); + } + + public void removeLaunchPermissionsFromImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + amiApi().removeLaunchPermissionsFromImageInRegion(DEFAULT_REGION, ImmutableList.of("bob", "sue"), + ImmutableList.of("all"), "imageId"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=ModifyImageAttribute&OperationType=remove&Attribute=launchPermission&ImageId=imageId&UserId.1=bob&UserId.2=sue&UserGroup.1=all"); + } + + public void resetLaunchPermissionsOnImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + amiApi().resetLaunchPermissionsOnImageInRegion(DEFAULT_REGION, "imageId"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=ResetImageAttribute&Attribute=launchPermission&ImageId=imageId"); + } + + public void getProductCodesForImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/describe_image_attribute_productCodes.xml"); + + Set result = amiApi().getProductCodesForImageInRegion(DEFAULT_REGION, "ami-246f8d4d"); + + assertFalse(result.isEmpty()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeImageAttribute&Attribute=productCodes&ImageId=ami-246f8d4d"); + } + + public void addProductCodesToImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + amiApi().addProductCodesToImageInRegion(DEFAULT_REGION, ImmutableList.of("code1", "code2"), "imageId"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=ModifyImageAttribute&OperationType=add&Attribute=productCodes&ImageId=imageId&ProductCode.1=code1&ProductCode.2=code2"); + } + + public void removeProductCodesFromImageInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + amiApi().removeProductCodesFromImageInRegion(DEFAULT_REGION, ImmutableList.of("code1", "code2"), "imageId"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=ModifyImageAttribute&OperationType=remove&Attribute=productCodes&ImageId=imageId&ProductCode.1=code1&ProductCode.2=code2"); + } + + private AWSAMIApi amiApi() { + return api().getAMIApi().get(); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiTest.java deleted file mode 100644 index cc99fb28a6..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiTest.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.features; - -import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.executableBy; -import static org.jclouds.reflect.Reflection2.method; - -import java.io.IOException; - -import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404; -import org.jclouds.aws.ec2.xml.ProductCodesHandler; -import org.jclouds.ec2.options.CreateImageOptions; -import org.jclouds.ec2.options.DescribeImagesOptions; -import org.jclouds.ec2.options.RegisterImageBackedByEbsOptions; -import org.jclouds.ec2.options.RegisterImageOptions; -import org.jclouds.ec2.xml.BlockDeviceMappingHandler; -import org.jclouds.ec2.xml.DescribeImagesResponseHandler; -import org.jclouds.ec2.xml.ImageIdHandler; -import org.jclouds.ec2.xml.PermissionHandler; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.functions.ParseSax; -import org.jclouds.http.functions.ReleasePayloadAndReturn; -import org.jclouds.rest.internal.GeneratedHttpRequest; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.reflect.Invokable; -/** - * Tests behavior of {@code AWSAMIApi} - */ -// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire -@Test(groups = "unit", testName = "AWSAMIApiTest") -public class AWSAMIApiTest extends BaseAWSEC2ApiTest { - public AWSAMIApiTest() { - provider = "aws-ec2"; - } - - HttpRequest createImage = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "CreateImage") - .addFormParam("InstanceId", "instanceId") - .addFormParam("Name", "name").build(); - - public void testCreateImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "createImageInRegion", String.class, String.class, String.class, - CreateImageOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "name", "instanceId")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(createImage).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, ImageIdHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest createImageOptions = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "CreateImage") - .addFormParam("Description", "description") - .addFormParam("InstanceId", "instanceId") - .addFormParam("Name", "name") - .addFormParam("NoReboot", "true").build(); - - public void testCreateImageOptions() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "createImageInRegion", String.class, String.class, String.class, - CreateImageOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "name", "instanceId", new CreateImageOptions() - .withDescription("description").noReboot())); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(createImageOptions).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, ImageIdHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest describeImages = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "DescribeImages").build(); - - public void testDescribeImages() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "describeImagesInRegion", String.class, - DescribeImagesOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList((String) null)); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(describeImages).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, DescribeImagesResponseHandler.class); - assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - HttpRequest describeImagesOptions = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "DescribeImages") - .addFormParam("ExecutableBy", "me") - .addFormParam("ImageId.1", "1") - .addFormParam("ImageId.2", "2") - .addFormParam("Owner.1", "fred") - .addFormParam("Owner.2", "nancy").build(); - - public void testDescribeImagesOptions() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "describeImagesInRegion", String.class, - DescribeImagesOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, executableBy("me").ownedBy("fred", "nancy").imageIds( - "1", "2"))); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(describeImagesOptions).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, DescribeImagesResponseHandler.class); - assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - HttpRequest deregisterImage = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "DeregisterImage") - .addFormParam("ImageId", "imageId").build(); - - public void testDeregisterImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "deregisterImageInRegion", String.class, String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "imageId")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(deregisterImage).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest registerImageFromManifest = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "RegisterImage") - .addFormParam("ImageLocation", "pathToManifest") - .addFormParam("Name", "name").build(); - - public void testRegisterImageFromManifest() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "registerImageFromManifestInRegion", String.class, String.class, - String.class, RegisterImageOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "name", "pathToManifest")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(registerImageFromManifest).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, ImageIdHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest registerImageFromManifestOptions = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "RegisterImage") - .addFormParam("Description", "description") - .addFormParam("ImageLocation", "pathToManifest") - .addFormParam("Name", "name").build(); - - public void testRegisterImageFromManifestOptions() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "registerImageFromManifestInRegion", String.class, String.class, - String.class, RegisterImageOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "name", "pathToManifest", new RegisterImageOptions() - .withDescription("description"))); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(registerImageFromManifestOptions).getPayload().getRawContent() - .toString(), "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, ImageIdHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest registerImageBackedByEBS = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "RegisterImage") - .addFormParam("BlockDeviceMapping.0.DeviceName", "/dev/sda1") - .addFormParam("BlockDeviceMapping.0.Ebs.SnapshotId", "snapshotId") - .addFormParam("Name", "imageName") - .addFormParam("RootDeviceName", "/dev/sda1").build(); - - public void testRegisterImageBackedByEBS() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "registerUnixImageBackedByEbsInRegion", String.class, - String.class, String.class, RegisterImageBackedByEbsOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "imageName", "snapshotId")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(registerImageBackedByEBS).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, ImageIdHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest registerImageBackedByEBSOptions = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "RegisterImage") - .addFormParam("BlockDeviceMapping.0.DeviceName", "/dev/sda1") - .addFormParam("BlockDeviceMapping.0.Ebs.SnapshotId", "snapshotId") - .addFormParam("BlockDeviceMapping.1.DeviceName", "/dev/device") - .addFormParam("BlockDeviceMapping.1.Ebs.DeleteOnTermination", "false") - .addFormParam("BlockDeviceMapping.1.Ebs.SnapshotId", "snapshot") - .addFormParam("BlockDeviceMapping.1.Ebs.VolumeType", "gp2") - .addFormParam("BlockDeviceMapping.2.DeviceName", "/dev/newdevice") - .addFormParam("BlockDeviceMapping.2.Ebs.DeleteOnTermination", "false") - .addFormParam("BlockDeviceMapping.2.Ebs.VolumeSize", "100") - .addFormParam("BlockDeviceMapping.2.VirtualName", "newblock") - .addFormParam("Description", "description") - .addFormParam("Name", "imageName") - .addFormParam("RootDeviceName", "/dev/sda1").build(); - - public void testRegisterImageBackedByEBSOptions() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "registerUnixImageBackedByEbsInRegion", String.class, - String.class, String.class, RegisterImageBackedByEbsOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "imageName", "snapshotId", - new RegisterImageBackedByEbsOptions().withDescription("description").addBlockDeviceFromSnapshot( - "/dev/device", null, "snapshot", false, "gp2", null, false).addNewBlockDevice("/dev/newdevice", "newblock", 100))); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(registerImageBackedByEBSOptions).getPayload().getRawContent() - .toString(), "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, ImageIdHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest getBlockDeviceMappingsForImage = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "DescribeImageAttribute") - .addFormParam("Attribute", "blockDeviceMapping") - .addFormParam("ImageId", "imageId").build(); - - public void testGetBlockDeviceMappingsForImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "getBlockDeviceMappingsForImageInRegion", String.class, - String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "imageId")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(getBlockDeviceMappingsForImage).getPayload().getRawContent() - .toString(), "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, BlockDeviceMappingHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest getLaunchPermissionForImage = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "DescribeImageAttribute") - .addFormParam("Attribute", "launchPermission") - .addFormParam("ImageId", "imageId").build(); - - public void testGetLaunchPermissionForImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "getLaunchPermissionForImageInRegion", String.class, String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "imageId")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(getLaunchPermissionForImage).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, PermissionHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest addLaunchPermission = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "ModifyImageAttribute") - .addFormParam("Attribute", "launchPermission") - .addFormParam("ImageId", "imageId") - .addFormParam("OperationType", "add") - .addFormParam("UserGroup.1", "all") - .addFormParam("UserId.1", "bob") - .addFormParam("UserId.2", "sue").build(); - - public void testAddLaunchPermissionsToImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "addLaunchPermissionsToImageInRegion", String.class, - Iterable.class, Iterable.class, String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, ImmutableList.of("bob", "sue"), ImmutableList - .of("all"), "imageId")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(addLaunchPermission).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest removeLaunchPermission = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "ModifyImageAttribute") - .addFormParam("Attribute", "launchPermission") - .addFormParam("ImageId", "imageId") - .addFormParam("OperationType", "remove") - .addFormParam("UserGroup.1", "all") - .addFormParam("UserId.1", "bob") - .addFormParam("UserId.2", "sue").build(); - - public void testRemoveLaunchPermissionsFromImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "removeLaunchPermissionsFromImageInRegion", String.class, - Iterable.class, Iterable.class, String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, ImmutableList.of("bob", "sue"), ImmutableList - .of("all"), "imageId")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(removeLaunchPermission).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - HttpRequest resetLaunchPermissionsOnImage = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "ResetImageAttribute") - .addFormParam("Attribute", "launchPermission") - .addFormParam("ImageId", "imageId").build(); - - public void testResetLaunchPermissionsOnImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "resetLaunchPermissionsOnImageInRegion", String.class, - String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "imageId")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, - filter.filter(resetLaunchPermissionsOnImage).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - public void testGetProductCodesForImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "getProductCodesForImageInRegion", String.class, String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "imageId")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, - "Action=DescribeImageAttribute&Attribute=productCodes&ImageId=imageId", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, ProductCodesHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - public void testAddProductCodesToImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "addProductCodesToImageInRegion", String.class, Iterable.class, - String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, ImmutableList.of("code1", "code2"), "imageId")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals( - request, - "Action=ModifyImageAttribute&OperationType=add&Attribute=productCodes&ImageId=imageId&ProductCode.1=code1&ProductCode.2=code2", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - public void testRemoveProductCodesFromImage() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(AWSAMIApi.class, "removeProductCodesFromImageInRegion", String.class, - Iterable.class, String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, ImmutableList.of("code1", "code2"), "imageId")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals( - request, - "Action=ModifyImageAttribute&OperationType=remove&Attribute=productCodes&ImageId=imageId&ProductCode.1=code1&ProductCode.2=code2", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSSecurityGroupApiTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSSecurityGroupApiTest.java index 735af5bff4..2e73dfef13 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSSecurityGroupApiTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSSecurityGroupApiTest.java @@ -24,7 +24,6 @@ import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404; import org.jclouds.Fallbacks.VoidOnNotFoundOr404; import org.jclouds.aws.ec2.xml.AWSEC2DescribeSecurityGroupsResponseHandler; import org.jclouds.ec2.util.IpPermissions; -import org.jclouds.http.HttpRequest; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ReleasePayloadAndReturn; import org.jclouds.net.domain.IpPermission; @@ -97,13 +96,6 @@ public class AWSSecurityGroupApiTest extends BaseAWSEC2ApiTest method = method(AWSSecurityGroupApi.class, "createSecurityGroupInRegion", String.class, String.class, String.class); @@ -113,7 +105,7 @@ public class AWSSecurityGroupApiTest extends BaseAWSEC2ApiTest extends BaseRestAnnotationProcessingTest { - @ConfiguresHttpApi + @ConfiguresHttpApi protected static class StubAWSEC2HttpApiModule extends AWSEC2HttpApiModule { @Override protected String provideTimeStamp(DateService dateService) { - return "2009-11-08T15:54:08.897Z"; + return "20120416T155408Z"; } @Provides @@ -83,10 +89,10 @@ public abstract class BaseAWSEC2ApiTest extends BaseRestAnnotationProcessingT @Override public Map> get() { - return transformValues(ImmutableMap. of(Region.EU_WEST_1, URI - .create("https://ec2.eu-west-1.amazonaws.com"), Region.US_EAST_1, URI - .create("https://ec2.us-east-1.amazonaws.com"), Region.US_WEST_1, URI - .create("https://ec2.us-west-1.amazonaws.com")), Suppliers2. ofInstanceFunction()); + return transformValues(ImmutableMap + .of(Region.EU_WEST_1, URI.create("https://ec2.eu-west-1.amazonaws.com"), + Region.US_EAST_1, URI.create("https://ec2.us-east-1.amazonaws.com"), Region.US_WEST_1, + URI.create("https://ec2.us-west-1.amazonaws.com")), Suppliers2.ofInstanceFunction()); } }); @@ -100,21 +106,49 @@ public abstract class BaseAWSEC2ApiTest extends BaseRestAnnotationProcessingT }); } + + @Provides ServiceAndRegion ServiceAndRegion(){ + return new ServiceAndRegion() { + @Override public String service() { + return "ec2"; + } + + @Override public String region(String host) { + return "us-east-1"; + } + }; + } } - protected FormSigner filter; + @Override protected void assertNonPayloadHeadersEqual(HttpRequest request, String toMatch) { + Multimap headersToCheck = LinkedHashMultimap.create(); + for (String key : request.getHeaders().keySet()) { + if (key.equals("X-Amz-Date")) { + assertEquals(request.getFirstHeaderOrNull(key), "20120416T155408Z"); + } else if (key.equals("Authorization")) { + assertThat(request.getFirstHeaderOrNull(AUTHORIZATION)).startsWith( + "AWS4-HMAC-SHA256 Credential=identity/20120416/" + + "us-east-1/ec2/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature="); + } else { + headersToCheck.putAll(key, request.getHeaders().get(key)); + } + } + assertEquals(sortAndConcatHeadersIntoString(headersToCheck), toMatch); + } + + protected FormSignerV4 filter; @Override protected void checkFilters(HttpRequest request) { assertEquals(request.getFilters().size(), 1); - assertEquals(request.getFilters().get(0).getClass(), FormSigner.class); + assertTrue(request.getFilters().get(0) instanceof FormSignerV4); } @Override @BeforeTest protected void setupFactory() throws IOException { super.setupFactory(); - this.filter = injector.getInstance(FormSigner.class); + this.filter = injector.getInstance(FormSignerV4.class); } @Override diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/MonitoringApiMockTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/MonitoringApiMockTest.java new file mode 100644 index 0000000000..cad674cc67 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/MonitoringApiMockTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.ec2.features; + +import static org.testng.Assert.assertFalse; + +import java.util.Map; + +import org.jclouds.aws.ec2.domain.MonitoringState; +import org.jclouds.aws.ec2.internal.BaseAWSEC2ApiMockTest; +import org.testng.annotations.Test; + +@Test(groups = "unit", testName = "MonitoringApiMockTest", singleThreaded = true) +public class MonitoringApiMockTest extends BaseAWSEC2ApiMockTest { + + public void monitorInstancesInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/monitoring.xml"); + + Map result = monitoringApi() + .monitorInstancesInRegion(DEFAULT_REGION, "i-911444f0", "i-911444f1"); + + assertFalse(result.isEmpty()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=MonitorInstances&InstanceId.0=i-911444f0&InstanceId.1=i-911444f1"); + } + + public void unmonitorInstancesInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/monitoring.xml"); + + Map result = monitoringApi() + .unmonitorInstancesInRegion(DEFAULT_REGION, "i-911444f0", "i-911444f1"); + + assertFalse(result.isEmpty()); + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=UnmonitorInstances&InstanceId.0=i-911444f0&InstanceId.1=i-911444f1"); + } + + private MonitoringApi monitoringApi() { + return api().getMonitoringApi().get(); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/MonitoringApiTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/MonitoringApiTest.java deleted file mode 100644 index 23b9d62111..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/MonitoringApiTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.features; - -import static org.jclouds.reflect.Reflection2.method; - -import java.io.IOException; - -import org.jclouds.aws.ec2.xml.MonitoringStateHandler; -import org.jclouds.http.functions.ParseSax; -import org.jclouds.rest.internal.GeneratedHttpRequest; -import org.testng.annotations.Test; - -import com.google.common.collect.Lists; -import com.google.common.reflect.Invokable; -/** - * Tests behavior of {@code MonitoringApi} - */ -// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire -@Test(groups = "unit", testName = "MonitoringApiTest") -public class MonitoringApiTest extends BaseAWSEC2ApiTest { - - public void testUnmonitorInstances() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(MonitoringApi.class, "unmonitorInstancesInRegion", String.class, String.class, - String[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "instance1", "instance2")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - String payload = "Action=UnmonitorInstances&InstanceId.0=instance1&InstanceId.1=instance2"; - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, payload, "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, MonitoringStateHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - public void testMonitorInstances() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(MonitoringApi.class, "monitorInstancesInRegion", String.class, String.class, - String[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "instance1", "instance2")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, - "Action=MonitorInstances&InstanceId.0=instance1&InstanceId.1=instance2", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, MonitoringStateHandler.class); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiExpectTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiExpectTest.java deleted file mode 100644 index 77d51de574..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiExpectTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.features; - -import static com.google.common.collect.Iterables.getOnlyElement; -import static org.testng.Assert.assertEquals; - -import org.jclouds.aws.ec2.AWSEC2Api; -import org.jclouds.aws.ec2.compute.internal.BaseAWSEC2ComputeServiceExpectTest; -import org.jclouds.aws.ec2.domain.PlacementGroup; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpResponse; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; - -@Test(groups = "unit", testName = "PlacementGroupApiExpectTest") -public class PlacementGroupApiExpectTest extends BaseAWSEC2ComputeServiceExpectTest { - - HttpRequest filter = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "DescribePlacementGroups") - .addFormParam("Filter.1.Name", "strategy") - .addFormParam("Filter.1.Value.1", "cluster") - .addFormParam("Signature", "SaA7Un1BE3m9jIEKyjXNdQPzFh/QAJSCebvKXiwUEK0%3D") - .addFormParam("SignatureMethod", "HmacSHA256") - .addFormParam("SignatureVersion", "2") - .addFormParam("Timestamp", "2012-04-16T15%3A54%3A08.897Z") - .addFormParam("Version", "2012-06-01") - .addFormParam("AWSAccessKeyId", "identity").build(); - - public void testFilterWhenResponseIs2xx() { - HttpResponse filterResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType("/describe_placement_groups.xml", "text/xml")).build(); - - AWSEC2Api apiWhenExist = requestsSendResponses(describeRegionsRequest, describeRegionsResponse, filter, filterResponse) - .getContext().unwrapApi(AWSEC2Api.class); - - PlacementGroup group = getOnlyElement(apiWhenExist.getPlacementGroupApi().get().describePlacementGroupsInRegionWithFilter("us-east-1", - ImmutableMultimap.builder() - .put("strategy", "cluster") - .build())); - - assertEquals(group.getName(), "XYZ-cluster"); - } - - public void testFilterWhenResponseIs404() { - HttpResponse filterResponse = HttpResponse.builder().statusCode(404).build(); - - AWSEC2Api apiWhenNotExist = requestsSendResponses(describeRegionsRequest, describeRegionsResponse, filter, filterResponse) - .getContext().unwrapApi(AWSEC2Api.class); - - assertEquals(apiWhenNotExist.getPlacementGroupApi().get().describePlacementGroupsInRegionWithFilter("us-east-1", - ImmutableMultimap.builder() - .put("strategy", "cluster") - .build()), - ImmutableSet.of()); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiMockTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiMockTest.java new file mode 100644 index 0000000000..b94c6c39f8 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiMockTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.ec2.features; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static org.testng.Assert.assertEquals; + +import org.jclouds.aws.ec2.domain.PlacementGroup; +import org.jclouds.aws.ec2.internal.BaseAWSEC2ApiMockTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.squareup.okhttp.mockwebserver.MockResponse; + +@Test(groups = "unit", testName = "PlacementGroupApiMockTest", singleThreaded = true) +public class PlacementGroupApiMockTest extends BaseAWSEC2ApiMockTest { + + public void describePlacementGroupsInRegionWithFilter() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/describe_placement_groups.xml"); + + PlacementGroup result = getOnlyElement(placementApi() + .describePlacementGroupsInRegionWithFilter(DEFAULT_REGION, ImmutableMultimap.of("strategy", "cluster"))); + + assertEquals(result.getName(), "XYZ-cluster"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribePlacementGroups&Filter.1.Name=strategy&Filter.1.Value.1=cluster"); + } + + public void describePlacementGroupsInRegionWithFilter_404() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setResponseCode(404)); + + assertEquals(placementApi().describePlacementGroupsInRegionWithFilter(DEFAULT_REGION, + ImmutableMultimap.of("strategy", "cluster")), ImmutableSet.of()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribePlacementGroups&Filter.1.Name=strategy&Filter.1.Value.1=cluster"); + } + + public void deletePlacementGroupInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + placementApi().deletePlacementGroupInRegion(DEFAULT_REGION, "name"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DeletePlacementGroup&GroupName=name"); + } + + public void deletePlacementGroupInRegion_404() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setResponseCode(404)); + + placementApi().deletePlacementGroupInRegion(DEFAULT_REGION, "name"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DeletePlacementGroup&GroupName=name"); + } + + public void createPlacementGroupInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + placementApi().createPlacementGroupInRegion(DEFAULT_REGION, "name"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=CreatePlacementGroup&Strategy=cluster&GroupName=name"); + } + + public void createPlacementGroupInRegion_strategy() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + placementApi().createPlacementGroupInRegion(DEFAULT_REGION, "name", "cluster"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=CreatePlacementGroup&GroupName=name&Strategy=cluster"); + } + + private PlacementGroupApi placementApi() { + return api().getPlacementGroupApi().get(); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiTest.java deleted file mode 100644 index 76bd95461c..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/PlacementGroupApiTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.features; - -import static org.jclouds.reflect.Reflection2.method; - -import java.io.IOException; - -import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404; -import org.jclouds.Fallbacks.VoidOnNotFoundOr404; -import org.jclouds.aws.ec2.xml.DescribePlacementGroupsResponseHandler; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.functions.ParseSax; -import org.jclouds.http.functions.ReleasePayloadAndReturn; -import org.jclouds.rest.internal.GeneratedHttpRequest; -import org.testng.annotations.Test; - -import com.google.common.collect.Lists; -import com.google.common.reflect.Invokable; -/** - * Tests behavior of {@code PlacementGroupApi} - */ -// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire -@Test(groups = "unit", testName = "PlacementGroupApiTest") -public class PlacementGroupApiTest extends BaseAWSEC2ApiTest { - - public void testDeletePlacementGroup() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(PlacementGroupApi.class, "deletePlacementGroupInRegion", String.class, - String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "name")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=DeletePlacementGroup&GroupName=name", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, VoidOnNotFoundOr404.class); - - checkFilters(request); - } - - HttpRequest createPlacementGroup = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "CreatePlacementGroup") - .addFormParam("GroupName", "name") - .addFormParam("Strategy", "cluster").build(); - - public void testCreatePlacementGroup() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(PlacementGroupApi.class, "createPlacementGroupInRegion", String.class, - String.class, String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "name", "cluster")); - - request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, filter.filter(createPlacementGroup).getPayload().getRawContent().toString(), - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - public void testCreatePlacementGroupDefault() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(PlacementGroupApi.class, "createPlacementGroupInRegion", String.class, - String.class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "name")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=CreatePlacementGroup&Strategy=cluster&GroupName=name", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, null); - - checkFilters(request); - } - - public void testDescribePlacementGroups() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(PlacementGroupApi.class, "describePlacementGroupsInRegion", String.class, - String[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList((String) null)); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=DescribePlacementGroups", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, DescribePlacementGroupsResponseHandler.class); - assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - public void testDescribePlacementGroupsArgs() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(PlacementGroupApi.class, "describePlacementGroupsInRegion", String.class, - String[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "1", "2")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=DescribePlacementGroups&GroupName.1=1&GroupName.2=2", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, DescribePlacementGroupsResponseHandler.class); - assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class); - - checkFilters(request); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiExpectTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiExpectTest.java deleted file mode 100644 index 7460505555..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiExpectTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.features; - -import static com.google.common.collect.Iterables.getOnlyElement; -import static org.testng.Assert.assertEquals; - -import org.jclouds.aws.ec2.AWSEC2Api; -import org.jclouds.aws.ec2.compute.internal.BaseAWSEC2ComputeServiceExpectTest; -import org.jclouds.aws.ec2.domain.SpotInstanceRequest; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpResponse; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; - -@Test(groups = "unit", testName = "SpotInstanceApiExpectTest") -public class SpotInstanceApiExpectTest extends BaseAWSEC2ComputeServiceExpectTest { - - HttpRequest filter = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "DescribeSpotInstanceRequests") - .addFormParam("Filter.1.Name", "instance-id") - .addFormParam("Filter.1.Value.1", "i-ef308e8e") - .addFormParam("Signature", "wQtGpumMCDEzvlldKepCKeEjD9iE7eAyiRBlQztcJMA%3D") - .addFormParam("SignatureMethod", "HmacSHA256") - .addFormParam("SignatureVersion", "2") - .addFormParam("Timestamp", "2012-04-16T15%3A54%3A08.897Z") - .addFormParam("Version", "2012-06-01") - .addFormParam("AWSAccessKeyId", "identity").build(); - - public void testFilterWhenResponseIs2xx() { - HttpResponse filterResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResourceWithContentType("/describe_spot_instance.xml", "text/xml")).build(); - - AWSEC2Api apiWhenExist = requestsSendResponses(describeRegionsRequest, describeRegionsResponse, filter, filterResponse) - .getContext().unwrapApi(AWSEC2Api.class); - - SpotInstanceRequest request = getOnlyElement(apiWhenExist.getSpotInstanceApi().get().describeSpotInstanceRequestsInRegionWithFilter("us-east-1", - ImmutableMultimap.builder() - .put("instance-id", "i-ef308e8e") - .build())); - - assertEquals(request.getId(), "sir-1ede0012"); - } - - public void testFilterWhenResponseIs404() { - HttpResponse filterResponse = HttpResponse.builder().statusCode(404).build(); - - AWSEC2Api apiWhenNotExist = requestsSendResponses(describeRegionsRequest, describeRegionsResponse, filter, filterResponse) - .getContext().unwrapApi(AWSEC2Api.class); - - assertEquals(apiWhenNotExist.getSpotInstanceApi().get().describeSpotInstanceRequestsInRegionWithFilter("us-east-1", - ImmutableMultimap.builder() - .put("instance-id", "i-ef308e8e") - .build()), - ImmutableSet.of()); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiMockTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiMockTest.java new file mode 100644 index 0000000000..5863059e04 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiMockTest.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.ec2.features; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static org.jclouds.aws.ec2.options.DescribeSpotPriceHistoryOptions.Builder.from; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Date; +import java.util.Set; + +import org.jclouds.aws.ec2.domain.Spot; +import org.jclouds.aws.ec2.domain.SpotInstanceRequest; +import org.jclouds.aws.ec2.internal.BaseAWSEC2ApiMockTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.squareup.okhttp.mockwebserver.MockResponse; + + @Test(groups = "unit", testName = "SpotInstanceApiMockTest", singleThreaded = true) + public class SpotInstanceApiMockTest extends BaseAWSEC2ApiMockTest { + + public void describeSpotInstanceRequestsInRegionWithFilter() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/describe_spot_instance.xml"); + + SpotInstanceRequest result = getOnlyElement(spotApi() + .describeSpotInstanceRequestsInRegionWithFilter(DEFAULT_REGION, + ImmutableMultimap.of("instance-id", "i-ef308e8e"))); + + assertEquals(result.getId(), "sir-1ede0012"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotInstanceRequests&Filter.1.Name=instance-id&Filter.1.Value.1=i-ef308e8e"); + } + + public void describeSpotInstanceRequestsInRegionWithFilter_404() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setResponseCode(404)); + + assertEquals(spotApi().describeSpotInstanceRequestsInRegionWithFilter(DEFAULT_REGION, + ImmutableMultimap.of("instance-id", "i-ef308e8e")), ImmutableSet.of()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotInstanceRequests&Filter.1.Name=instance-id&Filter.1.Value.1=i-ef308e8e"); + } + + public void cancelSpotInstanceRequestsInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse()); + + spotApi().cancelSpotInstanceRequestsInRegion(DEFAULT_REGION, "sir-f4d44212"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=CancelSpotInstanceRequests&SpotInstanceRequestId.1=sir-f4d44212"); + } + + public void cancelSpotInstanceRequestsInRegion_404() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setResponseCode(404)); + + spotApi().cancelSpotInstanceRequestsInRegion(DEFAULT_REGION, "sir-f4d44212"); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=CancelSpotInstanceRequests&SpotInstanceRequestId.1=sir-f4d44212"); + } + + public void describeSpotPriceHistoryInRegion() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/describe_spot_price_history.xml"); + + Set result = spotApi().describeSpotPriceHistoryInRegion(DEFAULT_REGION); + + assertEquals(result.size(), 3); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotPriceHistory"); + } + + public void describeSpotPriceHistoryInRegion_404() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueue(DEFAULT_REGION, new MockResponse().setResponseCode(404)); + + Set result = spotApi().describeSpotPriceHistoryInRegion(DEFAULT_REGION); + + assertTrue(result.isEmpty()); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotPriceHistory"); + } + + public void describeSpotPriceHistoryInRegionOptions() throws Exception { + enqueueRegions(DEFAULT_REGION); + enqueueXml(DEFAULT_REGION, "/describe_spot_price_history.xml"); + + Date from = new Date(12345678910l); + Date to = new Date(1234567891011l); + + Set result = spotApi().describeSpotPriceHistoryInRegion(DEFAULT_REGION, + from(from).to(to).productDescription("description").instanceType("m1.small")); + + assertEquals(result.size(), 3); + + assertPosted(DEFAULT_REGION, "Action=DescribeRegions"); + assertPosted(DEFAULT_REGION, "Action=DescribeSpotPriceHistory&StartTime=1970-05-23T21%3A21%3A18.910Z&EndTime=2009-02-13T23%3A31%3A31.011Z&ProductDescription=description&InstanceType.1=m1.small"); + } + + private SpotInstanceApi spotApi() { + return api().getSpotInstanceApi().get(); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiTest.java deleted file mode 100644 index 5337ca8e2d..0000000000 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/SpotInstanceApiTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.aws.ec2.features; - -import static org.jclouds.reflect.Reflection2.method; - -import java.io.IOException; -import java.util.Date; - -import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404; -import org.jclouds.Fallbacks.VoidOnNotFoundOr404; -import org.jclouds.aws.ec2.options.DescribeSpotPriceHistoryOptions; -import org.jclouds.aws.ec2.xml.DescribeSpotPriceHistoryResponseHandler; -import org.jclouds.http.functions.ParseSax; -import org.jclouds.http.functions.ReleasePayloadAndReturn; -import org.jclouds.rest.internal.GeneratedHttpRequest; -import org.testng.annotations.Test; - -import com.google.common.collect.Lists; -import com.google.common.reflect.Invokable; -/** - * Tests behavior of {@code SpotInstanceApi} - */ -// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire -@Test(groups = "unit", testName = "SpotInstanceApiTest") -public class SpotInstanceApiTest extends BaseAWSEC2ApiTest { - - public void testCancelSpotInstanceRequests() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(SpotInstanceApi.class, "cancelSpotInstanceRequestsInRegion", String.class, - String[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "id")); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=CancelSpotInstanceRequests&SpotInstanceRequestId.1=id", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); - assertSaxResponseParserClassEquals(method, null); - assertFallbackClassEquals(method, VoidOnNotFoundOr404.class); - - checkFilters(request); - } - - public void testDescribeSpotPriceHistory() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(SpotInstanceApi.class, "describeSpotPriceHistoryInRegion", String.class, - DescribeSpotPriceHistoryOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList((String) null)); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals(request, "Action=DescribeSpotPriceHistory", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, DescribeSpotPriceHistoryResponseHandler.class); - assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class); - - checkFilters(request); - } - - Date from = new Date(12345678910l); - Date to = new Date(1234567891011l); - - public void testDescribeSpotPriceHistoryArgs() throws SecurityException, NoSuchMethodException, IOException { - Invokable method = method(SpotInstanceApi.class, "describeSpotPriceHistoryInRegion", String.class, - DescribeSpotPriceHistoryOptions[].class); - GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, DescribeSpotPriceHistoryOptions.Builder.from(from) - .to(to).productDescription("description").instanceType("m1.small"))); - - assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); - assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); - assertPayloadEquals( - request, - "Action=DescribeSpotPriceHistory&StartTime=1970-05-23T21%3A21%3A18.910Z&EndTime=2009-02-13T23%3A31%3A31.011Z&ProductDescription=description&InstanceType.1=m1.small", - "application/x-www-form-urlencoded", false); - - assertResponseParserClassEquals(method, request, ParseSax.class); - assertSaxResponseParserClassEquals(method, DescribeSpotPriceHistoryResponseHandler.class); - assertFallbackClassEquals(method, EmptySetOnNotFoundOr404.class); - - checkFilters(request); - } -} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/internal/BaseAWSEC2ApiMockTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/internal/BaseAWSEC2ApiMockTest.java new file mode 100644 index 0000000000..bc2170696f --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/internal/BaseAWSEC2ApiMockTest.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.aws.ec2.internal; + +import static com.google.common.base.Throwables.propagate; +import static com.google.common.net.HttpHeaders.AUTHORIZATION; +import static com.google.common.net.HttpHeaders.CONTENT_TYPE; +import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; +import static org.assertj.core.api.Assertions.assertThat; +import static org.jclouds.aws.filters.FormSignerV4.ServiceAndRegion; +import static org.jclouds.util.Strings2.toStringAndClose; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.jclouds.Constants; +import org.jclouds.ContextBuilder; +import org.jclouds.aws.ec2.AWSEC2Api; +import org.jclouds.aws.ec2.AWSEC2ProviderMetadata; +import org.jclouds.aws.ec2.config.AWSEC2HttpApiModule; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.date.DateService; +import org.jclouds.rest.ConfiguresHttpApi; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import com.squareup.okhttp.mockwebserver.RecordedRequest; + +/** + * Tests need to run {@code singleThreaded = true) as otherwise tests will clash on the regionToServers field. + * Sharing the regionToServers field means less code to write. + */ +public class BaseAWSEC2ApiMockTest { + protected static final String DEFAULT_REGION = "us-east-1"; + + // Example keys from http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html + private static final String ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE"; + private static final String SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; + + private Map regionToServers = Maps.newLinkedHashMap(); + + protected AWSEC2Api api() { + return builder(new Properties()).buildApi(AWSEC2Api.class); + } + + protected ComputeService computeService() { + return builder(new Properties()).buildView(ComputeServiceContext.class).getComputeService(); + } + + protected ContextBuilder builder(Properties overrides) { + overrides.setProperty(Constants.PROPERTY_MAX_RETRIES, "1"); + return ContextBuilder.newBuilder(new AWSEC2ProviderMetadata()) + .credentials(ACCESS_KEY, SECRET_KEY) + .endpoint("http://localhost:" + regionToServers.get(DEFAULT_REGION).getPort()) + .overrides(overrides) + .modules(modules); + } + + private final Set modules = ImmutableSet + .of(new MockAWSEC2HttpApiModule(), new ExecutorServiceModule(sameThreadExecutor())); + + @ConfiguresHttpApi + class MockAWSEC2HttpApiModule extends AWSEC2HttpApiModule { + + @Override + protected String provideTimeStamp(DateService dateService) { + return "20120416T155408Z"; + } + + @Provides ServiceAndRegion serviceAndRegion(){ + return new ServiceAndRegion() { + @Override public String service() { + return "ec2"; + } + + @Override public String region(String host) { + for (Map.Entry regionToServer : regionToServers.entrySet()) { + if (host.equals("localhost:" + regionToServer.getValue().getPort())) { + return regionToServer.getKey(); + } + } + throw new IllegalStateException(host + " not found"); + } + }; + } + } + + @BeforeMethod + public void start() throws IOException { + MockWebServer server = new MockWebServer(); + server.play(); + regionToServers.put(DEFAULT_REGION, server); + } + + @AfterMethod(alwaysRun = true) + public void stop() throws IOException { + for (MockWebServer server : regionToServers.values()) { + server.shutdown(); + } + } + + protected void enqueue(String region, MockResponse response) { + regionToServers.get(region).enqueue(response); + } + + protected void enqueueRegions(String... regions) throws IOException { + StringBuilder describeRegionsResponse = new StringBuilder(); + describeRegionsResponse.append(""); + for (String region : regions) { + describeRegionsResponse.append(""); + describeRegionsResponse.append("").append(region).append(""); + if (!regionToServers.containsKey(region)) { + MockWebServer server = new MockWebServer(); + server.play(); + regionToServers.put(region, server); + } + String regionEndpoint = "http://localhost:" + regionToServers.get(region).getPort(); + describeRegionsResponse.append("").append(regionEndpoint).append(""); + describeRegionsResponse.append(""); + } + describeRegionsResponse.append(""); + enqueue(DEFAULT_REGION, + new MockResponse().addHeader(CONTENT_TYPE, APPLICATION_XML).setBody(describeRegionsResponse.toString())); + } + + protected void enqueueXml(String region, String resource) { + enqueue(region, + new MockResponse().addHeader(CONTENT_TYPE, APPLICATION_XML).setBody(stringFromResource(resource))); + } + + protected String stringFromResource(String resourceName) { + try { + return toStringAndClose(getClass().getResourceAsStream(resourceName)); + } catch (IOException e) { + throw propagate(e); + } + } + + protected RecordedRequest assertPosted(String region, String postParams) throws InterruptedException { + RecordedRequest request = regionToServers.get(region).takeRequest(); + assertEquals(request.getMethod(), "POST"); + assertEquals(request.getPath(), "/"); + assertEquals(request.getHeader("X-Amz-Date"), "20120416T155408Z"); + assertThat( + request.getHeader(AUTHORIZATION)).startsWith("AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20120416/" + + region + "/ec2/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature="); + String body = new String(request.getBody(), Charsets.UTF_8); + assertThat(body).contains("&Version=2012-06-01"); + assertEquals(body.replace("&Version=2012-06-01", ""), postParams); + return request; + } +}