From 78d72fae493067d02ea482304051f1b0359a9990 Mon Sep 17 00:00:00 2001 From: "adrian.f.cole" Date: Mon, 9 Nov 2009 03:39:28 +0000 Subject: [PATCH] Issue 29: supports basic operations git-svn-id: http://jclouds.googlecode.com/svn/trunk@2236 3d8758e0-26b5-11de-8745-db77d3ebf521 --- .../main/java/org/jclouds/aws/ec2/EC2.java} | 20 +- .../java/org/jclouds/aws/ec2/EC2Client.java | 461 +++++++++++++++++ .../jclouds/aws/ec2/EC2ContextBuilder.java | 62 +++ .../jclouds/aws/ec2/EC2ContextFactory.java | 66 +++ .../jclouds/aws/ec2/EC2PropertiesBuilder.java | 76 +++ .../BindGroupNameToIndexedFormParams.java | 26 + .../BindInstanceIdsToIndexedFormParams.java | 26 + .../BindKeyNameToIndexedFormParams.java | 26 + ...upPairToSourceSecurityGroupFormParams.java | 30 ++ .../aws/ec2/config/EC2ContextModule.java} | 64 +-- .../aws/ec2/config/EC2RestClientModule.java | 110 ++++ .../org/jclouds/aws/ec2/domain/Image.java | 262 ++++++++++ .../jclouds/aws/ec2/domain/InstanceState.java | 65 +++ .../jclouds/aws/ec2/domain/InstanceType.java | 108 ++++ .../jclouds/aws/ec2/domain/IpPermission.java | 114 +++++ .../jclouds/aws/ec2/domain/IpProtocol.java | 26 + .../org/jclouds/aws/ec2/domain/KeyPair.java | 91 ++++ .../jclouds/aws/ec2/domain/Reservation.java | 119 +++++ .../aws/ec2/domain/RunningInstance.java | 367 +++++++++++++ .../jclouds/aws/ec2/domain/SecurityGroup.java | 102 ++++ .../aws/ec2/domain/TerminatedInstance.java | 76 +++ .../aws/ec2/domain/UserIdGroupPair.java | 78 +++ .../jclouds/aws/ec2/filters/FormSigner.java | 213 ++++++++ .../functions/ReturnVoidOnGroupNotFound.java | 34 ++ .../ec2/options/DescribeImagesOptions.java | 94 ++++ .../aws/ec2/options/RunInstancesOptions.java | 277 ++++++++++ .../internal/BaseEC2RequestOptions.java | 64 +++ .../aws/ec2/reference/EC2Constants.java | 6 + .../aws/ec2/reference/EC2Parameters.java} | 2 +- .../org/jclouds/aws/ec2/util/EC2Utils.java | 24 + .../aws/ec2/xml/BaseReservationHandler.java | 218 ++++++++ .../xml/DescribeImagesResponseHandler.java | 137 +++++ .../xml/DescribeInstancesResponseHandler.java | 66 +++ .../xml/DescribeKeyPairsResponseHandler.java | 67 +++ ...DescribeSecurityGroupsResponseHandler.java | 133 +++++ .../aws/ec2/xml/KeyPairResponseHandler.java | 63 +++ .../ec2/xml/RunInstancesResponseHandler.java} | 40 +- .../TerminateInstancesResponseHandler.java | 102 ++++ .../handlers/AWSClientErrorRetryHandler.java | 82 +++ .../handlers/AWSRedirectionRetryHandler.java | 90 ++++ .../handlers/ParseAWSErrorFromXmlContent.java | 83 +++ .../jclouds/aws/handlers/package-info.java} | 11 +- .../jclouds/aws/ec2/EC2ClientLiveTest.java | 357 +++++++++++++ .../org/jclouds/aws/ec2/EC2ClientTest.java | 481 ++++++++++++++++++ .../aws/ec2/EC2ContextBuilderTest.java | 98 ++++ .../aws/ec2/ExpensiveEC2ClientLiveTest.java | 211 ++++++++ .../aws/ec2/config/EC2ContextModuleTest.java | 79 +++ .../ec2/config/EC2RestClientModuleTest.java | 70 +++ .../aws/ec2/filters/FormSignerTest.java | 59 +++ .../options/DescribeImagesOptionsTest.java | 118 +++++ .../ec2/options/RunInstancesOptionsTest.java | 344 +++++++++++++ .../DescribeImagesResponseHandlerTest.java | 62 +++ .../DescribeInstancesResponseHandlerTest.java | 111 ++++ .../DescribeKeyPairsResponseHandlerTest.java | 56 ++ ...ribeSecurityGroupsResponseHandlerTest.java | 65 +++ .../ec2/xml/KeyPairResponseHandlerTest.java | 76 +++ .../xml/RunInstancesResponseHandlerTest.java | 91 ++++ ...TerminateInstancesResponseHandlerTest.java | 70 +++ .../src/test/resources/ec2/create_keypair.xml | 27 + .../test/resources/ec2/describe_images.xml | 16 + .../test/resources/ec2/describe_instances.xml | 59 +++ .../ec2/describe_instances_running.xml | 44 ++ .../test/resources/ec2/describe_keypairs.xml | 8 + .../resources/ec2/describe_securitygroups.xml | 36 ++ aws/core/src/test/resources/ec2/log4j.xml | 114 +++++ .../src/test/resources/ec2/run_instances.xml | 71 +++ .../resources/ec2/terminate_instances.xml | 15 + aws/ec2/core/pom.xml | 48 -- .../options/BaseEC2RequestOptions.java | 271 ---------- .../ec2/commands/options/EC2QuerySigner.java | 176 ------- .../commands/options/EC2RequestOptions.java | 67 --- .../options/BaseEC2RequestOptionsTest.java | 124 ----- .../commands/options/EC2QuerySignerTest.java | 103 ---- aws/ec2/pom.xml | 57 --- 74 files changed, 6844 insertions(+), 921 deletions(-) rename aws/{ec2/core/src/main/java/org/jclouds/aws/ec2/config/EC2ConnectionModule.java => core/src/main/java/org/jclouds/aws/ec2/EC2.java} (77%) create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/EC2Client.java create mode 100755 aws/core/src/main/java/org/jclouds/aws/ec2/EC2ContextBuilder.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/EC2ContextFactory.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/EC2PropertiesBuilder.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindGroupNameToIndexedFormParams.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindInstanceIdsToIndexedFormParams.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindKeyNameToIndexedFormParams.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindUserIdGroupPairToSourceSecurityGroupFormParams.java rename aws/{ec2/core/src/main/java/org/jclouds/aws/ec2/internal/GuiceEC2Context.java => core/src/main/java/org/jclouds/aws/ec2/config/EC2ContextModule.java} (51%) create mode 100755 aws/core/src/main/java/org/jclouds/aws/ec2/config/EC2RestClientModule.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/Image.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/InstanceState.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/InstanceType.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/IpPermission.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/IpProtocol.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/KeyPair.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/Reservation.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/RunningInstance.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/SecurityGroup.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/TerminatedInstance.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/UserIdGroupPair.java create mode 100755 aws/core/src/main/java/org/jclouds/aws/ec2/filters/FormSigner.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/functions/ReturnVoidOnGroupNotFound.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/options/DescribeImagesOptions.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/options/RunInstancesOptions.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/options/internal/BaseEC2RequestOptions.java rename aws/{ec2 => }/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Constants.java (84%) mode change 100644 => 100755 rename aws/{ec2/core/src/main/java/org/jclouds/aws/ec2/reference/CommonEC2Parameters.java => core/src/main/java/org/jclouds/aws/ec2/reference/EC2Parameters.java} (98%) mode change 100644 => 100755 create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/util/EC2Utils.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/BaseReservationHandler.java create mode 100755 aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeImagesResponseHandler.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeInstancesResponseHandler.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeKeyPairsResponseHandler.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeSecurityGroupsResponseHandler.java create mode 100755 aws/core/src/main/java/org/jclouds/aws/ec2/xml/KeyPairResponseHandler.java rename aws/{ec2/core/src/main/java/org/jclouds/aws/ec2/EC2Context.java => core/src/main/java/org/jclouds/aws/ec2/xml/RunInstancesResponseHandler.java} (64%) create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/TerminateInstancesResponseHandler.java create mode 100755 aws/core/src/main/java/org/jclouds/aws/handlers/AWSClientErrorRetryHandler.java create mode 100755 aws/core/src/main/java/org/jclouds/aws/handlers/AWSRedirectionRetryHandler.java create mode 100755 aws/core/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java rename aws/{ec2/core/src/main/java/org/jclouds/aws/ec2/EC2Connection.java => core/src/main/java/org/jclouds/aws/handlers/package-info.java} (81%) mode change 100644 => 100755 create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/EC2ClientLiveTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/EC2ClientTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/EC2ContextBuilderTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/ExpensiveEC2ClientLiveTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/config/EC2ContextModuleTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/config/EC2RestClientModuleTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/filters/FormSignerTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/options/DescribeImagesOptionsTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/options/RunInstancesOptionsTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeImagesResponseHandlerTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeInstancesResponseHandlerTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeKeyPairsResponseHandlerTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/KeyPairResponseHandlerTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/RunInstancesResponseHandlerTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/TerminateInstancesResponseHandlerTest.java create mode 100644 aws/core/src/test/resources/ec2/create_keypair.xml create mode 100644 aws/core/src/test/resources/ec2/describe_images.xml create mode 100644 aws/core/src/test/resources/ec2/describe_instances.xml create mode 100644 aws/core/src/test/resources/ec2/describe_instances_running.xml create mode 100644 aws/core/src/test/resources/ec2/describe_keypairs.xml create mode 100644 aws/core/src/test/resources/ec2/describe_securitygroups.xml create mode 100755 aws/core/src/test/resources/ec2/log4j.xml create mode 100644 aws/core/src/test/resources/ec2/run_instances.xml create mode 100644 aws/core/src/test/resources/ec2/terminate_instances.xml delete mode 100644 aws/ec2/core/pom.xml delete mode 100644 aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/BaseEC2RequestOptions.java delete mode 100755 aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/EC2QuerySigner.java delete mode 100644 aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/EC2RequestOptions.java delete mode 100644 aws/ec2/core/src/test/java/org/jclouds/aws/ec2/commands/options/BaseEC2RequestOptionsTest.java delete mode 100755 aws/ec2/core/src/test/java/org/jclouds/aws/ec2/commands/options/EC2QuerySignerTest.java delete mode 100644 aws/ec2/pom.xml diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/config/EC2ConnectionModule.java b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2.java similarity index 77% rename from aws/ec2/core/src/main/java/org/jclouds/aws/ec2/config/EC2ConnectionModule.java rename to aws/core/src/main/java/org/jclouds/aws/ec2/EC2.java index 14992a10d3..bd437efe62 100644 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/config/EC2ConnectionModule.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2.java @@ -21,22 +21,24 @@ * under the License. * ==================================================================== */ -package org.jclouds.aws.ec2.config; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +package org.jclouds.aws.ec2; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import javax.inject.Qualifier; + /** - * designates the the module configures a {@link org.jclouds.aws.ec2.EC2Connection} + * Related to a EC2 resource. * * @author Adrian Cole * */ -@Retention(RUNTIME) -@Target(TYPE) -public @interface EC2ConnectionModule { +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Qualifier +public @interface EC2 { -} +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/EC2Client.java b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2Client.java new file mode 100644 index 0000000000..fa5abff67f --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2Client.java @@ -0,0 +1,461 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + +import static org.jclouds.aws.ec2.reference.EC2Parameters.ACTION; +import static org.jclouds.aws.ec2.reference.EC2Parameters.VERSION; + +import java.util.SortedSet; +import java.util.concurrent.Future; + +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import org.jclouds.aws.ec2.binders.BindGroupNameToIndexedFormParams; +import org.jclouds.aws.ec2.binders.BindInstanceIdsToIndexedFormParams; +import org.jclouds.aws.ec2.binders.BindKeyNameToIndexedFormParams; +import org.jclouds.aws.ec2.binders.BindUserIdGroupPairToSourceSecurityGroupFormParams; +import org.jclouds.aws.ec2.domain.Image; +import org.jclouds.aws.ec2.domain.IpProtocol; +import org.jclouds.aws.ec2.domain.KeyPair; +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.aws.ec2.domain.SecurityGroup; +import org.jclouds.aws.ec2.domain.TerminatedInstance; +import org.jclouds.aws.ec2.domain.UserIdGroupPair; +import org.jclouds.aws.ec2.filters.FormSigner; +import org.jclouds.aws.ec2.functions.ReturnVoidOnGroupNotFound; +import org.jclouds.aws.ec2.options.DescribeImagesOptions; +import org.jclouds.aws.ec2.options.RunInstancesOptions; +import org.jclouds.aws.ec2.xml.DescribeImagesResponseHandler; +import org.jclouds.aws.ec2.xml.DescribeInstancesResponseHandler; +import org.jclouds.aws.ec2.xml.DescribeKeyPairsResponseHandler; +import org.jclouds.aws.ec2.xml.DescribeSecurityGroupsResponseHandler; +import org.jclouds.aws.ec2.xml.KeyPairResponseHandler; +import org.jclouds.aws.ec2.xml.RunInstancesResponseHandler; +import org.jclouds.aws.ec2.xml.TerminateInstancesResponseHandler; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.Endpoint; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.FormParams; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.VirtualHost; +import org.jclouds.rest.annotations.XMLResponseParser; + +/** + * Provides access to EC2 via their REST API. + *

+ * + * @author Adrian Cole + */ +@Endpoint(EC2.class) +@RequestFilters(FormSigner.class) +@FormParams(keys = VERSION, values = "2009-08-15") +@VirtualHost +public interface EC2Client { + + /** + * Returns information about AMIs, AKIs, and ARIs. This includes image type, product codes, + * architecture, and kernel and RAM disk IDs. Images available to you include p ublic images, + * private images that you own, and private images owned by other users for which you have + * explicit launch permissions. + * + * @see #describeInstances + * @see #describeImageAttribute + * @see + * @see DescribeImagesOptions + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DescribeImages") + @XMLResponseParser(DescribeImagesResponseHandler.class) + Future> describeImages(DescribeImagesOptions... options); + + /** + * Returns information about instances that you own. + *

+ * + * If you specify one or more instance IDs, Amazon EC2 returns information for those instances. + * If you do not specify instance IDs, Amazon EC2 returns information for all relevant instances. + * If you specify an invalid instance ID, a fault is returned. If you specify an instance that + * you do not own, it will not be included in the returned results. + *

+ * Recently terminated instances might appear in the returned results.This interval is usually + * less than one hour. + * + * @see #runInstances + * @see #terminateInstances + * @see + * @see DescribeInstancesOptions + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DescribeInstances") + @XMLResponseParser(DescribeInstancesResponseHandler.class) + Future> describeInstances( + @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); + + /** + * Launches a specified number of instances of an AMI for which you have permissions. + *

+ * + * If Amazon EC2 cannot launch the minimum number AMIs you request, no instances will be + * launched. If there is insufficient capacity to launch the maximum number of AMIs you request, + * Amazon EC2 launches the minimum number specified for each AMI and allocate the remaining + * available instances using round robin. + *

+ *

Security Groups

+ * Note: Every instance is launched in a security group (created using the + * CreateSecurityGroup operation. + *

Key Pair

+ * You can provide an optional key pair ID for each image in the launch request (created using + * the CreateKeyPair operation). All instances that are created from images that use this key + * pair will have access to the associated public key at boot. You can use this key to provide + * secure access to an instance of an image on a per-instance basis. Amazon EC2 public images use + * this feature to provide secure access without passwords. + *

+ * Note: Launching public images without a key pair ID will leave them inaccessible. + *

+ * The public key material is made available to the instance at boot time by placing it in the + * openssh_id.pub file on a logical device that is exposed to the instance as /dev/sda2 (the + * instance store). The format of this file is suitable for use as an entry within + * ~/.ssh/authorized_keys (the OpenSSH format). This can be done at boot (e.g., as part of + * rc.local) allowing for secure access without passwords. + *

User Data

+ * Optional user data can be provided in the launch request. All instances that collectively + * comprise the launch request have access to this data. For more information, go the Amazon + * Elastic Compute Cloud Developer Guide. + *

Product Codes

+ * + * Note: If any of the AMIs have a product code attached for which the user has not + * subscribed, the RunInstances call will fail. + *

Kernel

+ * + * Important: We strongly recommend using the 2.6.18 Xen stock kernel with High-CPU and + * High-Memory instances. Although the default Amazon EC2 kernels will work, the new kernels + * provide greater stability and performance for these instance types. For more information about + * kernels, go the Amazon Elastic Compute Cloud Developer Guide. + * + * @param imageId + * Unique ID of a machine image, returned by a call to + * @param minCount + * Minimum number of instances to launch. If the value is more than Amazon EC2 can + * launch, no instances a re launched at all. Constraints: Between 1 and the maximum + * number allowed for your account (default: 20). + * @param maxCount + * Maximum number of instances to launch. If the value is more than Amazon EC2 can + * launch, the largest possible number above minCount will be launched instead. + * Constraints: Between 1 and the maximum number allowed for your account (default: + * 20). + * @see #describeInstances + * @see #terminateInstances + * @see #authorizeSecurityGroupIngress + * @see #revokeSecurityGroupIngress + * @see #describeSecurityGroups + * @see #createSecurityGroup + * @see #createKeyPair + * @see
+ * @see RunInstancesOptions + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "RunInstances") + @XMLResponseParser(RunInstancesResponseHandler.class) + Future runInstances(@FormParam("ImageId") String imageId, + @FormParam("MinCount") int minCount, @FormParam("MaxCount") int maxCount, + RunInstancesOptions... options); + + /** + * Shuts down one or more instances. This operation is idempotent; if you terminate an instance + * more than once, each call will succeed. + *

+ * Terminated instances will remain visible after termination (approximately one hour). + * + * @param instanceIds + * Instance ID to terminate. + * @see #describeInstances + * @see + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "TerminateInstances") + @XMLResponseParser(TerminateInstancesResponseHandler.class) + Future> terminateInstances( + @FormParam("InstanceId.0") String instanceId, + @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); + + /** + * Creates a new 2048-bit RSA key pair with the specified name. The public key is stored by + * Amazon EC2 and the private key is displayed on the console. The private key is returned as an + * unencrypted PEM encoded PKCS#8 private key. If a key with the specified name already exists, + * Amazon EC2 returns an error. + * + * @see #runInstances + * @see #describeKeyPairs + * @see #deleteKeyPair + * + * @see + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "CreateKeyPair") + @XMLResponseParser(KeyPairResponseHandler.class) + Future createKeyPair(@FormParam("KeyName") String keyName); + + /** + * Returns information about key pairs available to you. If you specify key pairs, information + * about those key pairs is returned. Otherwise, information for all registered key pairs is + * returned. + * + * @param keyPairNames + * Key pairs to describe. + * @see #runInstances + * @see #describeAvailabilityZones + * @see + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DescribeKeyPairs") + @XMLResponseParser(DescribeKeyPairsResponseHandler.class) + Future> describeKeyPairs( + @BinderParam(BindKeyNameToIndexedFormParams.class) String... keyPairNames); + + /** + * Deletes the specified key pair, by removing the public key from Amazon EC2. You must own the + * key pair + * + * @see #describeKeyPairs + * @see #createKeyPair + * + * @see + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DeleteKeyPair") + Future deleteKeyPair(@FormParam("KeyName") String keyName); + + /** + * Creates a new security group. Group names must be unique per account. + * + * @param name + * Name of the security group. Accepts alphanumeric characters, spaces, dashes, and + * underscores. + * @param description + * Description of the group. This is informational only. If the description contains + * spaces, you must enc lose it in single quotes (') or URL-encode it. Accepts + * alphanumeric characters, spaces, dashes, and underscores. + * @see #runInstances + * @see #describeSecurityGroups + * @see #authorizeSecurityGroupIngress + * @see #revokeSecurityGroupIngress + * @see #deleteSecurityGroup + * + * @see + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "CreateSecurityGroup") + Future createSecurityGroup(@FormParam("GroupName") String name, + @FormParam("GroupDescription") String description); + + /** + * Deletes a security group that you own. + * + * @param name + * Name of the security group to delete. + * + * @see #describeSecurityGroups + * @see #authorizeSecurityGroupIngress + * @see #revokeSecurityGroupIngress + * @see #createSecurityGroup + * + * @see + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DeleteSecurityGroup") + @ExceptionParser(ReturnVoidOnGroupNotFound.class) + Future deleteSecurityGroup(@FormParam("GroupName") String name); + + /** + * Returns information about security groups that you own. + * + * @param securityGroupNames + * Name of the security groups + * + * @see #createSecurityGroup + * @see #authorizeSecurityGroupIngress + * @see #revokeSecurityGroupIngress + * @see #deleteSecurityGroup + * + * @see + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DescribeSecurityGroups") + @XMLResponseParser(DescribeSecurityGroupsResponseHandler.class) + Future> describeSecurityGroups( + @BinderParam(BindGroupNameToIndexedFormParams.class) String... securityGroupNames); + + /** + * + * Adds permissions to a security group based on another group. + * + * @param groupName + * Name of the group to modify. The name must be valid and belong to the account + * @param sourceSecurityGroup + * group to associate with this group. + * + * @see #createSecurityGroup + * @see #describeSecurityGroups + * @see #revokeSecurityGroupIngress + * @see #deleteSecurityGroup + * + * @see authorizeSecurityGroupIngress( + @FormParam("GroupName") String groupName, + @BinderParam(BindUserIdGroupPairToSourceSecurityGroupFormParams.class) UserIdGroupPair sourceSecurityGroup); + + /** + * + * Adds permissions to a security group. + *

+ * Permissions are specified by the IP protocol (TCP, UDP or ICMP), the source of the request (by + * IP range or an Amazon EC2 user-group pair), the source and destination port ranges (for TCP + * and UDP), and the ICMP codes and types (for ICMP). When authorizing ICMP, -1 can be used as a + * wildcard in the type and code fields. Permission changes are propagated to instances within + * the security group as quickly as possible. However, depending on the number of instances, a + * small delay might occur. + * + * @param groupName + * Name of the group to modify. The name must be valid and belong to the account + * @param ipProtocol + * IP protocol. + * @param fromPort + * Start of port range for the TCP and UDP protocols, or an ICMP type number. An ICMP + * type number of -1 indicates a wildcard (i.e., any ICMP type number). + * @param toPort + * End of port range for the TCP and UDP protocols, or an ICMP code. An ICMP code of -1 + * indicates a wildcard (i.e., any ICMP code). + * @param cidrIp + * CIDR range. + * + * @see #createSecurityGroup + * @see #describeSecurityGroups + * @see #revokeSecurityGroupIngress + * @see #deleteSecurityGroup + * + * @see authorizeSecurityGroupIngress(@FormParam("GroupName") String groupName, + @FormParam("IpProtocol") IpProtocol ipProtocol, @FormParam("FromPort") int fromPort, + @FormParam("ToPort") int toPort, @FormParam("CidrIp") String cidrIp); + + /** + * + * Revokes permissions from a security group. The permissions used to revoke must be specified + * using the same values used to grant the permissions. + * + * @param groupName + * Name of the group to modify. The name must be valid and belong to the account + * @param sourceSecurityGroup + * group to associate with this group. + * + * @see #createSecurityGroup + * @see #describeSecurityGroups + * @see #authorizeSecurityGroupIngress + * @see #deleteSecurityGroup + * + * @see revokeSecurityGroupIngress( + @FormParam("GroupName") String groupName, + @BinderParam(BindUserIdGroupPairToSourceSecurityGroupFormParams.class) UserIdGroupPair sourceSecurityGroup); + + /** + * + * Revokes permissions from a security group. The permissions used to revoke must be specified + * using the same values used to grant the permissions. + *

+ * Permissions are specified by IP protocol (TCP, UDP, or ICMP), the source of the request (by IP + * range or an Amazon EC2 user-group pair), the source and destination port ranges (for TCP and + * UDP), and the ICMP codes and types (for ICMP). + * + * Permission changes are quickly propagated to instances within the security group. However, + * depending on the number of instances in the group, a small delay is might occur. + * + * @param groupName + * Name of the group to modify. The name must be valid and belong to the account + * @param ipProtocol + * IP protocol. + * @param fromPort + * Start of port range for the TCP and UDP protocols, or an ICMP type number. An ICMP + * type number of -1 indicates a wildcard (i.e., any ICMP type number). + * @param toPort + * End of port range for the TCP and UDP protocols, or an ICMP code. An ICMP code of -1 + * indicates a wildcard (i.e., any ICMP code). + * @param cidrIp + * CIDR range. + * + * @see #createSecurityGroup + * @see #describeSecurityGroups + * @see #authorizeSecurityGroupIngress + * @see #deleteSecurityGroup + * + * @see revokeSecurityGroupIngress(@FormParam("GroupName") String groupName, + @FormParam("IpProtocol") IpProtocol ipProtocol, @FormParam("FromPort") int fromPort, + @FormParam("ToPort") int toPort, @FormParam("CidrIp") String cidrIp); +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/EC2ContextBuilder.java b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2ContextBuilder.java new file mode 100755 index 0000000000..8b18996827 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2ContextBuilder.java @@ -0,0 +1,62 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID; +import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY; + +import java.util.List; +import java.util.Properties; + +import org.jclouds.aws.ec2.config.EC2ContextModule; +import org.jclouds.aws.ec2.config.EC2RestClientModule; +import org.jclouds.rest.RestContextBuilder; + +import com.google.inject.Module; +import com.google.inject.TypeLiteral; + +/** + * + * @author Adrian Cole + */ +public class EC2ContextBuilder extends RestContextBuilder { + + public EC2ContextBuilder(Properties props) { + super(new TypeLiteral() { + }, props); + checkNotNull(properties.getProperty(PROPERTY_AWS_ACCESSKEYID)); + checkNotNull(properties.getProperty(PROPERTY_AWS_SECRETACCESSKEY)); + } + + protected void addClientModule(List modules) { + modules.add(new EC2RestClientModule()); + } + + @Override + protected void addContextModule(List modules) { + modules.add(new EC2ContextModule()); + } + +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/EC2ContextFactory.java b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2ContextFactory.java new file mode 100644 index 0000000000..ea343229a4 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2ContextFactory.java @@ -0,0 +1,66 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + +import java.net.URI; +import java.util.Properties; + +import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; +import org.jclouds.logging.jdk.config.JDKLoggingModule; +import org.jclouds.rest.RestContext; + +import com.google.inject.Module; + +/** + * Creates {@link RestContext} instances bound with generic type {@link EC2Client} based on the most + * commonly requested arguments. + *

+ * Note that Threadsafe objects will be bound as singletons to the Injector or Context provided. + *

+ *

+ * If no Modules are specified, the default {@link JDKLoggingModule logging} and + * {@link JavaUrlHttpCommandExecutorServiceModule http transports} will be installed. + * + * @author Adrian Cole + * @see RestContext + */ +public class EC2ContextFactory { + public static RestContext createContext(Properties properties, Module... modules) { + return new EC2ContextBuilder(new EC2PropertiesBuilder(properties).build()).withModules( + modules).buildContext(); + } + + public static RestContext createContext(String awsAccessKeyId, + String awsSecretAccessKey, Module... modules) { + return new EC2ContextBuilder(new EC2PropertiesBuilder(awsAccessKeyId, awsSecretAccessKey) + .build()).withModules(modules).buildContext(); + } + + public static RestContext createContext(URI endpoint, String awsAccessKeyId, + String awsSecretAccessKey, Module... modules) { + return new EC2ContextBuilder(new EC2PropertiesBuilder(awsAccessKeyId, awsSecretAccessKey) + .withEndpoint(endpoint).build()).withModules(modules).buildContext(); + } + +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/EC2PropertiesBuilder.java b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2PropertiesBuilder.java new file mode 100644 index 0000000000..46e9e30d9e --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2PropertiesBuilder.java @@ -0,0 +1,76 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.aws.ec2.reference.EC2Constants.PROPERTY_EC2_ENDPOINT; +import static org.jclouds.aws.ec2.reference.EC2Constants.PROPERTY_EC2_EXPIREINTERVAL; +import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID; +import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY; + +import java.net.URI; +import java.util.Properties; + +import org.jclouds.http.HttpPropertiesBuilder; + +/** + * Builds properties used in EC2 Clients + * + * @author Adrian Cole + */ +public class EC2PropertiesBuilder extends HttpPropertiesBuilder { + @Override + protected Properties defaultProperties() { + Properties properties = super.defaultProperties(); + properties.setProperty(PROPERTY_EC2_ENDPOINT, "http://ec2.amazonaws.com"); + properties.setProperty(PROPERTY_EC2_EXPIREINTERVAL, "60"); + return properties; + } + + public EC2PropertiesBuilder(Properties properties) { + super(properties); + } + + public EC2PropertiesBuilder(String id, String secret) { + super(); + withCredentials(id, secret); + } + + public EC2PropertiesBuilder withCredentials(String id, String secret) { + properties.setProperty(PROPERTY_AWS_ACCESSKEYID, checkNotNull(id, "awsAccessKeyId")); + properties.setProperty(PROPERTY_AWS_SECRETACCESSKEY, checkNotNull(secret, + "awsSecretAccessKey")); + return this; + } + + public EC2PropertiesBuilder withEndpoint(URI endpoint) { + properties.setProperty(PROPERTY_EC2_ENDPOINT, checkNotNull(endpoint, "endpoint").toString()); + return this; + } + + public EC2PropertiesBuilder withRequestExpiration(long seconds) { + properties.setProperty(PROPERTY_EC2_EXPIREINTERVAL, seconds + ""); + return this; + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindGroupNameToIndexedFormParams.java b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindGroupNameToIndexedFormParams.java new file mode 100644 index 0000000000..a3df128c4f --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindGroupNameToIndexedFormParams.java @@ -0,0 +1,26 @@ +package org.jclouds.aws.ec2.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.aws.ec2.util.EC2Utils; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +/** + * Binds the String [] to query parameters named with GroupName.index + * + * @author Adrian Cole + * @since 4.0 + */ +public class BindGroupNameToIndexedFormParams implements Binder { + + @SuppressWarnings("unchecked") + public void bindToRequest(HttpRequest request, Object input) { + checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + EC2Utils.indexFormValuesWithPrefix((GeneratedHttpRequest) request, "GroupName", input); + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindInstanceIdsToIndexedFormParams.java b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindInstanceIdsToIndexedFormParams.java new file mode 100644 index 0000000000..df2c10e43c --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindInstanceIdsToIndexedFormParams.java @@ -0,0 +1,26 @@ +package org.jclouds.aws.ec2.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.aws.ec2.util.EC2Utils; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +/** + * Binds the String [] to form parameters named with InstanceId.index + * + * @author Adrian Cole + * @since 4.0 + */ +public class BindInstanceIdsToIndexedFormParams implements Binder { + + @SuppressWarnings("unchecked") + public void bindToRequest(HttpRequest request, Object input) { + checkArgument(checkNotNull(request, "input") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + EC2Utils.indexFormValuesWithPrefix((GeneratedHttpRequest) request, "InstanceId", input); + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindKeyNameToIndexedFormParams.java b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindKeyNameToIndexedFormParams.java new file mode 100644 index 0000000000..3734921ca4 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindKeyNameToIndexedFormParams.java @@ -0,0 +1,26 @@ +package org.jclouds.aws.ec2.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.aws.ec2.util.EC2Utils; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +/** + * Binds the String [] to query parameters named with KeyName.index + * + * @author Adrian Cole + * @since 4.0 + */ +public class BindKeyNameToIndexedFormParams implements Binder { + + @SuppressWarnings("unchecked") + public void bindToRequest(HttpRequest request, Object input) { + checkArgument(checkNotNull(request, "input") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + EC2Utils.indexFormValuesWithPrefix((GeneratedHttpRequest) request, "KeyName", input); + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindUserIdGroupPairToSourceSecurityGroupFormParams.java b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindUserIdGroupPairToSourceSecurityGroupFormParams.java new file mode 100644 index 0000000000..92a73cf2e0 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindUserIdGroupPairToSourceSecurityGroupFormParams.java @@ -0,0 +1,30 @@ +package org.jclouds.aws.ec2.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.aws.ec2.domain.UserIdGroupPair; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +/** + * Binds the String [] to query parameters named with GroupName.index + * + * @author Adrian Cole + * @since 4.0 + */ +public class BindUserIdGroupPairToSourceSecurityGroupFormParams implements Binder { + + @SuppressWarnings("unchecked") + public void bindToRequest(HttpRequest request, Object input) { + checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + checkArgument(checkNotNull(input, "input") instanceof UserIdGroupPair, + "this binder is only valid for UserIdGroupPair!"); + UserIdGroupPair pair = (UserIdGroupPair) input; + GeneratedHttpRequest generatedRequest = (GeneratedHttpRequest) request; + generatedRequest.addFormParam("SourceSecurityGroupOwnerId", pair.getUserId()); + generatedRequest.addFormParam("SourceSecurityGroupName", pair.getGroupName()); + } +} \ No newline at end of file diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/internal/GuiceEC2Context.java b/aws/core/src/main/java/org/jclouds/aws/ec2/config/EC2ContextModule.java similarity index 51% rename from aws/ec2/core/src/main/java/org/jclouds/aws/ec2/internal/GuiceEC2Context.java rename to aws/core/src/main/java/org/jclouds/aws/ec2/config/EC2ContextModule.java index bda92af15d..d4631c4c71 100644 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/internal/GuiceEC2Context.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/config/EC2ContextModule.java @@ -21,57 +21,41 @@ * under the License. * ==================================================================== */ -package org.jclouds.aws.ec2.internal; +package org.jclouds.aws.ec2.config; -import java.io.IOException; +import java.net.URI; -import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; -import org.jclouds.aws.ec2.EC2Connection; -import org.jclouds.aws.ec2.EC2Context; +import org.jclouds.aws.ec2.EC2; +import org.jclouds.aws.ec2.EC2Client; +import org.jclouds.aws.reference.AWSConstants; +import org.jclouds.http.functions.config.ParserModule.CDateTimeAdapter; +import org.jclouds.http.functions.config.ParserModule.DateTimeAdapter; import org.jclouds.lifecycle.Closer; -import org.jclouds.logging.Logger; +import org.jclouds.rest.RestContext; +import org.jclouds.rest.internal.RestContextImpl; -import javax.inject.Inject; -import com.google.inject.Injector; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; /** - * Uses a Guice Injector to configure the objects served by EC2Context methods. + * Configures the EC2 connection, including logging and http transport. * * @author Adrian Cole - * @see Injector */ -public class GuiceEC2Context implements EC2Context { - - @Resource - private Logger logger = Logger.NULL; - private final Injector injector; - private final Closer closer; - - @Inject - private GuiceEC2Context(Injector injector, Closer closer) { - this.injector = injector; - this.closer = closer; +public class EC2ContextModule extends AbstractModule { + @Override + protected void configure() { + bind(DateTimeAdapter.class).to(CDateTimeAdapter.class); } - /** - * {@inheritDoc} - */ - public EC2Connection getConnection() { - return injector.getInstance(EC2Connection.class); + @Provides + @Singleton + RestContext provideContext(Closer closer, EC2Client defaultApi, @EC2 URI endPoint, + @Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String account) { + return new RestContextImpl(closer, defaultApi, endPoint, account); } - /** - * {@inheritDoc} - * - * @see Closer - */ - public void close() { - try { - closer.close(); - } catch (IOException e) { - logger.error(e, "error closing content"); - } - } - -} +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/config/EC2RestClientModule.java b/aws/core/src/main/java/org/jclouds/aws/ec2/config/EC2RestClientModule.java new file mode 100755 index 0000000000..71485bbaf3 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/config/EC2RestClientModule.java @@ -0,0 +1,110 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.config; + +import java.net.URI; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.EC2; +import org.jclouds.aws.ec2.EC2Client; +import org.jclouds.aws.ec2.filters.FormSigner; +import org.jclouds.aws.ec2.reference.EC2Constants; +import org.jclouds.aws.handlers.AWSClientErrorRetryHandler; +import org.jclouds.aws.handlers.AWSRedirectionRetryHandler; +import org.jclouds.aws.handlers.ParseAWSErrorFromXmlContent; +import org.jclouds.aws.util.RequestSigner; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpRetryHandler; +import org.jclouds.http.RequiresHttp; +import org.jclouds.http.annotation.ClientError; +import org.jclouds.http.annotation.Redirection; +import org.jclouds.http.annotation.ServerError; +import org.jclouds.rest.ConfiguresRestClient; +import org.jclouds.rest.RestClientFactory; +import org.jclouds.util.DateService; +import org.jclouds.util.TimeStamp; +import org.joda.time.DateTime; + +import com.google.inject.AbstractModule; +import com.google.inject.Provides; + +/** + * Configures the EC2 connection. + * + * @author Adrian Cole + */ +@RequiresHttp +@ConfiguresRestClient +public class EC2RestClientModule extends AbstractModule { + + @Override + protected void configure() { + bindErrorHandlers(); + bindRetryHandlers(); + } + + @Provides + @TimeStamp + protected String provideTimeStamp(final DateService dateService, + @Named(EC2Constants.PROPERTY_EC2_EXPIREINTERVAL) final int expiration) { + return dateService.iso8601DateFormat(new DateTime().plusSeconds(expiration)); + } + + @Provides + @Singleton + RequestSigner provideRequestSigner(FormSigner in) { + return in; + } + + @Provides + @Singleton + protected EC2Client provideClient(RestClientFactory factory) { + return factory.create(EC2Client.class); + } + + @Provides + @Singleton + @EC2 + protected URI provideURI(@Named(EC2Constants.PROPERTY_EC2_ENDPOINT) String endpoint) { + return URI.create(endpoint); + } + + protected void bindErrorHandlers() { + bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to( + ParseAWSErrorFromXmlContent.class); + bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to( + ParseAWSErrorFromXmlContent.class); + bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to( + ParseAWSErrorFromXmlContent.class); + } + + protected void bindRetryHandlers() { + bind(HttpRetryHandler.class).annotatedWith(Redirection.class).to( + AWSRedirectionRetryHandler.class); + bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to( + AWSClientErrorRetryHandler.class); + } +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/Image.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/Image.java new file mode 100644 index 0000000000..a048baa0fc --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/Image.java @@ -0,0 +1,262 @@ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Set; + +import com.google.inject.internal.Nullable; + +/** + * + * @see + * @author Adrian Cole + */ +public class Image implements Comparable { + + public Image(Architecture architecture, String imageId, String imageLocation, + String imageOwnerId, ImageState imageState, ImageType imageType, boolean isPublic, + @Nullable String kernelId, @Nullable String platform, Set productCodes, + @Nullable String ramdiskId) { + this.architecture = checkNotNull(architecture, "architecture"); + this.imageId = checkNotNull(imageId, "imageId"); + this.imageLocation = checkNotNull(imageLocation, "imageLocation"); + this.imageOwnerId = checkNotNull(imageOwnerId, "imageOwnerId"); + this.imageState = checkNotNull(imageState, "imageState"); + this.imageType = checkNotNull(imageType, "imageType"); + this.isPublic = isPublic; + this.kernelId = kernelId; + this.platform = platform; + this.productCodes = checkNotNull(productCodes, "productCodes"); + this.ramdiskId = ramdiskId; + } + + /** The serialVersionUID */ + private static final long serialVersionUID = -6965068835316857535L; + + public enum ImageState { + /** + * the image is successfully registered and available for launching + */ + AVAILABLE, + /** + * the image is deregistered and no longer available for launching + */ + DEREGISTERED; + public String value() { + return name().toLowerCase(); + } + + public static ImageState fromValue(String v) { + return valueOf(v.toUpperCase()); + } + } + + public enum Architecture { + I386, X86_64; + public String value() { + return name().toLowerCase(); + } + + public static Architecture fromValue(String v) { + return valueOf(v.toUpperCase()); + } + } + + public enum ImageType { + + MACHINE, KERNEL, RAMDISK; + public String value() { + return name().toLowerCase(); + } + + public static ImageType fromValue(String v) { + return valueOf(v.toUpperCase()); + } + + } + + private final Architecture architecture; + private final String imageId; + private final String imageLocation; + private final String imageOwnerId; + private final ImageState imageState; + private final ImageType imageType; + private final boolean isPublic; + private final @Nullable + String kernelId; + private final @Nullable String platform; + private final Set productCodes; + private final @Nullable + String ramdiskId; + + /** + * The architecture of the image (i386 or x86_64). + */ + public Architecture getArchitecture() { + return architecture; + } + + /** + * The ID of the AMI. + */ + public String getImageId() { + return imageId; + } + + /** + * The location of the AMI. + */ + public String getImageLocation() { + return imageLocation; + } + + /** + * AWS Access Key ID of the image owner. + */ + public String getImageOwnerId() { + return imageOwnerId; + } + + /** + * Current state of the AMI. If the operation returns available, the image is successfully + * registered and avail able for launching. If the operation returns deregistered, the image is + * deregistered and no longer available for launching. + */ + public ImageState getImageState() { + return imageState; + } + + /** + * The type of image (machine, kernel, or ramdisk). + */ + public ImageType getImageType() { + return imageType; + } + + /** + * Returns true if this image has public launch permissions. Returns false if it only has + * implicit and explicit launch permissions. + */ + public boolean isPublic() { + return isPublic; + } + + /** + * The kernel associated with the image, if any. Only applicable for machine images. + */ + public String getKernelId() { + return kernelId; + } + + /** + * The operating platform of the instance. + */ + public String getPlatform() { + return platform; + } + + /** + * Product codes of the AMI. + */ + public Set getProductCodes() { + return productCodes; + } + + /** + * The RAM disk associated with the image, if any. Only applicable for machine images. + */ + public String getRamdiskId() { + return ramdiskId; + } + + /** + * {@inheritDoc} + */ + public int compareTo(Image o) { + return (this == o) ? 0 : getImageId().compareTo(o.getImageId()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((architecture == null) ? 0 : architecture.hashCode()); + result = prime * result + ((imageId == null) ? 0 : imageId.hashCode()); + result = prime * result + ((imageLocation == null) ? 0 : imageLocation.hashCode()); + result = prime * result + ((imageOwnerId == null) ? 0 : imageOwnerId.hashCode()); + result = prime * result + ((imageState == null) ? 0 : imageState.hashCode()); + result = prime * result + ((imageType == null) ? 0 : imageType.hashCode()); + result = prime * result + (isPublic ? 1231 : 1237); + result = prime * result + ((kernelId == null) ? 0 : kernelId.hashCode()); + result = prime * result + ((platform == null) ? 0 : platform.hashCode()); + result = prime * result + ((productCodes == null) ? 0 : productCodes.hashCode()); + result = prime * result + ((ramdiskId == null) ? 0 : ramdiskId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Image other = (Image) obj; + if (architecture == null) { + if (other.architecture != null) + return false; + } else if (!architecture.equals(other.architecture)) + return false; + if (imageId == null) { + if (other.imageId != null) + return false; + } else if (!imageId.equals(other.imageId)) + return false; + if (imageLocation == null) { + if (other.imageLocation != null) + return false; + } else if (!imageLocation.equals(other.imageLocation)) + return false; + if (imageOwnerId == null) { + if (other.imageOwnerId != null) + return false; + } else if (!imageOwnerId.equals(other.imageOwnerId)) + return false; + if (imageState == null) { + if (other.imageState != null) + return false; + } else if (!imageState.equals(other.imageState)) + return false; + if (imageType == null) { + if (other.imageType != null) + return false; + } else if (!imageType.equals(other.imageType)) + return false; + if (isPublic != other.isPublic) + return false; + if (kernelId == null) { + if (other.kernelId != null) + return false; + } else if (!kernelId.equals(other.kernelId)) + return false; + if (platform == null) { + if (other.platform != null) + return false; + } else if (!platform.equals(other.platform)) + return false; + if (productCodes == null) { + if (other.productCodes != null) + return false; + } else if (!productCodes.equals(other.productCodes)) + return false; + if (ramdiskId == null) { + if (other.ramdiskId != null) + return false; + } else if (!ramdiskId.equals(other.ramdiskId)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/InstanceState.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/InstanceState.java new file mode 100644 index 0000000000..2e7be68469 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/InstanceState.java @@ -0,0 +1,65 @@ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.aws.ec2.EC2Client; + +/** + * + * The current state of the instance.. + * + * @author Adrian Cole + * @see EC2Client#describeInstances + * @see EC2Client#runInstances + * @see EC2Client#terminateInstances + * + */ +public enum InstanceState { + + /** + * the instance is in the process of being launched + */ + PENDING, + + /** + * the instance launched (although the boot process might not be completed) + */ + RUNNING, + + /** + * the instance started shutting down + */ + SHUTTING_DOWN, + /** + * the instance terminated + */ + TERMINATED; + + public String value() { + return name().toLowerCase().replaceAll("_", "-"); + } + + @Override + public String toString() { + return value(); + } + + public static InstanceState fromValue(String state) { + return valueOf(checkNotNull(state, "state").replaceAll("-", "_").toUpperCase()); + } + + public static InstanceState fromValue(int v) { + switch (v) { + case 0: + return PENDING; + case 16: + return RUNNING; + case 32: + return SHUTTING_DOWN; + case 48: + return TERMINATED; + default: + throw new IllegalArgumentException("invalid state:" + v); + } + } +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/InstanceType.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/InstanceType.java new file mode 100644 index 0000000000..f19a228a53 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/InstanceType.java @@ -0,0 +1,108 @@ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.aws.ec2.EC2Client; + +/** + * + * The type of the instance. Description accurate as of 8-15-2009 release. + * + * @author Adrian Cole + * @see EC2Client#describeInstances + * @see EC2Client#runInstances + * @see EC2Client#terminateInstances + * + */ +public enum InstanceType { + /** + * Small Instance + *

+ */ + M1_SMALL, + /** + * Large Instance + *
    + *
  • 7.5 GB memory
  • + *
  • 4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute Units each)
  • + *
  • 850 GB instance storage (2x420 GB plus 10 GB root partition)
  • + *
  • 64-bit platform
  • + *
  • I/O Performance: High
  • + *
+ */ + M1_LARGE, + /** + * Extra Large Instance + *
    + *
  • 15 GB memory
  • + *
  • 8 EC2 Compute Units (4 virtual cores with 2 EC2 Compute Units each)
  • + *
  • 1690 GB instance storage (4x420 GB plus 10 GB root partition)
  • + *
  • 64-bit platform
  • + *
  • I/O Performance: High
  • + *
+ */ + M1_XLARGE, + /** + * High-Memory Double Extra Large Instance + *
    + *
  • 34.2 GB of memory
  • + *
  • 13 EC2 Compute Units (4 virtual cores with 3.25 EC2 Compute Units each)
  • + *
  • 850 GB of instance storage
  • + *
  • 64-bit platform
  • + *
  • I/O Performance: High
  • + *
+ */ + M2_2XLARGE, + /** + * High-Memory Quadruple Extra Large Instance + *
    + *
  • 68.4 GB of memory
  • + *
  • 26 EC2 Compute Units (8 virtual cores with 3.25 EC2 Compute Units each)
  • + *
  • 1690 GB of instance storage
  • + *
  • 64-bit platform
  • + *
  • I/O Performance: High
  • + *
+ */ + M2_4XLARGE, + /** + * High-CPU Medium Instance + *
    + *
  • 1.7 GB of memory
  • + *
  • 5 EC2 Compute Units (2 virtual cores with 2.5 EC2 Compute Units each)
  • + *
  • 350 GB of instance storage
  • + *
  • 32-bit platform
  • + *
  • I/O Performance: Moderate
  • + *
+ */ + C1_MEDIUM, + /** + * High-CPU Extra Large Instance + *
    + *
  • 7 GB of memory
  • + *
  • 20 EC2 Compute Units (8 virtual cores with 2.5 EC2 Compute Units each)
  • + *
  • 1690 GB of instance storage
  • + *
  • 64-bit platform
  • + *
  • I/O Performance: High
  • + *
+ */ + C1_XLARGE; + public String value() { + return name().toLowerCase().replaceAll("_", "."); + } + + @Override + public String toString() { + return value(); + } + + public static InstanceType fromValue(String type) { + return valueOf(checkNotNull(type, "type").replaceAll("\\.", "_").toUpperCase()); + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/IpPermission.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/IpPermission.java new file mode 100644 index 0000000000..86021a23be --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/IpPermission.java @@ -0,0 +1,114 @@ +package org.jclouds.aws.ec2.domain; + +import java.util.SortedSet; + +/** + * + * @see
+ * @author Adrian Cole + */ +public class IpPermission implements Comparable { + private final int fromPort; + private final int toPort; + private final SortedSet groups; + private final IpProtocol ipProtocol; + private final SortedSet ipRanges; + + public IpPermission(int fromPort, int toPort, SortedSet groups, + IpProtocol ipProtocol, SortedSet ipRanges) { + this.fromPort = fromPort; + this.toPort = toPort; + this.groups = groups; + this.ipProtocol = ipProtocol; + this.ipRanges = ipRanges; + } + + /** + * {@inheritDoc} + */ + public int compareTo(IpPermission o) { + return (this == o) ? 0 : getIpProtocol().compareTo(o.getIpProtocol()); + } + + /** + * Start of port range for the TCP and UDP protocols, or an ICMP type number. An ICMP type number + * of -1 indicates a wildcard (i.e., any ICMP type number). + */ + public int getFromPort() { + return fromPort; + } + + /** + * End of port range for the TCP and UDP protocols, or an ICMP code. An ICMP code of -1 indicates + * a wildcard (i.e., any ICMP code). + */ + public int getToPort() { + return toPort; + } + + /** + * List of security group and user ID pairs. + */ + public SortedSet getGroups() { + return groups; + } + + /** + * IP protocol + */ + public IpProtocol getIpProtocol() { + return ipProtocol; + } + + /** + * IP ranges. + */ + public SortedSet getIpRanges() { + return ipRanges; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + fromPort; + result = prime * result + ((groups == null) ? 0 : groups.hashCode()); + result = prime * result + ((ipProtocol == null) ? 0 : ipProtocol.hashCode()); + result = prime * result + ((ipRanges == null) ? 0 : ipRanges.hashCode()); + result = prime * result + toPort; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IpPermission other = (IpPermission) obj; + if (fromPort != other.fromPort) + return false; + if (groups == null) { + if (other.groups != null) + return false; + } else if (!groups.equals(other.groups)) + return false; + if (ipProtocol == null) { + if (other.ipProtocol != null) + return false; + } else if (!ipProtocol.equals(other.ipProtocol)) + return false; + if (ipRanges == null) { + if (other.ipRanges != null) + return false; + } else if (!ipRanges.equals(other.ipRanges)) + return false; + if (toPort != other.toPort) + return false; + return true; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/IpProtocol.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/IpProtocol.java new file mode 100644 index 0000000000..673104e0ca --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/IpProtocol.java @@ -0,0 +1,26 @@ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author Adrian Cole + * + */ +public enum IpProtocol { + + TCP, UDP, ICMP; + + public String value() { + return name().toLowerCase(); + } + + @Override + public String toString() { + return value(); + } + + public static IpProtocol fromValue(String protocol) { + return valueOf(checkNotNull(protocol, "protocol").toUpperCase()); + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/KeyPair.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/KeyPair.java new file mode 100644 index 0000000000..b129614ec2 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/KeyPair.java @@ -0,0 +1,91 @@ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.inject.internal.Nullable; + +/** + * + * @see + * @author Adrian Cole + */ +public class KeyPair implements Comparable { + private final String keyName; + private final String keyFingerprint; + @Nullable + private final String keyMaterial; + + public KeyPair(String keyName, String keyFingerprint, @Nullable String keyMaterial) { + this.keyName = checkNotNull(keyName, "keyName"); + this.keyFingerprint = checkNotNull(keyFingerprint, "keyFingerprint"); + this.keyMaterial = keyMaterial;// nullable on list + } + + /** + * {@inheritDoc} + */ + public int compareTo(KeyPair o) { + return (this == o) ? 0 : getKeyName().compareTo(o.getKeyName()); + } + + /** + * A SHA-1 digest of the DER encoded private key. + */ + public String getKeyFingerprint() { + return keyFingerprint; + } + + /** + * An unencrypted PEM encoded RSA private key. + */ + public String getKeyMaterial() { + return keyMaterial; + } + + /** + * The key pair name provided in the original request. + */ + public String getKeyName() { + return keyName; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((keyFingerprint == null) ? 0 : keyFingerprint.hashCode()); + result = prime * result + ((keyMaterial == null) ? 0 : keyMaterial.hashCode()); + result = prime * result + ((keyName == null) ? 0 : keyName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + KeyPair other = (KeyPair) obj; + if (keyFingerprint == null) { + if (other.keyFingerprint != null) + return false; + } else if (!keyFingerprint.equals(other.keyFingerprint)) + return false; + if (keyMaterial == null) { + if (other.keyMaterial != null) + return false; + } else if (!keyMaterial.equals(other.keyMaterial)) + return false; + if (keyName == null) { + if (other.keyName != null) + return false; + } else if (!keyName.equals(other.keyName)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/Reservation.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/Reservation.java new file mode 100644 index 0000000000..e5714e87d5 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/Reservation.java @@ -0,0 +1,119 @@ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.SortedSet; + +import com.google.inject.internal.Nullable; + +/** + * + * @see + * @author Adrian Cole + */ +public class Reservation implements Comparable { + public Reservation(SortedSet groupIds, SortedSet instances, + @Nullable String ownerId, @Nullable String requesterId, @Nullable String reservationId) { + this.groupIds = checkNotNull(groupIds, "groupIds"); + this.instances = checkNotNull(instances, "instances"); + this.ownerId = ownerId; + this.requesterId = requesterId; + this.reservationId = reservationId; + } + + private final SortedSet groupIds; + private final SortedSet instances; + private final @Nullable + String ownerId; + private final @Nullable + String requesterId; + private final @Nullable + String reservationId; + + public int compareTo(Reservation o) { + return (this == o) ? 0 : getReservationId().compareTo(o.getReservationId()); + } + + /** + * Names of the security groups. + */ + public SortedSet getGroupIds() { + return groupIds; + } + + public SortedSet getRunningInstances() { + return instances; + } + + /** + * AWS Access Key ID of the user who owns the reservation. + */ + public String getOwnerId() { + return ownerId; + } + + /** + * ID of the requester. + */ + public String getRequesterId() { + return requesterId; + } + + /** + * Unique ID of the reservation. + */ + public String getReservationId() { + return reservationId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((groupIds == null) ? 0 : groupIds.hashCode()); + result = prime * result + ((instances == null) ? 0 : instances.hashCode()); + result = prime * result + ((ownerId == null) ? 0 : ownerId.hashCode()); + result = prime * result + ((requesterId == null) ? 0 : requesterId.hashCode()); + result = prime * result + ((reservationId == null) ? 0 : reservationId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Reservation other = (Reservation) obj; + if (groupIds == null) { + if (other.groupIds != null) + return false; + } else if (!groupIds.equals(other.groupIds)) + return false; + if (instances == null) { + if (other.instances != null) + return false; + } else if (!instances.equals(other.instances)) + return false; + if (ownerId == null) { + if (other.ownerId != null) + return false; + } else if (!ownerId.equals(other.ownerId)) + return false; + if (requesterId == null) { + if (other.requesterId != null) + return false; + } else if (!requesterId.equals(other.requesterId)) + return false; + if (reservationId == null) { + if (other.reservationId != null) + return false; + } else if (!reservationId.equals(other.reservationId)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/RunningInstance.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/RunningInstance.java new file mode 100644 index 0000000000..8a52fe8141 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/RunningInstance.java @@ -0,0 +1,367 @@ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.InetAddress; +import java.util.Set; + +import org.joda.time.DateTime; + +import com.google.inject.internal.Nullable; + +/** + * + * @see + * @author Adrian Cole + */ +public class RunningInstance implements Comparable { + private final String amiLaunchIndex; + private final @Nullable + String dnsName; + private final String imageId; + private final String instanceId; + private final InstanceState instanceState; + private final InstanceType instanceType; + private final @Nullable + InetAddress ipAddress; + private final @Nullable + String kernelId; + private final @Nullable + String keyName; + private final DateTime launchTime; + private final boolean monitoring; + private final String availabilityZone; + private final @Nullable + String platform; + private final @Nullable + String privateDnsName; + private final @Nullable + InetAddress privateIpAddress; + private final Set productCodes; + private final @Nullable + String ramdiskId; + private final @Nullable + String reason; + private final @Nullable + String subnetId; + private final @Nullable + String vpcId; + + public int compareTo(RunningInstance o) { + return (this == o) ? 0 : getInstanceId().compareTo(o.getInstanceId()); + } + + public RunningInstance(String amiLaunchIndex, @Nullable String dnsName, String imageId, + String instanceId, InstanceState instanceState, InstanceType instanceType, + @Nullable InetAddress ipAddress, @Nullable String kernelId, @Nullable String keyName, + DateTime launchTime, boolean monitoring, String availabilityZone, + @Nullable String platform, @Nullable String privateDnsName, + @Nullable InetAddress privateIpAddress, Set productCodes, + @Nullable String ramdiskId, @Nullable String reason, @Nullable String subnetId, + @Nullable String vpcId) { + this.amiLaunchIndex = checkNotNull(amiLaunchIndex, "amiLaunchIndex"); + this.dnsName = dnsName; // nullable on runinstances. + this.imageId = checkNotNull(imageId, "imageId"); + this.instanceId = checkNotNull(instanceId, "instanceId"); + this.instanceState = checkNotNull(instanceState, "instanceState"); + this.instanceType = checkNotNull(instanceType, "instanceType"); + this.ipAddress = ipAddress; + this.kernelId = kernelId; + this.keyName = keyName; + this.launchTime = checkNotNull(launchTime, "launchTime"); + this.monitoring = checkNotNull(monitoring, "monitoring"); + this.availabilityZone = checkNotNull(availabilityZone, "availabilityZone"); + this.platform = platform; + this.privateDnsName = privateDnsName; // nullable on runinstances. + this.privateIpAddress = privateIpAddress; + this.productCodes = checkNotNull(productCodes, "productCodes"); + this.ramdiskId = ramdiskId; + this.reason = reason; + this.subnetId = subnetId; + this.vpcId = vpcId; + } + + /** + * The AMI launch index, which can be used to find this instance within the launch group. For + * more information, go to the Metadata section of the Amazon Elastic Compute Cloud Developer + * Guide. + * + * @see + */ + public String getAmiLaunchIndex() { + return amiLaunchIndex; + } + + /** + * The public DNS name assigned to the instance. This DNS name is contactable from outside the + * Amazon EC2 network. This element remains empty until the instance enters a running state. + */ + public String getDnsName() { + return dnsName; + } + + /** + * Image ID of the AMI used to launch the instance. + */ + public String getImageId() { + return imageId; + } + + /** + * Unique ID of the instance launched. + */ + public String getInstanceId() { + return instanceId; + } + + /** + * The current state of the instance. + */ + public InstanceState getInstanceState() { + return instanceState; + } + + /** + * The instance type. + */ + public InstanceType getInstanceType() { + return instanceType; + } + + /** + * Specifies the IP address of the instance. + */ + public InetAddress getIpAddress() { + return ipAddress; + } + + /** + * Optional. Kernel associated with this instance. + */ + public String getKernelId() { + return kernelId; + } + + /** + * If this instance was launched with an associated key pair, this displays the key pair name. + */ + public String getKeyName() { + return keyName; + } + + /** + * The time the instance launched. + */ + public DateTime getLaunchTime() { + return launchTime; + } + + /** + * Specifies whether monitoring is enabled for the instance. + */ + public boolean isMonitoring() { + return monitoring; + } + + /** + * The location where the instance launched. + */ + public String getAvailabilityZone() { + return availabilityZone; + } + + /** + * Platform of the instance (e.g., Windows). + */ + public String getPlatform() { + return platform; + } + + /** + * The private DNS name assigned to the instance. This DNS name can only be used inside the + * Amazon EC2 network. This element remains empty until the instance enters a running state. + */ + public String getPrivateDnsName() { + return privateDnsName; + } + + /** + * Specifies the private IP address that is assigned to the instance (Amazon VPC). + */ + public InetAddress getPrivateIpAddress() { + return privateIpAddress; + } + + /** + * Product codes attached to this instance. + */ + public Set getProductCodes() { + return productCodes; + } + + /** + * Optional. RAM disk associated with this instance. + */ + public String getRamdiskId() { + return ramdiskId; + } + + /** + * Reason for the most recent state transition. This might be an empty string. + */ + public String getReason() { + return reason; + } + + /** + * Specifies the subnet ID in which the instance is running (Amazon Virtual Private Cloud). + */ + public String getSubnetId() { + return subnetId; + } + + /** + * Specifies the VPC in which the instance is running (Amazon Virtual Private Cloud). + */ + public String getVpcId() { + return vpcId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((amiLaunchIndex == null) ? 0 : amiLaunchIndex.hashCode()); + result = prime * result + ((availabilityZone == null) ? 0 : availabilityZone.hashCode()); + result = prime * result + ((dnsName == null) ? 0 : dnsName.hashCode()); + result = prime * result + ((imageId == null) ? 0 : imageId.hashCode()); + result = prime * result + ((instanceId == null) ? 0 : instanceId.hashCode()); + result = prime * result + ((instanceState == null) ? 0 : instanceState.hashCode()); + result = prime * result + ((instanceType == null) ? 0 : instanceType.hashCode()); + result = prime * result + ((ipAddress == null) ? 0 : ipAddress.hashCode()); + result = prime * result + ((kernelId == null) ? 0 : kernelId.hashCode()); + result = prime * result + ((keyName == null) ? 0 : keyName.hashCode()); + result = prime * result + ((launchTime == null) ? 0 : launchTime.hashCode()); + result = prime * result + (monitoring ? 1231 : 1237); + result = prime * result + ((platform == null) ? 0 : platform.hashCode()); + result = prime * result + ((privateDnsName == null) ? 0 : privateDnsName.hashCode()); + result = prime * result + ((privateIpAddress == null) ? 0 : privateIpAddress.hashCode()); + result = prime * result + ((productCodes == null) ? 0 : productCodes.hashCode()); + result = prime * result + ((ramdiskId == null) ? 0 : ramdiskId.hashCode()); + result = prime * result + ((reason == null) ? 0 : reason.hashCode()); + result = prime * result + ((subnetId == null) ? 0 : subnetId.hashCode()); + result = prime * result + ((vpcId == null) ? 0 : vpcId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RunningInstance other = (RunningInstance) obj; + if (amiLaunchIndex == null) { + if (other.amiLaunchIndex != null) + return false; + } else if (!amiLaunchIndex.equals(other.amiLaunchIndex)) + return false; + if (availabilityZone == null) { + if (other.availabilityZone != null) + return false; + } else if (!availabilityZone.equals(other.availabilityZone)) + return false; + if (dnsName == null) { + if (other.dnsName != null) + return false; + } else if (!dnsName.equals(other.dnsName)) + return false; + if (imageId == null) { + if (other.imageId != null) + return false; + } else if (!imageId.equals(other.imageId)) + return false; + if (instanceId == null) { + if (other.instanceId != null) + return false; + } else if (!instanceId.equals(other.instanceId)) + return false; + if (instanceState == null) { + if (other.instanceState != null) + return false; + } else if (!instanceState.equals(other.instanceState)) + return false; + if (instanceType == null) { + if (other.instanceType != null) + return false; + } else if (!instanceType.equals(other.instanceType)) + return false; + if (ipAddress == null) { + if (other.ipAddress != null) + return false; + } else if (!ipAddress.equals(other.ipAddress)) + return false; + if (kernelId == null) { + if (other.kernelId != null) + return false; + } else if (!kernelId.equals(other.kernelId)) + return false; + if (keyName == null) { + if (other.keyName != null) + return false; + } else if (!keyName.equals(other.keyName)) + return false; + if (launchTime == null) { + if (other.launchTime != null) + return false; + } else if (!launchTime.equals(other.launchTime)) + return false; + if (monitoring != other.monitoring) + return false; + if (platform == null) { + if (other.platform != null) + return false; + } else if (!platform.equals(other.platform)) + return false; + if (privateDnsName == null) { + if (other.privateDnsName != null) + return false; + } else if (!privateDnsName.equals(other.privateDnsName)) + return false; + if (privateIpAddress == null) { + if (other.privateIpAddress != null) + return false; + } else if (!privateIpAddress.equals(other.privateIpAddress)) + return false; + if (productCodes == null) { + if (other.productCodes != null) + return false; + } else if (!productCodes.equals(other.productCodes)) + return false; + if (ramdiskId == null) { + if (other.ramdiskId != null) + return false; + } else if (!ramdiskId.equals(other.ramdiskId)) + return false; + if (reason == null) { + if (other.reason != null) + return false; + } else if (!reason.equals(other.reason)) + return false; + if (subnetId == null) { + if (other.subnetId != null) + return false; + } else if (!subnetId.equals(other.subnetId)) + return false; + if (vpcId == null) { + if (other.vpcId != null) + return false; + } else if (!vpcId.equals(other.vpcId)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/SecurityGroup.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/SecurityGroup.java new file mode 100644 index 0000000000..6569a00bb5 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/SecurityGroup.java @@ -0,0 +1,102 @@ +package org.jclouds.aws.ec2.domain; + +import java.util.SortedSet; + +/** + * + * @see + * @author Adrian Cole + */ +public class SecurityGroup implements Comparable { + private final String name; + private final String ownerId; + private final String description; + private final SortedSet ipPermissions; + + public SecurityGroup(String name, String ownerId, String description, + SortedSet ipPermissions) { + this.name = name; + this.ownerId = ownerId; + this.description = description; + this.ipPermissions = ipPermissions; + } + + /** + * {@inheritDoc} + */ + public int compareTo(SecurityGroup o) { + return (this == o) ? 0 : getName().compareTo(o.getName()); + } + + /** + * Name of the security group. + */ + public String getName() { + return name; + } + + /** + * AWS Access Key ID of the owner of the security group. + */ + public String getOwnerId() { + return ownerId; + } + + /** + * Description of the security group. + */ + public String getDescription() { + return description; + } + + /** + * Set of IP permissions associated with the security group. + */ + public SortedSet getIpPermissions() { + return ipPermissions; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((description == null) ? 0 : description.hashCode()); + result = prime * result + ((ipPermissions == null) ? 0 : ipPermissions.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((ownerId == null) ? 0 : ownerId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SecurityGroup other = (SecurityGroup) obj; + if (description == null) { + if (other.description != null) + return false; + } else if (!description.equals(other.description)) + return false; + if (ipPermissions == null) { + if (other.ipPermissions != null) + return false; + } else if (!ipPermissions.equals(other.ipPermissions)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (ownerId == null) { + if (other.ownerId != null) + return false; + } else if (!ownerId.equals(other.ownerId)) + return false; + return true; + } +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/TerminatedInstance.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/TerminatedInstance.java new file mode 100644 index 0000000000..8ef4fa75e2 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/TerminatedInstance.java @@ -0,0 +1,76 @@ +package org.jclouds.aws.ec2.domain; + + +/** + * + * @see + * @author Adrian Cole + */ +public class TerminatedInstance implements Comparable { + + private final String instanceId; + private final InstanceState shutdownState; + private final InstanceState previousState; + + public int compareTo(TerminatedInstance o) { + return (this == o) ? 0 : getInstanceId().compareTo(o.getInstanceId()); + } + + public TerminatedInstance(String instanceId, InstanceState shutdownState, + InstanceState previousState) { + this.instanceId = instanceId; + this.shutdownState = shutdownState; + this.previousState = previousState; + } + + public String getInstanceId() { + return instanceId; + } + + public InstanceState getShutdownState() { + return shutdownState; + } + + public InstanceState getPreviousState() { + return previousState; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((instanceId == null) ? 0 : instanceId.hashCode()); + result = prime * result + ((previousState == null) ? 0 : previousState.hashCode()); + result = prime * result + ((shutdownState == null) ? 0 : shutdownState.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TerminatedInstance other = (TerminatedInstance) obj; + if (instanceId == null) { + if (other.instanceId != null) + return false; + } else if (!instanceId.equals(other.instanceId)) + return false; + if (previousState == null) { + if (other.previousState != null) + return false; + } else if (!previousState.equals(other.previousState)) + return false; + if (shutdownState == null) { + if (other.shutdownState != null) + return false; + } else if (!shutdownState.equals(other.shutdownState)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/UserIdGroupPair.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/UserIdGroupPair.java new file mode 100644 index 0000000000..8997462430 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/UserIdGroupPair.java @@ -0,0 +1,78 @@ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * + * @see + * @author Adrian Cole + */ +public class UserIdGroupPair implements Comparable { + private final String userId; + private final String groupName; + + public UserIdGroupPair(String userId, String groupName) { + this.userId = checkNotNull(userId,"userId"); + this.groupName = checkNotNull(groupName,"groupName"); + } + + + /** + * {@inheritDoc} + */ + public int compareTo(UserIdGroupPair o) { + return (this == o) ? 0 : getUserId().compareTo(o.getUserId()); + } + + + /** + * AWS User ID of an account. Cannot be used when specifying a CIDR IP address. + */ + public String getUserId() { + return userId; + } + + + /** + * Name of the security group. Cannot be used when specifying a CIDR IP address. + */ + public String getGroupName() { + return groupName; + } + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((groupName == null) ? 0 : groupName.hashCode()); + result = prime * result + ((userId == null) ? 0 : userId.hashCode()); + return result; + } + + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + UserIdGroupPair other = (UserIdGroupPair) obj; + if (groupName == null) { + if (other.groupName != null) + return false; + } else if (!groupName.equals(other.groupName)) + return false; + if (userId == null) { + if (other.userId != null) + return false; + } else if (!userId.equals(other.userId)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/filters/FormSigner.java b/aws/core/src/main/java/org/jclouds/aws/ec2/filters/FormSigner.java new file mode 100755 index 0000000000..1ccad1650b --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/filters/FormSigner.java @@ -0,0 +1,213 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.filters; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.jclouds.aws.ec2.reference.EC2Parameters.ACTION; +import static org.jclouds.aws.ec2.reference.EC2Parameters.AWS_ACCESS_KEY_ID; +import static org.jclouds.aws.ec2.reference.EC2Parameters.SIGNATURE; +import static org.jclouds.aws.ec2.reference.EC2Parameters.SIGNATURE_METHOD; +import static org.jclouds.aws.ec2.reference.EC2Parameters.SIGNATURE_VERSION; +import static org.jclouds.aws.ec2.reference.EC2Parameters.TIMESTAMP; +import static org.jclouds.aws.ec2.reference.EC2Parameters.VERSION; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.ws.rs.core.HttpHeaders; + +import org.apache.commons.io.IOUtils; +import org.jclouds.aws.reference.AWSConstants; +import org.jclouds.aws.util.RequestSigner; +import org.jclouds.http.HttpConstants; +import org.jclouds.http.HttpException; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpRequestFilter; +import org.jclouds.http.HttpUtils; +import org.jclouds.http.internal.SignatureWire; +import org.jclouds.logging.Logger; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.jclouds.util.TimeStamp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Multimap; + +/** + * + * @see + * @author Adrian Cole + * + */ +@Singleton +public class FormSigner implements HttpRequestFilter, RequestSigner { + + public static String[] mandatoryParametersForSignature = new String[] { ACTION, + SIGNATURE_METHOD, SIGNATURE_VERSION, VERSION }; + private final SignatureWire signatureWire; + private final String accessKey; + private final String secretKey; + private final Provider dateService; + @Resource + @Named(HttpConstants.SIGNATURE_LOGGER) + private final Logger signatureLog = Logger.NULL; + + @Inject + public FormSigner(SignatureWire signatureWire, + @Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String accessKey, + @Named(AWSConstants.PROPERTY_AWS_SECRETACCESSKEY) String secretKey, + @TimeStamp Provider dateService) { + this.signatureWire = signatureWire; + this.accessKey = accessKey; + this.secretKey = secretKey; + this.dateService = dateService; + } + + public void filter(HttpRequest in) throws HttpException { + GeneratedHttpRequest request = (GeneratedHttpRequest) in; + checkNotNull(request.getFirstHeaderOrNull(HttpHeaders.HOST), + "request is not ready to sign; host not present"); + Multimap decodedParams = RestAnnotationProcessor.parseQueryToMap(request + .getEntity().toString()); + addSigningParams(decodedParams); + validateParams(decodedParams); + String stringToSign = createStringToSign(request, decodedParams); + String signature = signString(stringToSign); + addSignature(decodedParams, signature); + setEntity(request, decodedParams); + HttpUtils.logRequest(signatureLog, request, "<<"); + } + + String[] sortForSigning(String queryLine) { + String[] parts = queryLine.split("&"); + // 1. Sort the UTF-8 query string components by parameter name with natural byte ordering. + Arrays.sort(parts, new Comparator() { + + public int compare(String o1, String o2) { + if (o1.startsWith("AWSAccessKeyId")) + return -1; + return o1.compareTo(o2); + } + + }); + return parts; + } + + void setEntity(GeneratedHttpRequest request, Multimap decodedParams) { + request.setEntity(RestAnnotationProcessor.makeQueryLine(decodedParams, + new Comparator>() { + public int compare(Entry o1, Entry o2) { + if (o1.getKey().startsWith("Action") + || o2.getKey().startsWith("AWSAccessKeyId")) + return -1; + if (o1.getKey().startsWith("AWSAccessKeyId") + || o2.getKey().startsWith("Action")) + return 1; + return o1.getKey().compareTo(o2.getKey()); + } + })); + } + + @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 signString(String stringToSign) { + String signature; + try { + signature = HttpUtils.hmacSha256Base64(stringToSign, secretKey.getBytes()); + if (signatureWire.enabled()) + signatureWire.input(IOUtils.toInputStream(signature)); + } catch (Exception e) { + throw new HttpException("error signing request", e); + } + return signature; + } + + @VisibleForTesting + public String createStringToSign(HttpRequest request, Multimap decodedParams) { + HttpUtils.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) { + return RestAnnotationProcessor.makeQueryLine(decodedParams, + new Comparator>() { + public int compare(Map.Entry o1, Map.Entry o2) { + if (o1.getKey().startsWith("AWSAccessKeyId")) + return -1; + return o1.getKey().compareTo(o2.getKey()); + } + }); + } + + @VisibleForTesting + void addSigningParams(Multimap params) { + 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(accessKey)); + params.removeAll(SIGNATURE); + } + + public String createStringToSign(HttpRequest input) { + return createStringToSign(input, RestAnnotationProcessor.parseQueryToMap(input.getEntity() + .toString())); + } + +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/functions/ReturnVoidOnGroupNotFound.java b/aws/core/src/main/java/org/jclouds/aws/ec2/functions/ReturnVoidOnGroupNotFound.java new file mode 100644 index 0000000000..33a7416a9f --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/functions/ReturnVoidOnGroupNotFound.java @@ -0,0 +1,34 @@ +package org.jclouds.aws.ec2.functions; + +import java.lang.reflect.Constructor; + +import javax.inject.Singleton; + +import org.jclouds.aws.AWSResponseException; + +import com.google.common.base.Function; + +@Singleton +public class ReturnVoidOnGroupNotFound implements Function { + + static final Void v; + static { + Constructor cv; + try { + cv = Void.class.getDeclaredConstructor(); + cv.setAccessible(true); + v = cv.newInstance(); + } catch (Exception e) { + throw new Error("Error setting up class", e); + } + } + + public Void apply(Exception from) { + if (from instanceof AWSResponseException) { + if (((AWSResponseException) from).getError().getCode().equals("InvalidGroup.NotFound")) + return v; + } + return null; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/options/DescribeImagesOptions.java b/aws/core/src/main/java/org/jclouds/aws/ec2/options/DescribeImagesOptions.java new file mode 100644 index 0000000000..87bde1da41 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/options/DescribeImagesOptions.java @@ -0,0 +1,94 @@ +package org.jclouds.aws.ec2.options; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Set; + +import org.jclouds.aws.ec2.options.internal.BaseEC2RequestOptions; + +/** + * Contains options supported in the Form API for the DescribeImages operation.

+ * Usage

The recommended way to instantiate a DescribeImagesOptions object is to statically + * import DescribeImagesOptions.Builder.* and invoke a static creation method followed by an + * instance mutator (if needed): + *

+ * + * import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.* + *

+ * EC2Client connection = // get connection + * Future> images = connection.describeImages(executableBy("123125").imageIds(1000, 1004)); + * + * + * @author Adrian Cole + * @see + */ +public class DescribeImagesOptions extends BaseEC2RequestOptions { + public static final DescribeImagesOptions NONE = new DescribeImagesOptions(); + + /** + * AMIs for which the specified user has explicit launch permissions. + * + */ + public DescribeImagesOptions executableBy(String accountId) { + formParameters.put("ExecutableBy", checkNotNull(accountId, "accountId")); + return this; + } + + public String getExecutableBy() { + return getFirstFormOrNull("ExecutableBy"); + } + + /** + * AMI IDs to describe. + */ + public DescribeImagesOptions imageIds(String... imageIds) { + indexFormValuesWithPrefix("ImageId", imageIds); + return this; + } + + public Set getImageIds() { + return getFormValuesWithKeysPrefixedBy("ImageId."); + } + + /** + * Returns AMIs owned by the specified owner. Multiple owners can be specified. + */ + public DescribeImagesOptions ownedBy(String... owners) { + indexFormValuesWithPrefix("Owner", owners); + return this; + } + + public Set getOwners() { + return getFormValuesWithKeysPrefixedBy("Owner."); + } + + public static class Builder { + + /** + * @see DescribeImagesOptions#executableBy(String ) + */ + public static DescribeImagesOptions executableBy(String accountId) { + DescribeImagesOptions options = new DescribeImagesOptions(); + return options.executableBy(accountId); + } + + /** + * @see DescribeImagesOptions#imageIds(String[] ) + */ + public static DescribeImagesOptions imageIds(String... imageIds) { + DescribeImagesOptions options = new DescribeImagesOptions(); + return options.imageIds(imageIds); + } + + /** + * @see DescribeImagesOptions#ownedBy(String[] ) + */ + public static DescribeImagesOptions ownedBy(String... owners) { + DescribeImagesOptions options = new DescribeImagesOptions(); + return options.ownedBy(owners); + } + + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/options/RunInstancesOptions.java b/aws/core/src/main/java/org/jclouds/aws/ec2/options/RunInstancesOptions.java new file mode 100644 index 0000000000..7a20c05fc6 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/options/RunInstancesOptions.java @@ -0,0 +1,277 @@ +package org.jclouds.aws.ec2.options; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.aws.ec2.domain.InstanceType; +import org.jclouds.aws.ec2.options.internal.BaseEC2RequestOptions; + +/** + * Contains options supported in the Form API for the RunInstances operation.

+ * Usage

The recommended way to instantiate a RunInstancesOptions object is to statically + * import RunInstancesOptions.Builder.* and invoke a static creation method followed by an instance + * mutator (if needed): + *

+ * + * import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.* + *

+ * EC2Client connection = // get connection + * Future instances = connection.runInstances(executableBy("123125").imageIds(1000, 1004)); + * + * + * @author Adrian Cole + * @see + */ +public class RunInstancesOptions extends BaseEC2RequestOptions { + public static final RunInstancesOptions NONE = new RunInstancesOptions(); + + /** + * The name of the key pair. + */ + public RunInstancesOptions withKeyName(String keyName) { + formParameters.put("KeyName", checkNotNull(keyName, "keyName")); + return this; + } + + String getKeyName() { + return getFirstFormOrNull("KeyName"); + } + + /** + * Name of the security group. + */ + public RunInstancesOptions withSecurityGroup(String securityGroup) { + formParameters.put("SecurityGroup", checkNotNull(securityGroup, "securityGroup")); + return this; + } + + String getSecurityGroup() { + return getFirstFormOrNull("SecurityGroup"); + } + + /** + * Specifies additional information to make available to the instance(s). + */ + public RunInstancesOptions withAdditionalInfo(String info) { + formParameters.put("AdditionalInfo", checkNotNull(info, "info")); + return this; + } + + String getAdditionalInfo() { + return getFirstFormOrNull("AdditionalInfo"); + } + + /** + * MIME, Base64-encoded user data. + */ + public RunInstancesOptions withUserData(String info) { + formParameters.put("UserData", checkNotNull(info, "info")); + return this; + } + + String getUserData() { + return getFirstFormOrNull("UserData"); + } + + /** + * Specifies the instance type. default small; + */ + public RunInstancesOptions asType(InstanceType type) { + formParameters.put("InstanceType", checkNotNull(type, "type").toString()); + return this; + } + + String getType() { + return getFirstFormOrNull("InstanceType"); + } + + /** + * Specifies the placement constraints (Availability Zones) for launching the instances. + */ + public RunInstancesOptions inAvailabilityZone(String availabilityZone) { + formParameters.put("Placement.AvailabilityZone", checkNotNull(availabilityZone, + "availabilityZone")); + return this; + } + + String getAvailabilityZone() { + return getFirstFormOrNull("Placement.AvailabilityZone"); + } + + /** + * The ID of the kernel with which to launch the instance. + */ + public RunInstancesOptions withKernelId(String kernelId) { + formParameters.put("KernelId", checkNotNull(kernelId, "kernelId")); + return this; + } + + String getKernelId() { + return getFirstFormOrNull("KernelId"); + } + + /** + * The ID of the RAM disk with which to launch the instance. Some kernels require additional + * drivers at l aunch. Check the kernel requirements for information on whether you need to + * specify a RAM disk. To find kernel requirements, go to th e Resource Center and search for the + * kernel ID. + */ + public RunInstancesOptions withRamdisk(String ramDiskId) { + formParameters.put("RamdiskId", checkNotNull(ramDiskId, "ramDiskId")); + return this; + } + + String getRamdiskId() { + return getFirstFormOrNull("RamdiskId"); + } + + /** + * The virtual name. + */ + public RunInstancesOptions withVirtualName(String virtualName) { + formParameters + .put("BlockDeviceMapping.VirtualName", checkNotNull(virtualName, "virtualName")); + return this; + } + + String getVirtualName() { + return getFirstFormOrNull("BlockDeviceMapping.VirtualName"); + } + + /** + * The device name (e.g., /dev/sdh). + */ + public RunInstancesOptions withDeviceName(String deviceName) { + formParameters.put("BlockDeviceMapping.DeviceName", checkNotNull(deviceName, "deviceName")); + return this; + } + + String getDeviceName() { + return getFirstFormOrNull("BlockDeviceMapping.DeviceName"); + } + + /** + * Enables monitoring for the instance. + */ + public RunInstancesOptions enableMonitoring() { + formParameters.put("Monitoring.Enabled", "true"); + return this; + } + + String getMonitoringEnabled() { + return getFirstFormOrNull("Monitoring.Enabled"); + } + + /** + * Specifies the subnet ID within which to launch the instance(s) for Amazon Virtual Private + * Cloud. + */ + public RunInstancesOptions withSubnetId(String subnetId) { + formParameters.put("SubnetId", checkNotNull(subnetId, "subnetId")); + return this; + } + + String getSubnetId() { + return getFirstFormOrNull("SubnetId"); + } + + public static class Builder { + /** + * @see RunInstancesOptions#withKeyName(String) + */ + public static RunInstancesOptions withKeyName(String keyName) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withKeyName(keyName); + } + + /** + * @see RunInstancesOptions#withSecurityGroup(String) + */ + public static RunInstancesOptions withSecurityGroup(String securityGroup) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withSecurityGroup(securityGroup); + } + + /** + * @see RunInstancesOptions#withAdditionalInfo(String) + */ + public static RunInstancesOptions withAdditionalInfo(String additionalInfo) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withAdditionalInfo(additionalInfo); + } + + /** + * @see RunInstancesOptions#withUserData(String) + */ + public static RunInstancesOptions withUserData(String userData) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withUserData(userData); + } + + /** + * @see RunInstancesOptions#asType(InstanceType) + */ + public static RunInstancesOptions asType(InstanceType instanceType) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.asType(instanceType); + } + + /** + * @see RunInstancesOptions#inAvailabilityZone(String) + */ + public static RunInstancesOptions inAvailabilityZone(String availabilityZone) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.inAvailabilityZone(availabilityZone); + } + + /** + * @see RunInstancesOptions#withKernelId(String) + */ + public static RunInstancesOptions withKernelId(String kernelId) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withKernelId(kernelId); + } + + /** + * @see RunInstancesOptions#withDeviceName(String) + */ + public static RunInstancesOptions withDeviceName(String deviceName) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withDeviceName(deviceName); + } + + /** + * @see RunInstancesOptions#enableMonitoring() + */ + public static RunInstancesOptions enableMonitoring() { + RunInstancesOptions options = new RunInstancesOptions(); + return options.enableMonitoring(); + } + + /** + * @see RunInstancesOptions#withSubnetId(String) + */ + public static RunInstancesOptions withSubnetId(String subnetId) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withSubnetId(subnetId); + } + + /** + * @see RunInstancesOptions#withRamdisk(String) + */ + public static RunInstancesOptions withRamdisk(String ramdiskId) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withRamdisk(ramdiskId); + } + + /** + * @see RunInstancesOptions#withVirtualName(String) + */ + public static RunInstancesOptions withVirtualName(String virtualName) { + RunInstancesOptions options = new RunInstancesOptions(); + return options.withVirtualName(virtualName); + } + + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/options/internal/BaseEC2RequestOptions.java b/aws/core/src/main/java/org/jclouds/aws/ec2/options/internal/BaseEC2RequestOptions.java new file mode 100644 index 0000000000..a8f6745138 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/options/internal/BaseEC2RequestOptions.java @@ -0,0 +1,64 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.options.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Set; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +/** + * + * @author Adrian Cole + */ +public class BaseEC2RequestOptions extends BaseHttpRequestOptions { + + protected void indexFormValuesWithPrefix(String prefix, String... values) { + for (int i = 0; i < values.length; i++) { + formParameters.put(prefix + "." + (i + 1), checkNotNull(values[i], prefix.toLowerCase() + + "s[" + i + "]")); + } + } + + protected Set getFormValuesWithKeysPrefixedBy(final String prefix) { + Set values = Sets.newLinkedHashSet(); + for (String key : Iterables.filter(formParameters.keySet(), new Predicate() { + + public boolean apply(String input) { + return input.startsWith(prefix); + } + + })) { + values.add(formParameters.get(key).iterator().next()); + + } + return values; + } + +} \ No newline at end of file diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Constants.java b/aws/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Constants.java old mode 100644 new mode 100755 similarity index 84% rename from aws/ec2/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Constants.java rename to aws/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Constants.java index 12a5fec00d..e146cb5ac8 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Constants.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Constants.java @@ -31,4 +31,10 @@ import org.jclouds.aws.reference.AWSConstants; * @author Adrian Cole */ public interface EC2Constants extends AWSConstants { + public static final String PROPERTY_EC2_ENDPOINT = "jclouds.ec2.endpoint"; + + /** + * how long do we wait before expiring requests. + */ + public static final String PROPERTY_EC2_EXPIREINTERVAL = "jclouds.ec2.expireinterval"; } diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/reference/CommonEC2Parameters.java b/aws/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Parameters.java old mode 100644 new mode 100755 similarity index 98% rename from aws/ec2/core/src/main/java/org/jclouds/aws/ec2/reference/CommonEC2Parameters.java rename to aws/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Parameters.java index d23268a655..a1b66b9652 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/reference/CommonEC2Parameters.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/reference/EC2Parameters.java @@ -29,7 +29,7 @@ package org.jclouds.aws.ec2.reference; * @see * @author Adrian Cole */ -public interface CommonEC2Parameters { +public interface EC2Parameters { /** * Indicates the action to perform. Example: RunInstances diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/util/EC2Utils.java b/aws/core/src/main/java/org/jclouds/aws/ec2/util/EC2Utils.java new file mode 100644 index 0000000000..5d9e335ab6 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/util/EC2Utils.java @@ -0,0 +1,24 @@ +package org.jclouds.aws.ec2.util; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.rest.internal.GeneratedHttpRequest; + +/** + * + * @author Adrian Cole + */ +public class EC2Utils { + public static void indexFormValuesWithPrefix(GeneratedHttpRequest request, String prefix, + Object input) { + checkArgument(checkNotNull(input, "input") instanceof String[], + "this binder is only valid for String[] : " + input.getClass()); + String[] values = (String[]) input; + for (int i = 0; i < values.length; i++) { + request.addFormParam(prefix + "." + (i + 1), checkNotNull(values[i], prefix + .toLowerCase() + + "s[" + i + "]")); + } + } +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/BaseReservationHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/BaseReservationHandler.java new file mode 100644 index 0000000000..1e651226bf --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/BaseReservationHandler.java @@ -0,0 +1,218 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.xml; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Set; +import java.util.SortedSet; + +import javax.annotation.Resource; + +import org.jclouds.aws.ec2.domain.InstanceState; +import org.jclouds.aws.ec2.domain.InstanceType; +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.aws.ec2.domain.RunningInstance; +import org.jclouds.http.functions.ParseSax.HandlerWithResult; +import org.jclouds.logging.Logger; +import org.jclouds.util.DateService; +import org.joda.time.DateTime; +import org.xml.sax.Attributes; + +import com.google.common.collect.Sets; + +public abstract class BaseReservationHandler extends HandlerWithResult { + + protected final DateService dateService; + + public BaseReservationHandler(DateService dateService) { + this.dateService = dateService; + } + + @Resource + protected Logger logger = Logger.NULL; + private StringBuilder currentText = new StringBuilder(); + private SortedSet groupIds = Sets.newTreeSet(); + private SortedSet instances = Sets.newTreeSet(); + private String ownerId; + private String requesterId; + private String reservationId; + private String amiLaunchIndex; + private String dnsName; + private String imageId; + private String instanceId; + private InstanceState instanceState; + private InstanceType instanceType; + private InetAddress ipAddress; + private String kernelId; + private String keyName; + private DateTime launchTime; + private boolean monitoring; + private String availabilityZone; + private String platform; + private String privateDnsName; + private InetAddress privateIpAddress; + private Set productCodes = Sets.newHashSet(); + private String ramdiskId; + private String reason; + private String subnetId; + private String vpcId; + protected boolean inInstances; + protected boolean inProductCodes; + protected boolean inGroups; + + public void startElement(String uri, String name, String qName, Attributes attrs) { + if (qName.equals("instancesSet")) { + inInstances = true; + } else if (qName.equals("productCodesSet")) { + inProductCodes = true; + } else if (qName.equals("groupSet")) { + inGroups = true; + } + } + + protected String currentOrNull() { + String returnVal = currentText.toString().trim(); + return returnVal.equals("") ? null : returnVal; + } + + public void endElement(String uri, String name, String qName) { + if (qName.equals("groupId")) { + groupIds.add(currentOrNull()); + } else if (qName.equals("ownerId")) { + ownerId = currentOrNull(); + } else if (qName.equals("requesterId")) { + requesterId = currentOrNull(); + } else if (qName.equals("reservationId")) { + reservationId = currentOrNull(); + } else if (qName.equals("amiLaunchIndex")) { + amiLaunchIndex = currentOrNull(); + } else if (qName.equals("dnsName")) { + dnsName = currentOrNull(); + } else if (qName.equals("imageId")) { + imageId = currentOrNull(); + } else if (qName.equals("instanceId")) { + instanceId = currentOrNull(); + } else if (qName.equals("name")) { + instanceState = InstanceState.fromValue(currentOrNull()); + } else if (qName.equals("instanceType")) { + instanceType = InstanceType.fromValue(currentOrNull()); + } else if (qName.equals("ipAddress")) { + ipAddress = parseInetAddress(currentOrNull()); + } else if (qName.equals("kernelId")) { + kernelId = currentOrNull(); + } else if (qName.equals("keyName")) { + keyName = currentOrNull(); + } else if (qName.equals("launchTime")) { + launchTime = dateService.iso8601DateParse(currentOrNull()); + } else if (qName.equals("enabled")) { + monitoring = Boolean.parseBoolean(currentOrNull()); + } else if (qName.equals("availabilityZone")) { + availabilityZone = currentOrNull(); + } else if (qName.equals("platform")) { + platform = currentOrNull(); + } else if (qName.equals("privateDnsName")) { + privateDnsName = currentOrNull(); + } else if (qName.equals("privateIpAddress")) { + privateIpAddress = parseInetAddress(currentOrNull()); + } else if (qName.equals("ramdiskId")) { + ramdiskId = currentOrNull(); + } else if (qName.equals("reason")) { + reason = currentOrNull(); + } else if (qName.equals("subnetId")) { + subnetId = currentOrNull(); + } else if (qName.equals("vpcId")) { + vpcId = currentOrNull(); + } else if (qName.equals("productCode")) { + productCodes.add(currentOrNull()); + } else if (qName.equals("productCodesSet")) { + inProductCodes = false; + } else if (qName.equals("instancesSet")) { + inInstances = false; + } else if (qName.equals("groupSet")) { + inGroups = false; + } else if (qName.equals("item")) { + inItem(); + } + currentText = new StringBuilder(); + } + + protected void inItem() { + if (inInstances && !inProductCodes) { + instances.add(new RunningInstance(amiLaunchIndex, dnsName, imageId, instanceId, + instanceState, instanceType, ipAddress, kernelId, keyName, launchTime, + monitoring, availabilityZone, platform, privateDnsName, privateIpAddress, + productCodes, ramdiskId, reason, subnetId, vpcId)); + this.amiLaunchIndex = null; + this.dnsName = null; + this.imageId = null; + this.instanceId = null; + this.instanceState = null; + this.instanceType = null; + this.ipAddress = null; + this.kernelId = null; + this.keyName = null; + this.launchTime = null; + this.monitoring = false; + this.availabilityZone = null; + this.platform = null; + this.privateDnsName = null; + this.privateIpAddress = null; + this.productCodes = Sets.newHashSet(); + this.ramdiskId = null; + this.reason = null; + this.subnetId = null; + this.vpcId = null; + } + } + + private InetAddress parseInetAddress(String string) { + String[] byteStrings = string.split("\\."); + byte[] bytes = new byte[4]; + for (int i = 0; i < 4; i++) { + bytes[i] = (byte) Integer.parseInt(byteStrings[i]); + } + try { + return InetAddress.getByAddress(bytes); + } catch (UnknownHostException e) { + logger.warn(e, "error parsing ipAddress", currentText); + } + return null; + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } + + protected Reservation newReservation() { + Reservation info = new Reservation(groupIds, instances, ownerId, requesterId, reservationId); + this.groupIds = Sets.newTreeSet(); + this.instances = Sets.newTreeSet(); + this.ownerId = null; + this.requesterId = null; + this.reservationId = null; + return info; + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeImagesResponseHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeImagesResponseHandler.java new file mode 100755 index 0000000000..16718ebd1a --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeImagesResponseHandler.java @@ -0,0 +1,137 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import java.util.Set; +import java.util.SortedSet; + +import javax.annotation.Resource; + +import org.jclouds.aws.ec2.domain.Image; +import org.jclouds.aws.ec2.domain.Image.Architecture; +import org.jclouds.aws.ec2.domain.Image.ImageState; +import org.jclouds.aws.ec2.domain.Image.ImageType; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.logging.Logger; +import org.xml.sax.Attributes; + +import com.google.common.collect.Sets; +import com.google.inject.internal.Nullable; + +/** + * Parses the following XML document: + *

+ * DescribeImagesResponse xmlns="http://ec2.amazonaws.com/doc/2009-08-15/" + * + * @author Adrian Cole + * @see + */ +public class DescribeImagesResponseHandler extends ParseSax.HandlerWithResult> { + @Resource + protected Logger logger = Logger.NULL; + + private SortedSet contents = Sets.newTreeSet(); + private StringBuilder currentText = new StringBuilder(); + + private Architecture architecture; + private String imageId; + private String imageLocation; + private String imageOwnerId; + private ImageState imageState; + private ImageType imageType; + private boolean isPublic; + private @Nullable + String kernelId; + private String platform; + private Set productCodes = Sets.newHashSet(); + private @Nullable + String ramdiskId; + private boolean inProductCodes; + + public SortedSet getResult() { + return contents; + } + + public void startElement(String uri, String name, String qName, Attributes attrs) { + if (qName.equals("productCodesSet")) { + inProductCodes = true; + } + } + + public void endElement(String uri, String name, String qName) { + if (qName.equals("architecture")) { + architecture = Architecture.fromValue(currentText.toString().trim()); + } else if (qName.equals("imageId")) { + imageId = currentText.toString().trim(); + } else if (qName.equals("imageLocation")) { + imageLocation = currentText.toString().trim(); + } else if (qName.equals("imageOwnerId")) { + imageOwnerId = currentText.toString().trim(); + } else if (qName.equals("imageState")) { + imageState = ImageState.fromValue(currentText.toString().trim()); + } else if (qName.equals("imageType")) { + imageType = ImageType.fromValue(currentText.toString().trim()); + } else if (qName.equals("isPublic")) { + isPublic = Boolean.parseBoolean(currentText.toString().trim()); + } else if (qName.equals("kernelId")) { + kernelId = currentText.toString().trim(); + } else if (qName.equals("platform")) { + platform = currentText.toString().trim(); + } else if (qName.equals("productCode")) { + productCodes.add(currentText.toString().trim()); + } else if (qName.equals("productCodesSet")) { + inProductCodes = false; + } else if (qName.equals("ramdiskId")) { + ramdiskId = currentText.toString().trim(); + } else if (qName.equals("item")) { + if (!inProductCodes) { + try { + contents.add(new Image(architecture, imageId, imageLocation, imageOwnerId, + imageState, imageType, isPublic, kernelId, platform, productCodes, + ramdiskId)); + } catch (NullPointerException e) { + logger.warn(e, "malformed image: %s", imageId); + } + this.architecture = null; + this.imageId = null; + this.imageLocation = null; + this.imageOwnerId = null; + this.imageState = null; + this.imageType = null; + this.isPublic = false; + this.kernelId = null; + this.platform = null; + this.productCodes = Sets.newHashSet(); + this.ramdiskId = null; + } + + } + currentText = new StringBuilder(); + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeInstancesResponseHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeInstancesResponseHandler.java new file mode 100644 index 0000000000..0c712a1bc5 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeInstancesResponseHandler.java @@ -0,0 +1,66 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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: + * + * 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.xml; + +import java.util.SortedSet; + +import javax.inject.Inject; + +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.util.DateService; + +import com.google.common.collect.Sets; + +/** + * Parses the following XML document: + *

+ * DescribeImagesResponse xmlns="http: + * + * @author Adrian Cole + * @see + * @author Adrian Cole + */ +public class DescribeKeyPairsResponseHandler extends ParseSax.HandlerWithResult> { + + private StringBuilder currentText = new StringBuilder(); + private SortedSet keyPairs = Sets.newTreeSet(); + private String keyFingerprint; + private String keyName; + + public SortedSet getResult() { + return keyPairs; + } + + public void endElement(String uri, String name, String qName) { + + if (qName.equals("keyFingerprint")) { + this.keyFingerprint = currentText.toString().trim(); + } else if (qName.equals("item")) { + keyPairs.add(new KeyPair(keyName, keyFingerprint, null)); + } else if (qName.equals("keyName")) { + this.keyName = currentText.toString().trim(); + } + + currentText = new StringBuilder(); + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeSecurityGroupsResponseHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeSecurityGroupsResponseHandler.java new file mode 100644 index 0000000000..83ce5eb276 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeSecurityGroupsResponseHandler.java @@ -0,0 +1,133 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import java.util.SortedSet; + +import org.jclouds.aws.ec2.domain.IpPermission; +import org.jclouds.aws.ec2.domain.IpProtocol; +import org.jclouds.aws.ec2.domain.SecurityGroup; +import org.jclouds.aws.ec2.domain.UserIdGroupPair; +import org.jclouds.http.functions.ParseSax; +import org.xml.sax.Attributes; + +import com.google.common.collect.Sets; + +/** + * Parses: DescribeSecurityGroupsResponse xmlns="http://ec2.amazonaws.com/doc/2009-08-15/" + * + * @see + * @author Adrian Cole + */ +public class DescribeSecurityGroupsResponseHandler extends + ParseSax.HandlerWithResult> { + + private StringBuilder currentText = new StringBuilder(); + private SortedSet securtyGroups = Sets.newTreeSet(); + private String groupName; + private String ownerId; + private String groupDescription; + private SortedSet ipPermissions = Sets.newTreeSet(); + private int fromPort; + private int toPort; + private SortedSet groups = Sets.newTreeSet(); + private String userId; + private String userIdGroupName; + private IpProtocol ipProtocol; + private SortedSet ipRanges = Sets.newTreeSet(); + + private boolean inIpPermissions; + private boolean inIpRanges; + private boolean inGroups; + + public SortedSet getResult() { + return securtyGroups; + } + + public void startElement(String uri, String name, String qName, Attributes attrs) { + if (qName.equals("ipPermissions")) { + inIpPermissions = true; + } else if (qName.equals("ipRanges")) { + inIpRanges = true; + } else if (qName.equals("groups")) { + inGroups = true; + } + } + + public void endElement(String uri, String name, String qName) { + if (qName.equals("groupName")) { + if (!inGroups) + this.groupName = currentText.toString().trim(); + else + this.userIdGroupName = currentText.toString().trim(); + } else if (qName.equals("ownerId")) { + this.ownerId = currentText.toString().trim(); + } else if (qName.equals("userId")) { + this.userId = currentText.toString().trim(); + } else if (qName.equals("groupDescription")) { + this.groupDescription = currentText.toString().trim(); + } else if (qName.equals("ipProtocol")) { + this.ipProtocol = IpProtocol.fromValue(currentText.toString().trim()); + } else if (qName.equals("fromPort")) { + this.fromPort = Integer.parseInt(currentText.toString().trim()); + } else if (qName.equals("toPort")) { + this.toPort = Integer.parseInt(currentText.toString().trim()); + } else if (qName.equals("cidrIp")) { + this.ipRanges.add(currentText.toString().trim()); + } else if (qName.equals("ipPermissions")) { + inIpPermissions = false; + } else if (qName.equals("ipRanges")) { + inIpRanges = false; + } else if (qName.equals("groups")) { + inGroups = false; + } else if (qName.equals("item")) { + if (inIpPermissions && !inIpRanges && !inGroups) { + ipPermissions.add(new IpPermission(fromPort, toPort, groups, ipProtocol, ipRanges)); + this.fromPort = -1; + this.toPort = -1; + this.groups = Sets.newTreeSet(); + this.ipProtocol = null; + this.ipRanges = Sets.newTreeSet(); + } else if (inIpPermissions && !inIpRanges && inGroups) { + this.groups.add(new UserIdGroupPair(userId, userIdGroupName)); + this.userId = null; + this.userIdGroupName = null; + } else if (!inIpPermissions && !inIpRanges && !inGroups) { + securtyGroups + .add(new SecurityGroup(groupName, ownerId, groupDescription, ipPermissions)); + this.groupName = null; + this.ownerId = null; + this.groupDescription = null; + this.ipPermissions = Sets.newTreeSet(); + } + } + + currentText = new StringBuilder(); + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/KeyPairResponseHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/KeyPairResponseHandler.java new file mode 100755 index 0000000000..0ac9a2b776 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/KeyPairResponseHandler.java @@ -0,0 +1,63 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import org.jclouds.aws.ec2.domain.KeyPair; +import org.jclouds.http.functions.ParseSax; + +/** + * + * @see + * @author Adrian Cole + */ +public class KeyPairResponseHandler extends ParseSax.HandlerWithResult { + + private StringBuilder currentText = new StringBuilder(); + private String keyFingerprint; + private String keyMaterial; + private String keyName; + + public KeyPair getResult() { + return new KeyPair(keyName, keyFingerprint, keyMaterial); + } + + public void endElement(String uri, String name, String qName) { + + if (qName.equals("keyFingerprint")) { + this.keyFingerprint = currentText.toString().trim(); + } else if (qName.equals("keyMaterial")) { + this.keyMaterial = currentText.toString().trim(); + } else if (qName.equals("keyName")) { + this.keyName = currentText.toString().trim(); + } + + currentText = new StringBuilder(); + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } +} diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/EC2Context.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/RunInstancesResponseHandler.java similarity index 64% rename from aws/ec2/core/src/main/java/org/jclouds/aws/ec2/EC2Context.java rename to aws/core/src/main/java/org/jclouds/aws/ec2/xml/RunInstancesResponseHandler.java index f70d743af0..5d2177a0b9 100644 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/EC2Context.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/RunInstancesResponseHandler.java @@ -11,7 +11,7 @@ * "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 + * http: * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -21,31 +21,31 @@ * under the License. * ==================================================================== */ -package org.jclouds.aws.ec2; +package org.jclouds.aws.ec2.xml; + +import javax.inject.Inject; + +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.util.DateService; /** - * Represents an authenticated context to EC2. + * Parses the following XML document: + *

+ * RunInstancesResponse xmlns="http: * - *

Note

Please issue {@link #close()} when you are finished with this - * context in order to release resources. - * - * - * @see EC2Connection * @author Adrian Cole - * + * @see + * TerminateInstancesResponse xmlns="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-ItemType-TerminateInstancesResponseInfoType.html" + * + * @author Adrian Cole + * @see + */ +public class TerminateInstancesResponseHandler extends + HandlerWithResult> { + private StringBuilder currentText = new StringBuilder(); + + SortedSet instances = Sets.newTreeSet(); + private InstanceState shutdownState; + private InstanceState previousState; + private String instanceId; + + private boolean inShutdownState; + + private boolean inPreviousState; + + @Override + public SortedSet getResult() { + return instances; + } + + public void startElement(String uri, String name, String qName, Attributes attrs) { + if (qName.equals("shutdownState")) { + inShutdownState = true; + } else if (qName.equals("previousState")) { + inPreviousState = true; + } + } + + public void endElement(String uri, String name, String qName) { + + if (qName.equals("instanceId")) { + this.instanceId = currentOrNull(); + } else if (qName.equals("shutdownState")) { + inShutdownState = false; + } else if (qName.equals("previousState")) { + inPreviousState = false; + } else if (qName.equals("name")) { + if (inShutdownState) { + shutdownState = InstanceState.fromValue(currentOrNull()); + } else if (inPreviousState) { + previousState = InstanceState.fromValue(currentOrNull()); + } + } else if (qName.equals("item")) { + instances.add(new TerminatedInstance(instanceId, shutdownState, previousState)); + this.instanceId = null; + this.shutdownState = null; + this.previousState = null; + } + + currentText = new StringBuilder(); + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } + + protected String currentOrNull() { + String returnVal = currentText.toString().trim(); + return returnVal.equals("") ? null : returnVal; + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/handlers/AWSClientErrorRetryHandler.java b/aws/core/src/main/java/org/jclouds/aws/handlers/AWSClientErrorRetryHandler.java new file mode 100755 index 0000000000..576d32f9e1 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/handlers/AWSClientErrorRetryHandler.java @@ -0,0 +1,82 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.handlers; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.aws.domain.AWSError; +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpConstants; +import org.jclouds.http.HttpException; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpRetryHandler; +import org.jclouds.logging.Logger; +import org.jclouds.util.Utils; + +/** + * Handles Retryable responses with error codes in the 3xx range + * + * @author Adrian Cole + */ +public class AWSClientErrorRetryHandler implements HttpRetryHandler { + + @Inject(optional = true) + @Named(HttpConstants.PROPERTY_HTTP_MAX_RETRIES) + private int retryCountLimit = 5; + + private final AWSUtils utils; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + public AWSClientErrorRetryHandler(AWSUtils utils) { + this.utils = utils; + } + + public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) { + if (command.getFailureCount() > retryCountLimit) + return false; + if (response.getStatusCode() == 400 || response.getStatusCode() == 403 + || response.getStatusCode() == 409) { + byte[] content = Utils.closeClientButKeepContentStream(response); + command.incrementFailureCount(); + try { + AWSError error = utils.parseAWSErrorFromContent(command, response, new String(content)); + if ("RequestTimeout".equals(error.getCode()) + || "OperationAborted".equals(error.getCode()) + || "SignatureDoesNotMatch".equals(error.getCode())) { + return true; + } + } catch (HttpException e) { + logger.warn(e, "error parsing response: %s", new String(content)); + } + } + return false; + } + +} diff --git a/aws/core/src/main/java/org/jclouds/aws/handlers/AWSRedirectionRetryHandler.java b/aws/core/src/main/java/org/jclouds/aws/handlers/AWSRedirectionRetryHandler.java new file mode 100755 index 0000000000..942076d845 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/handlers/AWSRedirectionRetryHandler.java @@ -0,0 +1,90 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.handlers; + +import javax.inject.Inject; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.HttpHeaders; + +import org.jclouds.aws.domain.AWSError; +import org.jclouds.aws.reference.AWSConstants; +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpException; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.handlers.BackoffLimitedRetryHandler; +import org.jclouds.http.handlers.RedirectionRetryHandler; +import org.jclouds.util.Utils; + +/** + * Handles Retryable responses with error codes in the 3xx range + * + * @author Adrian Cole + */ +public class AWSRedirectionRetryHandler extends RedirectionRetryHandler { + private final AWSUtils utils; + + @Inject + public AWSRedirectionRetryHandler(BackoffLimitedRetryHandler backoffHandler, AWSUtils utils) { + super(backoffHandler); + this.utils = utils; + } + + @Override + public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) { + if (response.getFirstHeaderOrNull(HttpHeaders.LOCATION) == null + && (response.getStatusCode() == 301 || response.getStatusCode() == 307)) { + byte[] content = Utils.closeClientButKeepContentStream(response); + if (command.getRequest().getMethod() == HttpMethod.HEAD) { + command.setMethod(HttpMethod.GET); + return true; + } else { + command.incrementRedirectCount(); + try { + AWSError error = utils.parseAWSErrorFromContent(command, response, new String( + content)); + String host = error.getDetails().get(AWSConstants.ENDPOINT); + if (host != null) { + if (host.equals(command.getRequest().getEndpoint().getHost())) { + // must be an amazon error related to + // http://developer.amazonwebservices.com/connect/thread.jspa?messageID=72287𑩟 + return backoffHandler.shouldRetryRequest(command, response); + } else { + command.setHostAndPort(host, command.getRequest().getEndpoint().getPort()); + } + return true; + } else { + return false; + } + } catch (HttpException e) { + logger.error(e, "error on redirect for command %s; response %s; retrying...", + command, response); + return false; + } + } + } else { + return super.shouldRetryRequest(command, response); + } + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java b/aws/core/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java new file mode 100755 index 0000000000..de7a2eed66 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java @@ -0,0 +1,83 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.handlers; + +import javax.annotation.Resource; +import javax.inject.Inject; + +import org.jclouds.aws.AWSResponseException; +import org.jclouds.aws.domain.AWSError; +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.jclouds.logging.Logger; +import org.jclouds.util.Utils; + +/** + * This will parse and set an appropriate exception on the command object. + * + * @see AWSError + * @author Adrian Cole + * + */ +public class ParseAWSErrorFromXmlContent implements HttpErrorHandler { + @Resource + protected Logger logger = Logger.NULL; + + private final AWSUtils utils; + + @Inject + public ParseAWSErrorFromXmlContent(AWSUtils utils) { + this.utils = utils; + } + + public void handleError(HttpCommand command, HttpResponse response) { + String content; + try { + content = response.getContent() != null ? Utils.toStringAndClose(response.getContent()) + : null; + if (content != null) { + try { + if (content.indexOf('<') >= 0) { + AWSError error = utils.parseAWSErrorFromContent(command, response, content); + command.setException(new AWSResponseException(command, response, error)); + } else { + command.setException(new HttpResponseException(command, response, content)); + } + } catch (Exception he) { + command.setException(new HttpResponseException(command, response, content)); + Utils.rethrowIfRuntime(he); + } + } else { + command.setException(new HttpResponseException(command, response)); + } + } catch (Exception e) { + command.setException(new HttpResponseException(command, response)); + Utils.rethrowIfRuntime(e); + } + } + +} \ No newline at end of file diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/EC2Connection.java b/aws/core/src/main/java/org/jclouds/aws/handlers/package-info.java old mode 100644 new mode 100755 similarity index 81% rename from aws/ec2/core/src/main/java/org/jclouds/aws/ec2/EC2Connection.java rename to aws/core/src/main/java/org/jclouds/aws/handlers/package-info.java index 38a7d6813d..ebdc0af8d9 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/EC2Connection.java +++ b/aws/core/src/main/java/org/jclouds/aws/handlers/package-info.java @@ -21,8 +21,9 @@ * under the License. * ==================================================================== */ -package org.jclouds.aws.ec2; - -public class EC2Connection { - -} +/** + * This package contains HttpResponseHandlers needed to operate the REST api. + * @see + * @author Adrian Cole + */ +package org.jclouds.aws.handlers; \ No newline at end of file diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ClientLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ClientLiveTest.java new file mode 100644 index 0000000000..0131434248 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ClientLiveTest.java @@ -0,0 +1,357 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.imageIds; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +import java.util.Iterator; +import java.util.SortedSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.jclouds.aws.ec2.domain.Image; +import org.jclouds.aws.ec2.domain.RunningInstance; +import org.jclouds.aws.ec2.domain.IpPermission; +import org.jclouds.aws.ec2.domain.IpProtocol; +import org.jclouds.aws.ec2.domain.KeyPair; +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.aws.ec2.domain.SecurityGroup; +import org.jclouds.aws.ec2.domain.UserIdGroupPair; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Sets; + +/** + * Tests behavior of {@code EC2Client} + * + * @author Adrian Cole + */ +@Test(groups = "live", sequential = true, testName = "ec2.EC2ClientLiveTest") +public class EC2ClientLiveTest { + + private EC2Client client; + private String user; + + @BeforeGroups(groups = { "live" }) + public void setupClient() throws InterruptedException, ExecutionException, TimeoutException { + user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user"); + String password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key"); + + client = EC2ContextFactory.createContext(user, password, new Log4JLoggingModule()).getApi(); + } + + @Test + void testDescribeImages() throws InterruptedException, ExecutionException, TimeoutException { + SortedSet allResults = client.describeImages().get(30, TimeUnit.SECONDS); + assertNotNull(allResults); + assert allResults.size() >= 2 : allResults.size(); + Iterator iterator = allResults.iterator(); + String id1 = iterator.next().getImageId(); + String id2 = iterator.next().getImageId(); + SortedSet twoResults = client.describeImages(imageIds(id1, id2)).get(30, + TimeUnit.SECONDS); + assertNotNull(twoResults); + assertEquals(twoResults.size(), 2); + iterator = twoResults.iterator(); + assertEquals(iterator.next().getImageId(), id1); + assertEquals(iterator.next().getImageId(), id2); + } + + @Test + void testDescribeInstances() throws InterruptedException, ExecutionException, TimeoutException { + SortedSet allResults = client.describeInstances().get(30, TimeUnit.SECONDS); + assertNotNull(allResults); + assert allResults.size() >= 0 : allResults.size(); + if (allResults.size() >= 2) { + Iterator iterator = allResults.iterator().next().getRunningInstances() + .iterator(); + String id1 = iterator.next().getInstanceId(); + String id2 = iterator.next().getInstanceId(); + SortedSet twoResults = client.describeInstances(id1, id2).get(30, + TimeUnit.SECONDS); + assertNotNull(twoResults); + assertEquals(twoResults.size(), 2); + iterator = twoResults.iterator().next().getRunningInstances().iterator(); + assertEquals(iterator.next().getInstanceId(), id1); + assertEquals(iterator.next().getInstanceId(), id2); + } + } + + @Test + void testDescribeKeyPairs() throws InterruptedException, ExecutionException, TimeoutException { + SortedSet allResults = client.describeKeyPairs().get(30, TimeUnit.SECONDS); + assertNotNull(allResults); + assert allResults.size() >= 0 : allResults.size(); + if (allResults.size() >= 2) { + Iterator iterator = allResults.iterator(); + String id1 = iterator.next().getKeyName(); + String id2 = iterator.next().getKeyName(); + SortedSet twoResults = client.describeKeyPairs(id1, id2) + .get(30, TimeUnit.SECONDS); + assertNotNull(twoResults); + assertEquals(twoResults.size(), 2); + iterator = twoResults.iterator(); + assertEquals(iterator.next().getKeyName(), id1); + assertEquals(iterator.next().getKeyName(), id2); + } + } + + @Test + void testDescribeSecurityGroups() throws InterruptedException, ExecutionException, + TimeoutException { + SortedSet allResults = client.describeSecurityGroups().get(30, + TimeUnit.SECONDS); + assertNotNull(allResults); + assert allResults.size() >= 0 : allResults.size(); + if (allResults.size() >= 2) { + Iterator iterator = allResults.iterator(); + String id1 = iterator.next().getName(); + String id2 = iterator.next().getName(); + SortedSet twoResults = client.describeSecurityGroups(id1, id2).get(30, + TimeUnit.SECONDS); + assertNotNull(twoResults); + assertEquals(twoResults.size(), 2); + iterator = twoResults.iterator(); + assertEquals(iterator.next().getName(), id1); + assertEquals(iterator.next().getName(), id2); + } + } + + public static final String PREFIX = System.getProperty("user.name") + "-ec2"; + + @Test + void testCreateKeyPair() throws InterruptedException, ExecutionException, TimeoutException { + String keyName = PREFIX + "1"; + try { + client.deleteKeyPair(keyName).get(30, TimeUnit.SECONDS); + } catch (Exception e) { + + } + client.deleteKeyPair(keyName).get(30, TimeUnit.SECONDS); + + KeyPair result = client.createKeyPair(keyName).get(30, TimeUnit.SECONDS); + assertNotNull(result); + assertNotNull(result.getKeyMaterial()); + assertNotNull(result.getKeyFingerprint()); + assertEquals(result.getKeyName(), keyName); + + SortedSet twoResults = client.describeKeyPairs(keyName).get(30, TimeUnit.SECONDS); + assertNotNull(twoResults); + assertEquals(twoResults.size(), 1); + KeyPair listPair = twoResults.iterator().next(); + assertEquals(listPair.getKeyName(), result.getKeyName()); + assertEquals(listPair.getKeyFingerprint(), result.getKeyFingerprint()); + } + + @Test + void testCreateSecurityGroup() throws InterruptedException, ExecutionException, TimeoutException { + String groupName = PREFIX + "1"; + String groupDescription = PREFIX + "1 description"; + try { + client.deleteSecurityGroup(groupName).get(30, TimeUnit.SECONDS); + } catch (Exception e) { + + } + client.deleteSecurityGroup(groupName).get(30, TimeUnit.SECONDS); + + client.createSecurityGroup(groupName, groupDescription).get(30, TimeUnit.SECONDS); + + verifySecurityGroup(groupName, groupDescription); + } + + @Test + void testAuthorizeSecurityGroupIngressCidr() throws InterruptedException, ExecutionException, + TimeoutException { + String groupName = PREFIX + "ingress"; + + try { + client.deleteSecurityGroup(groupName).get(30, TimeUnit.SECONDS); + } catch (Exception e) { + } + + client.createSecurityGroup(groupName, groupName).get(30, TimeUnit.SECONDS); + client.authorizeSecurityGroupIngress(groupName, IpProtocol.TCP, 80, 80, "0.0.0.0/0").get(30, + TimeUnit.SECONDS); + assertEventually(new GroupHasPermission(client, groupName, new IpPermission(80, 80, Sets + . newTreeSet(), IpProtocol.TCP, ImmutableSortedSet.of("0.0.0.0/0")))); + + client.revokeSecurityGroupIngress(groupName, IpProtocol.TCP, 80, 80, "0.0.0.0/0").get(30, + TimeUnit.SECONDS); + assertEventually(new GroupHasNoPermissions(client, groupName)); + + } + + private void verifySecurityGroup(String groupName, String description) + throws InterruptedException, ExecutionException, TimeoutException { + SortedSet oneResult = client.describeSecurityGroups(groupName).get(30, + TimeUnit.SECONDS); + assertNotNull(oneResult); + assertEquals(oneResult.size(), 1); + SecurityGroup listPair = oneResult.iterator().next(); + assertEquals(listPair.getName(), groupName); + assertEquals(listPair.getDescription(), description); + } + + @Test(enabled = false) + // TODO + void testAuthorizeSecurityGroupIngressSourceGroup() throws InterruptedException, + ExecutionException, TimeoutException { + String group1Name = PREFIX + "ingress1"; + String group2Name = PREFIX + "ingress2"; + + try { + client.deleteSecurityGroup(group1Name).get(30, TimeUnit.SECONDS); + } catch (Exception e) { + + } + try { + client.deleteSecurityGroup(group2Name).get(30, TimeUnit.SECONDS); + } catch (Exception e) { + + } + + client.createSecurityGroup(group1Name, group1Name).get(30, TimeUnit.SECONDS); + client.createSecurityGroup(group2Name, group2Name).get(30, TimeUnit.SECONDS); + ensureGroupsExist(group1Name, group2Name); + client.authorizeSecurityGroupIngress(group1Name, IpProtocol.TCP, 80, 80, "0.0.0.0/0").get(30, + TimeUnit.SECONDS); + assertEventually(new GroupHasPermission(client, group2Name, new IpPermission(80, 80, Sets + . newTreeSet(), IpProtocol.TCP, ImmutableSortedSet.of("0.0.0.0/0")))); + + SortedSet oneResult = client.describeSecurityGroups(group1Name).get(30, + TimeUnit.SECONDS); + assertNotNull(oneResult); + assertEquals(oneResult.size(), 1); + SecurityGroup group = oneResult.iterator().next(); + assertEquals(group.getName(), group1Name); + + client.authorizeSecurityGroupIngress(group2Name, + new UserIdGroupPair(group.getOwnerId(), group1Name)).get(30, TimeUnit.SECONDS); + assertEventually(new GroupHasPermission(client, group2Name, new IpPermission(80, 80, Sets + . newTreeSet(), IpProtocol.TCP, ImmutableSortedSet.of("0.0.0.0/0")))); + + client.revokeSecurityGroupIngress(group2Name, + new UserIdGroupPair(group.getOwnerId(), group1Name)).get(30, TimeUnit.SECONDS); + assertEventually(new GroupHasNoPermissions(client, group2Name)); + } + + private static final class GroupHasPermission implements Runnable { + private final EC2Client client; + private final String group; + private final IpPermission permission; + + private GroupHasPermission(EC2Client client, String group, IpPermission permission) { + this.client = client; + this.group = group; + this.permission = permission; + } + + public void run() { + try { + SortedSet oneResult = client.describeSecurityGroups(group).get(30, + TimeUnit.SECONDS); + assertNotNull(oneResult); + assertEquals(oneResult.size(), 1); + SecurityGroup listPair = oneResult.iterator().next(); + assert listPair.getIpPermissions().contains(permission); + } catch (Exception e) { + throw new AssertionError(e); + } + } + } + + private static final class GroupHasNoPermissions implements Runnable { + private final EC2Client client; + private final String group; + + private GroupHasNoPermissions(EC2Client client, String group) { + this.client = client; + this.group = group; + } + + public void run() { + try { + SortedSet oneResult = client.describeSecurityGroups(group).get(30, + TimeUnit.SECONDS); + assertNotNull(oneResult); + assertEquals(oneResult.size(), 1); + SecurityGroup listPair = oneResult.iterator().next(); + assertEquals(listPair.getIpPermissions().size(), 0); + } catch (Exception e) { + throw new AssertionError(e); + } + } + } + + private void ensureGroupsExist(String group1Name, String group2Name) + throws InterruptedException, ExecutionException, TimeoutException { + SortedSet twoResults = client.describeSecurityGroups(group1Name, group2Name) + .get(30, TimeUnit.SECONDS); + assertNotNull(twoResults); + assertEquals(twoResults.size(), 2); + Iterator iterator = twoResults.iterator(); + SecurityGroup listPair1 = iterator.next(); + assertEquals(listPair1.getName(), group1Name); + assertEquals(listPair1.getDescription(), group1Name); + + SecurityGroup listPair2 = iterator.next(); + assertEquals(listPair2.getName(), group2Name); + assertEquals(listPair2.getDescription(), group2Name); + } + + private static final int INCONSISTENCY_WINDOW = 5000; + + /** + * Due to eventual consistency, container commands may not return correctly immediately. Hence, + * we will try up to the inconsistency window to see if the assertion completes. + */ + protected static void assertEventually(Runnable assertion) throws InterruptedException { + long start = System.currentTimeMillis(); + AssertionError error = null; + for (int i = 0; i < 30; i++) { + try { + assertion.run(); + if (i > 0) + System.err.printf("%d attempts and %dms asserting %s%n", i + 1, System + .currentTimeMillis() + - start, assertion.getClass().getSimpleName()); + return; + } catch (AssertionError e) { + error = e; + } + Thread.sleep(INCONSISTENCY_WINDOW / 30); + } + if (error != null) + throw error; + + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ClientTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ClientTest.java new file mode 100644 index 0000000000..c5cb2a5851 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ClientTest.java @@ -0,0 +1,481 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + +import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.executableBy; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.net.URI; + +import org.jclouds.aws.ec2.domain.IpProtocol; +import org.jclouds.aws.ec2.domain.UserIdGroupPair; +import org.jclouds.aws.ec2.filters.FormSigner; +import org.jclouds.aws.ec2.functions.ReturnVoidOnGroupNotFound; +import org.jclouds.aws.ec2.options.DescribeImagesOptions; +import org.jclouds.aws.ec2.options.RunInstancesOptions; +import org.jclouds.aws.ec2.xml.DescribeImagesResponseHandler; +import org.jclouds.aws.ec2.xml.DescribeInstancesResponseHandler; +import org.jclouds.aws.ec2.xml.DescribeKeyPairsResponseHandler; +import org.jclouds.aws.ec2.xml.DescribeSecurityGroupsResponseHandler; +import org.jclouds.aws.ec2.xml.KeyPairResponseHandler; +import org.jclouds.aws.ec2.xml.RunInstancesResponseHandler; +import org.jclouds.aws.ec2.xml.TerminateInstancesResponseHandler; +import org.jclouds.aws.reference.AWSConstants; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ReturnVoidIf2xx; +import org.jclouds.logging.Logger; +import org.jclouds.logging.Logger.LoggerFactory; +import org.jclouds.rest.RestClientTest; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.jclouds.util.Jsr330; +import org.jclouds.util.TimeStamp; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of {@code EC2Client} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.EC2ClientTest") +public class EC2ClientTest extends RestClientTest { + + public void testDescribeImages() throws SecurityException, NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("describeImages", Array.newInstance( + DescribeImagesOptions.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 40\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, "Version=2009-08-15&Action=DescribeImages"); + filter.filter(httpMethod); + assertEntityEquals( + httpMethod, + "Action=DescribeImages&Signature=wJmKTnrx5v3PSECZJjm%2BE1s8I%2FqHkVs2K1hJ6yxbpC0%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2009-11-08T15%3A54%3A08.897Z&Version=2009-08-15&AWSAccessKeyId=user"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeImagesResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDescribeImagesOptions() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("describeImages", Array.newInstance( + DescribeImagesOptions.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method, executableBy( + "me").ownedBy("fred", "nancy").imageIds("1", "2")); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 107\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals( + httpMethod, + "Version=2009-08-15&Action=DescribeImages&ExecutableBy=me&Owner.1=fred&Owner.2=nancy&ImageId.1=1&ImageId.2=2"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeImagesResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDescribeInstances() throws SecurityException, NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("describeInstances", Array.newInstance( + String.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 43\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, "Version=2009-08-15&Action=DescribeInstances"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeInstancesResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDescribeInstancesArgs() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("describeInstances", Array.newInstance( + String.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "1", "2"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 43\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, + "Version=2009-08-15&Action=DescribeInstances&InstanceId.1=1&InstanceId.2=2"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeInstancesResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testTerminateInstances() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("terminateInstances", String.class, Array + .newInstance(String.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "1", "2"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 59\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, + "Version=2009-08-15&Action=TerminateInstances&InstanceId.0=1&InstanceId.1=2"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, TerminateInstancesResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testRunInstances() throws SecurityException, NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("runInstances", String.class, int.class, int.class, + Array.newInstance(RunInstancesOptions.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "ami-voo", 1, 1); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 76\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, + "Version=2009-08-15&Action=RunInstances&ImageId=ami-voo&MaxCount=1&MinCount=1"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, RunInstancesResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testRunInstancesOptions() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("runInstances", String.class, int.class, int.class, + Array.newInstance(RunInstancesOptions.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "ami-voo", 1, 5, + new RunInstancesOptions().withKernelId("kernelId").enableMonitoring()); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 118\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals( + httpMethod, + "Version=2009-08-15&Action=RunInstances&ImageId=ami-voo&MaxCount=5&MinCount=1&KernelId=kernelId&Monitoring.Enabled=true"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, RunInstancesResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testCreateKeyPair() throws SecurityException, NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("createKeyPair", String.class); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "mykey"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 53\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, "Version=2009-08-15&Action=CreateKeyPair&KeyName=mykey"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, KeyPairResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDeleteKeyPair() throws SecurityException, NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("deleteKeyPair", String.class); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "mykey"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 53\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, "Version=2009-08-15&Action=DeleteKeyPair&KeyName=mykey"); + + assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDescribeKeyPairs() throws SecurityException, NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("describeKeyPairs", Array.newInstance(String.class, + 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 42\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, "Version=2009-08-15&Action=DescribeKeyPairs"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeKeyPairsResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDescribeKeyPairsArgs() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("describeKeyPairs", Array.newInstance(String.class, + 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "1", "2"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 42\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, + "Version=2009-08-15&Action=DescribeKeyPairs&KeyName.1=1&KeyName.2=2"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeKeyPairsResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDeleteSecurityGroup() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("deleteSecurityGroup", String.class); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "name"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 60\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, "Version=2009-08-15&Action=DeleteSecurityGroup&GroupName=name"); + + assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnVoidOnGroupNotFound.class); + + checkFilters(httpMethod); + } + + public void testCreateSecurityGroup() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("createSecurityGroup", String.class, String.class); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "name", + "description"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 89\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, + "Version=2009-08-15&Action=CreateSecurityGroup&GroupName=name&GroupDescription=description"); + + assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDescribeSecurityGroups() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("describeSecurityGroups", Array.newInstance( + String.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 48\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, "Version=2009-08-15&Action=DescribeSecurityGroups"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeSecurityGroupsResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testDescribeSecurityGroupsArgs() throws SecurityException, NoSuchMethodException, + IOException { + Method method = EC2Client.class.getMethod("describeSecurityGroups", Array.newInstance( + String.class, 0).getClass()); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "1", "2"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 48\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals(httpMethod, + "Version=2009-08-15&Action=DescribeSecurityGroups&GroupName.1=1&GroupName.2=2"); + + assertResponseParserClassEquals(method, httpMethod, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeSecurityGroupsResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testAuthorizeSecurityGroupIngressGroup() throws SecurityException, + NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("authorizeSecurityGroupIngress", String.class, + UserIdGroupPair.class); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "group", + new UserIdGroupPair("sourceUser", "sourceGroup")); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 71\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals( + httpMethod, + "Version=2009-08-15&Action=AuthorizeSecurityGroupIngress&GroupName=group&SourceSecurityGroupOwnerId=sourceUser&SourceSecurityGroupName=sourceGroup"); + + assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testAuthorizeSecurityGroupIngressCidr() throws SecurityException, + NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("authorizeSecurityGroupIngress", String.class, + IpProtocol.class, int.class, int.class, String.class); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "group", + IpProtocol.TCP, 6000, 7000, "0.0.0.0/0"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 131\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals( + httpMethod, + "Version=2009-08-15&Action=AuthorizeSecurityGroupIngress&GroupName=group&FromPort=6000&IpProtocol=tcp&ToPort=7000&CidrIp=0.0.0.0%2F0"); + + assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testRevokeSecurityGroupIngressGroup() throws SecurityException, + NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("revokeSecurityGroupIngress", String.class, + UserIdGroupPair.class); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "group", + new UserIdGroupPair("sourceUser", "sourceGroup")); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 68\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals( + httpMethod, + "Version=2009-08-15&Action=RevokeSecurityGroupIngress&GroupName=group&SourceSecurityGroupOwnerId=sourceUser&SourceSecurityGroupName=sourceGroup"); + + assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + public void testRevokeSecurityGroupIngressCidr() throws SecurityException, + NoSuchMethodException, IOException { + Method method = EC2Client.class.getMethod("revokeSecurityGroupIngress", String.class, + IpProtocol.class, int.class, int.class, String.class); + GeneratedHttpRequest httpMethod = processor.createRequest(method, "group", + IpProtocol.TCP, 6000, 7000, "0.0.0.0/0"); + + assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1"); + assertHeadersEqual(httpMethod, + "Content-Length: 128\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n"); + assertEntityEquals( + httpMethod, + "Version=2009-08-15&Action=RevokeSecurityGroupIngress&GroupName=group&FromPort=6000&IpProtocol=tcp&ToPort=7000&CidrIp=0.0.0.0%2F0"); + + assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, null); + + checkFilters(httpMethod); + } + + @Override + protected void checkFilters(GeneratedHttpRequest httpMethod) { + assertEquals(httpMethod.getFilters().size(), 1); + assertEquals(httpMethod.getFilters().get(0).getClass(), FormSigner.class); + } + + @Override + protected TypeLiteral> createTypeLiteral() { + return new TypeLiteral>() { + }; + } + + private FormSigner filter; + + @Override + @BeforeTest + protected void setupFactory() { + super.setupFactory(); + this.filter = injector.getInstance(FormSigner.class); + } + + @Override + protected Module createModule() { + return new AbstractModule() { + @Override + protected void configure() { + bind(URI.class).annotatedWith(EC2.class).toInstance( + URI.create("https://ec2.amazonaws.com")); + bindConstant().annotatedWith(Jsr330.named(AWSConstants.PROPERTY_AWS_ACCESSKEYID)).to( + "user"); + bindConstant().annotatedWith(Jsr330.named(AWSConstants.PROPERTY_AWS_SECRETACCESSKEY)) + .to("key"); + bind(Logger.LoggerFactory.class).toInstance(new LoggerFactory() { + public Logger getLogger(String category) { + return Logger.NULL; + } + }); + } + + @SuppressWarnings("unused") + @Provides + @TimeStamp + String provide() { + return "2009-11-08T15:54:08.897Z"; + } + }; + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ContextBuilderTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ContextBuilderTest.java new file mode 100644 index 0000000000..bdf652f34e --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/EC2ContextBuilderTest.java @@ -0,0 +1,98 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + +import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID; +import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY; +import static org.testng.Assert.assertEquals; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.jclouds.aws.ec2.config.EC2RestClientModule; +import org.jclouds.aws.ec2.filters.FormSigner; +import org.jclouds.aws.ec2.reference.EC2Constants; +import org.jclouds.rest.RestContext; +import org.jclouds.rest.internal.RestContextImpl; +import org.testng.annotations.Test; + +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of modules configured in EC2ContextBuilderT + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.EC2ContextBuilderTest") +public class EC2ContextBuilderTest { + + public void testNewBuilder() { + EC2ContextBuilder builder = newBuilder(); + assertEquals(builder.getProperties().getProperty(EC2Constants.PROPERTY_EC2_ENDPOINT), + "http://ec2.amazonaws.com"); + assertEquals(builder.getProperties().getProperty(PROPERTY_AWS_ACCESSKEYID), "id"); + assertEquals(builder.getProperties().getProperty(PROPERTY_AWS_SECRETACCESSKEY), "secret"); + } + + public void testBuildContext() { + RestContext context = newBuilder().buildContext(); + assertEquals(context.getClass(), RestContextImpl.class); + assertEquals(context.getAccount(), "id"); + assertEquals(context.getEndPoint(), URI.create("http://ec2.amazonaws.com")); + } + + public void testBuildInjector() { + Injector i = newBuilder().buildInjector(); + assert i.getInstance(Key.get(new TypeLiteral>() { + })) != null; // TODO: test all things taken from context + assert i.getInstance(FormSigner.class) != null; + } + + protected void testAddContextModule() { + List modules = new ArrayList(); + EC2ContextBuilder builder = newBuilder(); + builder.addContextModule(modules); + assertEquals(modules.size(), 1); + assertEquals(modules.get(0).getClass(), EC2RestClientModule.class); + } + + private EC2ContextBuilder newBuilder() { + EC2ContextBuilder builder = new EC2ContextBuilder(new EC2PropertiesBuilder("id", "secret") + .build()); + return builder; + } + + protected void addClientModule() { + List modules = new ArrayList(); + EC2ContextBuilder builder = newBuilder(); + builder.addClientModule(modules); + assertEquals(modules.size(), 1); + assertEquals(modules.get(0).getClass(), EC2RestClientModule.class); + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/ExpensiveEC2ClientLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/ExpensiveEC2ClientLiveTest.java new file mode 100644 index 0000000000..12d240df59 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/ExpensiveEC2ClientLiveTest.java @@ -0,0 +1,211 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withKeyName; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.UndeclaredThrowableException; +import java.net.InetSocketAddress; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.jclouds.aws.ec2.domain.InstanceState; +import org.jclouds.aws.ec2.domain.InstanceType; +import org.jclouds.aws.ec2.domain.IpProtocol; +import org.jclouds.aws.ec2.domain.KeyPair; +import org.jclouds.aws.ec2.domain.RunningInstance; +import org.jclouds.concurrent.WithinThreadExecutorService; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.http.HttpResponseException; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.ssh.SshClient; +import org.jclouds.ssh.SshException; +import org.jclouds.ssh.jsch.config.JschSshClientModule; +import org.jclouds.util.Utils; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * Follows the book Cloud Application Architectures ISBN: 978-0-596-15636-7 + * + * @author Adrian Cole + */ +@Test(groups = "live", sequential = true, testName = "ec2.ExpensiveEC2ClientLiveTest") +public class ExpensiveEC2ClientLiveTest { + + private EC2Client client; + protected SshClient.Factory sshFactory; + private String serverPrefix = System.getProperty("user.name") + ".ec2"; + private KeyPair keyPair; + private String securityGroupName; + private String serverId; + + @BeforeGroups(groups = { "live" }) + public void setupClient() throws InterruptedException, ExecutionException, TimeoutException { + String user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user"); + String password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key"); + + client = new EC2ContextBuilder(new EC2PropertiesBuilder(user, password).build()).withModules( + new Log4JLoggingModule()).buildContext().getApi(); + + Injector injector = Guice.createInjector(new Log4JLoggingModule(), new JschSshClientModule(), + new ExecutorServiceModule(new WithinThreadExecutorService())); + client = EC2ContextFactory.createContext(user, password, new Log4JLoggingModule()).getApi(); + sshFactory = injector.getInstance(SshClient.Factory.class); + } + + @Test + void testCreateSecurityGroupIngressCidr() throws InterruptedException, ExecutionException, + TimeoutException { + securityGroupName = serverPrefix + "ingress"; + + try { + client.deleteSecurityGroup(securityGroupName).get(30, TimeUnit.SECONDS); + } catch (Exception e) { + } + + client.createSecurityGroup(securityGroupName, securityGroupName).get(30, TimeUnit.SECONDS); + client.authorizeSecurityGroupIngress(securityGroupName, IpProtocol.TCP, 80, 80, "0.0.0.0/0") + .get(30, TimeUnit.SECONDS); + client + .authorizeSecurityGroupIngress(securityGroupName, IpProtocol.TCP, 443, 443, + "0.0.0.0/0").get(30, TimeUnit.SECONDS); + client.authorizeSecurityGroupIngress(securityGroupName, IpProtocol.TCP, 22, 22, "0.0.0.0/0") + .get(30, TimeUnit.SECONDS); + } + + @Test + void testCreateKeyPair() throws InterruptedException, ExecutionException, TimeoutException { + String keyName = serverPrefix + "1"; + try { + client.deleteKeyPair(keyName).get(30, TimeUnit.SECONDS); + } catch (Exception e) { + + } + client.deleteKeyPair(keyName).get(30, TimeUnit.SECONDS); + + keyPair = client.createKeyPair(keyName).get(30, TimeUnit.SECONDS); + assertNotNull(keyPair); + assertNotNull(keyPair.getKeyMaterial()); + assertNotNull(keyPair.getKeyFingerprint()); + assertEquals(keyPair.getKeyName(), keyName); + + } + + @Test(dependsOnMethods = { "testCreateKeyPair", "testCreateSecurityGroupIngressCidr" }) + public void testCreateRunningInstance() throws Exception { + String imageId = "ami-1fd73376"; + RunningInstance server = null; + while (server == null) { + try { + server = client.runInstances( + imageId, + 1, + 1, + withKeyName(keyPair.getKeyName()).asType(InstanceType.M1_SMALL) + .withSecurityGroup(securityGroupName)).get(30, TimeUnit.SECONDS) + .getRunningInstances().iterator().next(); + } catch (UndeclaredThrowableException e) { + HttpResponseException htpe = (HttpResponseException) e.getCause().getCause(); + if (htpe.getResponse().getStatusCode() == 400) + continue; + throw e; + } + } + assertNotNull(server.getInstanceId()); + serverId = server.getInstanceId(); + assertEquals(server.getInstanceState(), InstanceState.PENDING); + server = blockUntilRunningInstanceActive(serverId); + + sshPing(server); + } + + /** + * this tests "personality" as the file looked up was sent during server creation + */ + private void sshPing(RunningInstance newDetails) throws IOException { + try { + doCheckKey(newDetails); + } catch (SshException e) {// try twice in case there is a network timeout + try { + Thread.sleep(10 * 1000); + } catch (InterruptedException e1) { + } + doCheckKey(newDetails); + } + } + + private void doCheckKey(RunningInstance newDetails) throws IOException { + SshClient connection = sshFactory.create(new InetSocketAddress(newDetails.getDnsName(), 22), + "root", keyPair.getKeyMaterial().getBytes()); + try { + connection.connect(); + InputStream etcPasswd = connection.get("/etc/passwd"); + Utils.toStringAndClose(etcPasswd); + } finally { + if (connection != null) + connection.disconnect(); + } + } + + private RunningInstance blockUntilRunningInstanceActive(String serverId) + throws InterruptedException, ExecutionException, TimeoutException { + RunningInstance currentDetails = null; + for (currentDetails = getRunningInstance(serverId); currentDetails.getInstanceState() != InstanceState.RUNNING; currentDetails = getRunningInstance(serverId)) { + System.out.printf("%s blocking on status active: currently: %s%n", currentDetails + .getInstanceId(), currentDetails.getInstanceState()); + Thread.sleep(5 * 1000); + } + System.out.printf("%s awaiting daemons to start%n", currentDetails.getInstanceId()); + Thread.sleep(10 * 1000); + return currentDetails; + } + + @AfterClass + void cleanup() { + if (serverId != null) + client.terminateInstances(serverId); + if (keyPair != null) + client.deleteKeyPair(keyPair.getKeyName()); + if (securityGroupName != null) + client.deleteSecurityGroup(securityGroupName); + } + + private RunningInstance getRunningInstance(String serverId) throws InterruptedException, + ExecutionException, TimeoutException { + return client.describeInstances(serverId).get(15, TimeUnit.SECONDS).first() + .getRunningInstances().first(); + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/config/EC2ContextModuleTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/config/EC2ContextModuleTest.java new file mode 100644 index 0000000000..1a06f98309 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/config/EC2ContextModuleTest.java @@ -0,0 +1,79 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.config; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.aws.ec2.EC2Client; +import org.jclouds.aws.ec2.reference.EC2Constants; +import org.jclouds.concurrent.WithinThreadExecutorService; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; +import org.jclouds.logging.jdk.config.JDKLoggingModule; +import org.jclouds.rest.RestContext; +import org.jclouds.rest.config.RestModule; +import org.jclouds.rest.internal.RestContextImpl; +import org.jclouds.util.Jsr330; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.EC2ContextModuleTest") +public class EC2ContextModuleTest { + + Injector createInjector() { + return Guice.createInjector(new ExecutorServiceModule(new WithinThreadExecutorService()), + new EC2RestClientModule(), new RestModule(), + new JavaUrlHttpCommandExecutorServiceModule(), new JDKLoggingModule(), + new EC2ContextModule() { + @Override + protected void configure() { + bindConstant().annotatedWith( + Jsr330.named(EC2Constants.PROPERTY_AWS_ACCESSKEYID)).to("user"); + bindConstant().annotatedWith( + Jsr330.named(EC2Constants.PROPERTY_AWS_SECRETACCESSKEY)).to("key"); + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_EC2_ENDPOINT)) + .to("http://localhost"); + bindConstant().annotatedWith( + Jsr330.named(EC2Constants.PROPERTY_EC2_EXPIREINTERVAL)).to(30); + super.configure(); + } + }); + } + + @Test + void testContextImpl() { + RestContext handler = createInjector().getInstance( + Key.get(new TypeLiteral>() { + })); + assertEquals(handler.getClass(), RestContextImpl.class); + } + +} \ No newline at end of file diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/config/EC2RestClientModuleTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/config/EC2RestClientModuleTest.java new file mode 100644 index 0000000000..3bc84cda9d --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/config/EC2RestClientModuleTest.java @@ -0,0 +1,70 @@ +package org.jclouds.aws.ec2.config; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.aws.ec2.reference.EC2Constants; +import org.jclouds.aws.handlers.AWSClientErrorRetryHandler; +import org.jclouds.aws.handlers.AWSRedirectionRetryHandler; +import org.jclouds.aws.handlers.ParseAWSErrorFromXmlContent; +import org.jclouds.concurrent.WithinThreadExecutorService; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.http.functions.config.ParserModule; +import org.jclouds.http.handlers.DelegatingErrorHandler; +import org.jclouds.http.handlers.DelegatingRetryHandler; +import org.jclouds.util.Jsr330; +import org.testng.annotations.Test; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.EC2RestClientModuleTest") +public class EC2RestClientModuleTest { + + Injector createInjector() { + return Guice.createInjector(new EC2RestClientModule(), new ExecutorServiceModule( + new WithinThreadExecutorService()), new ParserModule(), new AbstractModule() { + @Override + protected void configure() { + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_AWS_ACCESSKEYID)).to( + "user"); + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_AWS_SECRETACCESSKEY)) + .to("key"); + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_EC2_ENDPOINT)).to( + "http://localhost"); + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_EC2_EXPIREINTERVAL)) + .to(30); + } + }); + } + + @Test + void testServerErrorHandler() { + DelegatingErrorHandler handler = createInjector().getInstance(DelegatingErrorHandler.class); + assertEquals(handler.getServerErrorHandler().getClass(), ParseAWSErrorFromXmlContent.class); + } + + @Test + void testClientErrorHandler() { + DelegatingErrorHandler handler = createInjector().getInstance(DelegatingErrorHandler.class); + assertEquals(handler.getClientErrorHandler().getClass(), ParseAWSErrorFromXmlContent.class); + } + + @Test + void testClientRetryHandler() { + DelegatingRetryHandler handler = createInjector().getInstance(DelegatingRetryHandler.class); + assertEquals(handler.getClientErrorRetryHandler().getClass(), + AWSClientErrorRetryHandler.class); + } + + @Test + void testRedirectionRetryHandler() { + DelegatingRetryHandler handler = createInjector().getInstance(DelegatingRetryHandler.class); + assertEquals(handler.getRedirectionRetryHandler().getClass(), + AWSRedirectionRetryHandler.class); + } + +} \ No newline at end of file diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/filters/FormSignerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/filters/FormSignerTest.java new file mode 100644 index 0000000000..5933578c58 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/filters/FormSignerTest.java @@ -0,0 +1,59 @@ +package org.jclouds.aws.ec2.filters; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.aws.ec2.config.EC2RestClientModule; +import org.jclouds.aws.ec2.reference.EC2Constants; +import org.jclouds.concurrent.WithinThreadExecutorService; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.http.functions.config.ParserModule; +import org.jclouds.util.Jsr330; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; + +@Test(groups = "unit", testName = "ec2.FormSignerTest") +public class FormSignerTest { + + private Injector injector; + private FormSigner filter; + + @Test + void testBuildCanonicalizedString() { + assertEquals( + filter.buildCanonicalizedString(new ImmutableMultimap.Builder().put( + "AWSAccessKeyId", "foo").put( "Action","DescribeImages").put( + "Expires","2008-02-10T12:00:00Z").put("ImageId.1", "ami-2bb65342").put( + "SignatureMethod", "HmacSHA256").put("SignatureVersion", "2").put("Version", + "2009-08-15").build()), + "AWSAccessKeyId=foo&Action=DescribeImages&Expires=2008-02-10T12%3A00%3A00Z&ImageId.1=ami-2bb65342&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2009-08-15"); + } + + /** + * before class, as we need to ensure that the filter is threadsafe. + * + */ + @BeforeClass + protected void createFilter() { + injector = Guice.createInjector(new EC2RestClientModule(), new ExecutorServiceModule( + new WithinThreadExecutorService()), new ParserModule(), new AbstractModule() { + + protected void configure() { + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_AWS_ACCESSKEYID)).to( + "foo"); + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_AWS_SECRETACCESSKEY)) + .to("bar"); + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_EC2_ENDPOINT)).to( + "https://ec2.amazonaws.com"); + bindConstant().annotatedWith(Jsr330.named(EC2Constants.PROPERTY_EC2_EXPIREINTERVAL)) + .to(30); + } + }); + filter = injector.getInstance(FormSigner.class); + } + +} \ No newline at end of file diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/options/DescribeImagesOptionsTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/options/DescribeImagesOptionsTest.java new file mode 100644 index 0000000000..da2256f201 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/options/DescribeImagesOptionsTest.java @@ -0,0 +1,118 @@ +package org.jclouds.aws.ec2.options; + +import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.executableBy; +import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.imageIds; +import static org.jclouds.aws.ec2.options.DescribeImagesOptions.Builder.ownedBy; +import static org.testng.Assert.assertEquals; + +import java.util.Collections; + +import org.jclouds.http.options.HttpRequestOptions; +import org.testng.annotations.Test; + +/** + * Tests possible uses of DescribeImagesOptions and DescribeImagesOptions.Builder.* + * + * @author Adrian Cole + */ +public class DescribeImagesOptionsTest { + + @Test + public void testAssignability() { + assert HttpRequestOptions.class.isAssignableFrom(DescribeImagesOptions.class); + assert !String.class.isAssignableFrom(DescribeImagesOptions.class); + } + + @Test + public void testExecutableBy() { + DescribeImagesOptions options = new DescribeImagesOptions(); + options.executableBy("test"); + assertEquals(options.buildFormParameters().get("ExecutableBy"), Collections + .singletonList("test")); + } + + @Test + public void testNullExecutableBy() { + DescribeImagesOptions options = new DescribeImagesOptions(); + assertEquals(options.buildFormParameters().get("ExecutableBy"), Collections.EMPTY_LIST); + } + + @Test + public void testExecutableByStatic() { + DescribeImagesOptions options = executableBy("test"); + assertEquals(options.buildFormParameters().get("ExecutableBy"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testExecutableByNPE() { + executableBy(null); + } + + @Test + public void testOwners() { + DescribeImagesOptions options = new DescribeImagesOptions(); + options.ownedBy("test"); + assertEquals(options.buildFormParameters().get("Owner.1"), Collections.singletonList("test")); + } + + @Test + public void testMultipleOwners() { + DescribeImagesOptions options = new DescribeImagesOptions(); + options.ownedBy("test", "trouble"); + assertEquals(options.buildFormParameters().get("Owner.1"), Collections.singletonList("test")); + assertEquals(options.buildFormParameters().get("Owner.2"), Collections + .singletonList("trouble")); + } + + @Test + public void testNullOwners() { + DescribeImagesOptions options = new DescribeImagesOptions(); + assertEquals(options.buildFormParameters().get("Owner.1"), Collections.EMPTY_LIST); + } + + @Test + public void testOwnersStatic() { + DescribeImagesOptions options = ownedBy("test"); + assertEquals(options.buildFormParameters().get("Owner.1"), Collections.singletonList("test")); + } + + public void testNoOwners() { + ownedBy(); + } + + @Test + public void testImageIds() { + DescribeImagesOptions options = new DescribeImagesOptions(); + options.imageIds("test"); + assertEquals(options.buildFormParameters().get("ImageId.1"), Collections + .singletonList("test")); + } + + @Test + public void testMultipleImageIds() { + DescribeImagesOptions options = new DescribeImagesOptions(); + options.imageIds("test", "trouble"); + assertEquals(options.buildFormParameters().get("ImageId.1"), Collections + .singletonList("test")); + assertEquals(options.buildFormParameters().get("ImageId.2"), Collections + .singletonList("trouble")); + } + + @Test + public void testNullImageIds() { + DescribeImagesOptions options = new DescribeImagesOptions(); + assertEquals(options.buildFormParameters().get("ImageId.1"), Collections.EMPTY_LIST); + } + + @Test + public void testImageIdsStatic() { + DescribeImagesOptions options = imageIds("test"); + assertEquals(options.buildFormParameters().get("ImageId.1"), Collections + .singletonList("test")); + } + + public void testNoImageIds() { + imageIds(); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/options/RunInstancesOptionsTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/options/RunInstancesOptionsTest.java new file mode 100644 index 0000000000..58aab05577 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/options/RunInstancesOptionsTest.java @@ -0,0 +1,344 @@ +package org.jclouds.aws.ec2.options; + +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.asType; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.enableMonitoring; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.inAvailabilityZone; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withAdditionalInfo; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withDeviceName; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withKernelId; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withKeyName; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withRamdisk; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withSecurityGroup; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withSubnetId; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withUserData; +import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.withVirtualName; +import static org.testng.Assert.assertEquals; + +import java.util.Collections; + +import org.jclouds.aws.ec2.domain.InstanceType; +import org.jclouds.http.options.HttpRequestOptions; +import org.testng.annotations.Test; + +/** + * Tests possible uses of RunInstancesOptions and RunInstancesOptions.Builder.* + * + * @author Adrian Cole + */ +public class RunInstancesOptionsTest { + + @Test + public void testAssignability() { + assert HttpRequestOptions.class.isAssignableFrom(RunInstancesOptions.class); + assert !String.class.isAssignableFrom(RunInstancesOptions.class); + } + + @Test + public void testWithKeyName() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withKeyName("test"); + assertEquals(options.buildFormParameters().get("KeyName"), Collections.singletonList("test")); + } + + @Test + public void testNullWithKeyName() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("KeyName"), Collections.EMPTY_LIST); + } + + @Test + public void testWithKeyNameStatic() { + RunInstancesOptions options = withKeyName("test"); + assertEquals(options.buildFormParameters().get("KeyName"), Collections.singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithKeyNameNPE() { + withKeyName(null); + } + + @Test + public void testWithSecurityGroup() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withSecurityGroup("test"); + assertEquals(options.buildFormParameters().get("SecurityGroup"), Collections + .singletonList("test")); + } + + @Test + public void testNullWithSecurityGroup() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("SecurityGroup"), Collections.EMPTY_LIST); + } + + @Test + public void testWithSecurityGroupStatic() { + RunInstancesOptions options = withSecurityGroup("test"); + assertEquals(options.buildFormParameters().get("SecurityGroup"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithSecurityGroupNPE() { + withSecurityGroup(null); + } + + @Test + public void testWithAdditionalInfo() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withAdditionalInfo("test"); + assertEquals(options.buildFormParameters().get("AdditionalInfo"), Collections + .singletonList("test")); + } + + @Test + public void testNullWithAdditionalInfo() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("AdditionalInfo"), Collections.EMPTY_LIST); + } + + @Test + public void testWithAdditionalInfoStatic() { + RunInstancesOptions options = withAdditionalInfo("test"); + assertEquals(options.buildFormParameters().get("AdditionalInfo"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithAdditionalInfoNPE() { + withAdditionalInfo(null); + } + + @Test + public void testWithUserData() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withUserData("test"); + assertEquals(options.buildFormParameters().get("UserData"), Collections + .singletonList("test")); + } + + @Test + public void testNullWithUserData() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("UserData"), Collections.EMPTY_LIST); + } + + @Test + public void testWithUserDataStatic() { + RunInstancesOptions options = withUserData("test"); + assertEquals(options.buildFormParameters().get("UserData"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithUserDataNPE() { + withUserData(null); + } + + @Test + public void testWithInstanceType() { + RunInstancesOptions options = new RunInstancesOptions(); + options.asType(InstanceType.C1_XLARGE); + assertEquals(options.buildFormParameters().get("InstanceType"), Collections + .singletonList("c1.xlarge")); + } + + @Test + public void testNullWithInstanceType() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("InstanceType"), Collections.EMPTY_LIST); + } + + @Test + public void testWithInstanceTypeStatic() { + RunInstancesOptions options = asType(InstanceType.C1_XLARGE); + assertEquals(options.buildFormParameters().get("InstanceType"), Collections + .singletonList("c1.xlarge")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithInstanceTypeNPE() { + asType(null); + } + + @Test + public void testInAvailabilityZone() { + RunInstancesOptions options = new RunInstancesOptions(); + options.inAvailabilityZone("test"); + assertEquals(options.buildFormParameters().get("Placement.AvailabilityZone"), Collections + .singletonList("test")); + } + + @Test + public void testNullAvailabilityZone() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("Placement.AvailabilityZone"), + Collections.EMPTY_LIST); + } + + @Test + public void testInAvailabilityZoneStatic() { + RunInstancesOptions options = inAvailabilityZone("test"); + assertEquals(options.buildFormParameters().get("Placement.AvailabilityZone"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testInAvailabilityZoneNPE() { + inAvailabilityZone(null); + } + + @Test + public void testWithKernelId() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withKernelId("test"); + assertEquals(options.buildFormParameters().get("KernelId"), Collections + .singletonList("test")); + } + + @Test + public void testNullWithKernelId() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("KernelId"), Collections.EMPTY_LIST); + } + + @Test + public void testWithKernelIdStatic() { + RunInstancesOptions options = withKernelId("test"); + assertEquals(options.buildFormParameters().get("KernelId"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithKernelIdNPE() { + withKernelId(null); + } + + @Test + public void testWithDeviceName() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withDeviceName("test"); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.DeviceName"), Collections + .singletonList("test")); + } + + @Test + public void testNullWithDeviceName() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.DeviceName"), + Collections.EMPTY_LIST); + } + + @Test + public void testWithDeviceNameStatic() { + RunInstancesOptions options = withDeviceName("test"); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.DeviceName"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithDeviceNameNPE() { + withDeviceName(null); + } + + @Test + public void testWithMonitoringEnabled() { + RunInstancesOptions options = new RunInstancesOptions(); + options.enableMonitoring(); + assertEquals(options.buildFormParameters().get("Monitoring.Enabled"), Collections + .singletonList("true")); + } + + @Test + public void testNullWithMonitoringEnabled() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("Monitoring.Enabled"), Collections.EMPTY_LIST); + } + + @Test + public void testWithMonitoringEnabledStatic() { + RunInstancesOptions options = enableMonitoring(); + assertEquals(options.buildFormParameters().get("Monitoring.Enabled"), Collections + .singletonList("true")); + } + + @Test + public void testWithSubnetId() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withSubnetId("test"); + assertEquals(options.buildFormParameters().get("SubnetId"), Collections + .singletonList("test")); + } + + @Test + public void testNullWithSubnetId() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("SubnetId"), Collections.EMPTY_LIST); + } + + @Test + public void testWithSubnetIdStatic() { + RunInstancesOptions options = withSubnetId("test"); + assertEquals(options.buildFormParameters().get("SubnetId"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithSubnetIdNPE() { + withSubnetId(null); + } + + @Test + public void testWithRamdisk() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withRamdisk("test"); + assertEquals(options.buildFormParameters().get("RamdiskId"), Collections + .singletonList("test")); + } + + @Test + public void testNullWithRamdisk() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("RamdiskId"), Collections.EMPTY_LIST); + } + + @Test + public void testWithRamdiskStatic() { + RunInstancesOptions options = withRamdisk("test"); + assertEquals(options.buildFormParameters().get("RamdiskId"), Collections + .singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithRamdiskNPE() { + withRamdisk(null); + } + + @Test + public void testWithVirtualName() { + RunInstancesOptions options = new RunInstancesOptions(); + options.withVirtualName("test"); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.VirtualName"), + Collections.singletonList("test")); + } + + @Test + public void testNullWithVirtualName() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.VirtualName"), + Collections.EMPTY_LIST); + } + + @Test + public void testWithVirtualNameStatic() { + RunInstancesOptions options = withVirtualName("test"); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.VirtualName"), + Collections.singletonList("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testWithVirtualNameNPE() { + withVirtualName(null); + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeImagesResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeImagesResponseHandlerTest.java new file mode 100644 index 0000000000..711d6f8067 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeImagesResponseHandlerTest.java @@ -0,0 +1,62 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.util.SortedSet; + +import org.jclouds.aws.ec2.domain.Image; +import org.jclouds.aws.ec2.domain.Image.Architecture; +import org.jclouds.aws.ec2.domain.Image.ImageState; +import org.jclouds.aws.ec2.domain.Image.ImageType; +import org.jclouds.http.functions.BaseHandlerTest; +import org.testng.annotations.Test; + +import com.google.common.collect.Sets; + +/** + * Tests behavior of {@code DescribeImagesResponseHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.DescribeImagesResponseHandlerTest") +public class DescribeImagesResponseHandlerTest extends BaseHandlerTest { + + public void testApplyInputStream() { + InputStream is = getClass().getResourceAsStream("/ec2/describe_images.xml"); + SortedSet contents = Sets.newTreeSet(); + + contents.add(new Image(Architecture.I386, "ami-be3adfd7", + "ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml", "206029621532", + ImageState.AVAILABLE, ImageType.MACHINE, false, "aki-4438dd2d", null, Sets + . newHashSet(), "ari-4538dd2c")); + + SortedSet result = (SortedSet) factory.create( + injector.getInstance(DescribeImagesResponseHandler.class)).parse(is); + + assertEquals(result, contents); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeInstancesResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeInstancesResponseHandlerTest.java new file mode 100644 index 0000000000..528a75bbff --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeInstancesResponseHandlerTest.java @@ -0,0 +1,111 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.SortedSet; + +import org.jclouds.aws.ec2.domain.InstanceState; +import org.jclouds.aws.ec2.domain.InstanceType; +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.aws.ec2.domain.RunningInstance; +import org.jclouds.http.functions.BaseHandlerTest; +import org.jclouds.util.DateService; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Sets; + +/** + * Tests behavior of {@code DescribeInstancesResponseHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.DescribeInstancesResponseHandlerTest") +public class DescribeInstancesResponseHandlerTest extends BaseHandlerTest { + + private DateService dateService; + + @BeforeTest + @Override + protected void setUpInjector() { + super.setUpInjector(); + dateService = injector.getInstance(DateService.class); + assert dateService != null; + } + + public void testWhenRunning() throws UnknownHostException { + + InputStream is = getClass().getResourceAsStream("/ec2/describe_instances_running.xml"); + SortedSet contents = Sets.newTreeSet(); + + contents.add(new Reservation(ImmutableSortedSet.of("adriancole.ec2ingress"), + ImmutableSortedSet.of(new RunningInstance("0", + "ec2-174-129-81-68.compute-1.amazonaws.com", "ami-1fd73376", "i-0799056f", + InstanceState.RUNNING, InstanceType.M1_SMALL, InetAddress + .getByName("174.129.81.68"), "aki-a71cf9ce", "adriancole.ec21", + dateService.iso8601DateParse("2009-11-09T03:00:34.000Z"), false, + "us-east-1c", null, "ip-10-243-42-70.ec2.internal", InetAddress + .getByName("10.243.42.70"), ImmutableSet. of(), + "ari-a51cf9cc", null, null, null)), "993194456877", null, "r-a3c508cb")); + + SortedSet result = factory.create( + injector.getInstance(DescribeInstancesResponseHandler.class)).parse(is); + + assertEquals(result, contents); + + } + + public void testApplyInputStream() { + + InputStream is = getClass().getResourceAsStream("/ec2/describe_instances.xml"); + SortedSet contents = Sets.newTreeSet(); + + contents.add(new Reservation(ImmutableSortedSet.of("default"), ImmutableSortedSet.of( + new RunningInstance("23", "ec2-72-44-33-4.compute-1.amazonaws.com", "ami-6ea54007", + "i-28a64341", InstanceState.RUNNING, InstanceType.M1_LARGE, + (InetAddress) null, "aki-ba3adfd3", "example-key-name", dateService + .iso8601DateParse("2007-08-07T11:54:42.000Z"), false, + "us-east-1b", null, "10-251-50-132.ec2.internal", null, ImmutableSet + .of("774F4FF8"), "ari-badbad00", null, null, null), + new RunningInstance("23", "ec2-72-44-33-6.compute-1.amazonaws.com", "ami-6ea54007", + "i-28a64435", InstanceState.RUNNING, InstanceType.M1_LARGE, + (InetAddress) null, "aki-ba3adfd3", "example-key-name", dateService + .iso8601DateParse("2007-08-07T11:54:42.000Z"), false, + "us-east-1b", null, "10-251-50-134.ec2.internal", null, ImmutableSet + .of("774F4FF8"), "ari-badbad00", null, null, null)), + "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", null, "r-44a5402d")); + + SortedSet result = factory.create( + injector.getInstance(DescribeInstancesResponseHandler.class)).parse(is); + + assertEquals(result, contents); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeKeyPairsResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeKeyPairsResponseHandlerTest.java new file mode 100644 index 0000000000..5d30ba3361 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeKeyPairsResponseHandlerTest.java @@ -0,0 +1,56 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.util.SortedSet; + +import org.jclouds.aws.ec2.domain.KeyPair; +import org.jclouds.http.functions.BaseHandlerTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSortedSet; + +/** + * Tests behavior of {@code DescribeKeyPairsHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.DescribeKeyPairsHandlerTest") +public class DescribeKeyPairsResponseHandlerTest extends BaseHandlerTest { + public void testApplyInputStream() { + + InputStream is = getClass().getResourceAsStream("/ec2/describe_keypairs.xml"); + + SortedSet expected = ImmutableSortedSet.of(new KeyPair("gsg-keypair", + "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f", null)); + + SortedSet result = factory.create( + injector.getInstance(DescribeKeyPairsResponseHandler.class)).parse(is); + + assertEquals(result, expected); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java new file mode 100644 index 0000000000..253681076c --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeSecurityGroupsResponseHandlerTest.java @@ -0,0 +1,65 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.util.SortedSet; + +import org.jclouds.aws.ec2.domain.IpPermission; +import org.jclouds.aws.ec2.domain.IpProtocol; +import org.jclouds.aws.ec2.domain.SecurityGroup; +import org.jclouds.aws.ec2.domain.UserIdGroupPair; +import org.jclouds.http.functions.BaseHandlerTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSortedSet; + +/** + * Tests behavior of {@code DescribeSecurityGroupsHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.DescribeSecurityGroupsHandlerTest") +public class DescribeSecurityGroupsResponseHandlerTest extends BaseHandlerTest { + public void testApplyInputStream() { + + InputStream is = getClass().getResourceAsStream("/ec2/describe_securitygroups.xml"); + + SortedSet expected = ImmutableSortedSet.of(new SecurityGroup("WebServers", + "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", "Web Servers", ImmutableSortedSet + .of(new IpPermission(80, 80, ImmutableSortedSet. of(), + IpProtocol.TCP, ImmutableSortedSet.of("0.0.0.0/0")))), + new SecurityGroup("RangedPortsBySource", "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", + "Group A", ImmutableSortedSet.of(new IpPermission(6000, 7000, + ImmutableSortedSet. of(), IpProtocol.TCP, + ImmutableSortedSet. of())))); + + SortedSet result = factory.create( + injector.getInstance(DescribeSecurityGroupsResponseHandler.class)).parse(is); + + assertEquals(result, expected); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/KeyPairResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/KeyPairResponseHandlerTest.java new file mode 100644 index 0000000000..80db25f87f --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/KeyPairResponseHandlerTest.java @@ -0,0 +1,76 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; + +import org.jclouds.aws.ec2.domain.KeyPair; +import org.jclouds.http.functions.BaseHandlerTest; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code KeyPairResponseHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.KeyPairResponseHandlerTest") +public class KeyPairResponseHandlerTest extends BaseHandlerTest { + public void testApplyInputStream() { + + InputStream is = getClass().getResourceAsStream("/ec2/create_keypair.xml"); + + KeyPair expected = new KeyPair( + "gsg-keypair", + "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f", + "-----BEGIN RSA PRIVATE KEY-----\n" + + "MIIEoQIBAAKCAQBuLFg5ujHrtm1jnutSuoO8Xe56LlT+HM8v/xkaa39EstM3/aFxTHgElQiJLChp\n" + + "HungXQ29VTc8rc1bW0lkdi23OH5eqkMHGhvEwqa0HWASUMll4o3o/IX+0f2UcPoKCOVUR+jx71Sg\n" + + "5AU52EQfanIn3ZQ8lFW7Edp5a3q4DhjGlUKToHVbicL5E+g45zfB95wIyywWZfeW/UUF3LpGZyq/\n" + + "ebIUlq1qTbHkLbCC2r7RTn8vpQWp47BGVYGtGSBMpTRP5hnbzzuqj3itkiLHjU39S2sJCJ0TrJx5\n" + + "i8BygR4s3mHKBj8l+ePQxG1kGbF6R4yg6sECmXn17MRQVXODNHZbAgMBAAECggEAY1tsiUsIwDl5\n" + + "91CXirkYGuVfLyLflXenxfI50mDFms/mumTqloHO7tr0oriHDR5K7wMcY/YY5YkcXNo7mvUVD1pM\n" + + "ZNUJs7rw9gZRTrf7LylaJ58kOcyajw8TsC4e4LPbFaHwS1d6K8rXh64o6WgW4SrsB6ICmr1kGQI7\n" + + "3wcfgt5ecIu4TZf0OE9IHjn+2eRlsrjBdeORi7KiUNC/pAG23I6MdDOFEQRcCSigCj+4/mciFUSA\n" + + "SWS4dMbrpb9FNSIcf9dcLxVM7/6KxgJNfZc9XWzUw77Jg8x92Zd0fVhHOux5IZC+UvSKWB4dyfcI\n" + + "tE8C3p9bbU9VGyY5vLCAiIb4qQKBgQDLiO24GXrIkswF32YtBBMuVgLGCwU9h9HlO9mKAc2m8Cm1\n" + + "jUE5IpzRjTedc9I2qiIMUTwtgnw42auSCzbUeYMURPtDqyQ7p6AjMujp9EPemcSVOK9vXYL0Ptco\n" + + "xW9MC0dtV6iPkCN7gOqiZXPRKaFbWADp16p8UAIvS/a5XXk5jwKBgQCKkpHi2EISh1uRkhxljyWC\n" + + "iDCiK6JBRsMvpLbc0v5dKwP5alo1fmdR5PJaV2qvZSj5CYNpMAy1/EDNTY5OSIJU+0KFmQbyhsbm\n" + + "rdLNLDL4+TcnT7c62/aH01ohYaf/VCbRhtLlBfqGoQc7+sAc8vmKkesnF7CqCEKDyF/dhrxYdQKB\n" + + "gC0iZzzNAapayz1+JcVTwwEid6j9JqNXbBc+Z2YwMi+T0Fv/P/hwkX/ypeOXnIUcw0Ih/YtGBVAC\n" + + "DQbsz7LcY1HqXiHKYNWNvXgwwO+oiChjxvEkSdsTTIfnK4VSCvU9BxDbQHjdiNDJbL6oar92UN7V\n" + + "rBYvChJZF7LvUH4YmVpHAoGAbZ2X7XvoeEO+uZ58/BGKOIGHByHBDiXtzMhdJr15HTYjxK7OgTZm\n" + + "gK+8zp4L9IbvLGDMJO8vft32XPEWuvI8twCzFH+CsWLQADZMZKSsBasOZ/h1FwhdMgCMcY+Qlzd4\n" + + "JZKjTSu3i7vhvx6RzdSedXEMNTZWN4qlIx3kR5aHcukCgYA9T+Zrvm1F0seQPbLknn7EqhXIjBaT\n" + + "P8TTvW/6bdPi23ExzxZn7KOdrfclYRph1LHMpAONv/x2xALIf91UB+v5ohy1oDoasL0gij1houRe\n" + + "2ERKKdwz0ZL9SWq6VTdhr/5G994CK72fy5WhyERbDjUIdHaK3M849JJuf8cSrvSb4g==\n" + + "-----END RSA PRIVATE KEY-----"); + + KeyPair result = factory.create(injector.getInstance(KeyPairResponseHandler.class)).parse(is); + + assertEquals(result, expected); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/RunInstancesResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/RunInstancesResponseHandlerTest.java new file mode 100644 index 0000000000..f5940d4e66 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/RunInstancesResponseHandlerTest.java @@ -0,0 +1,91 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.net.InetAddress; + +import org.jclouds.aws.ec2.domain.InstanceState; +import org.jclouds.aws.ec2.domain.InstanceType; +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.aws.ec2.domain.RunningInstance; +import org.jclouds.http.functions.BaseHandlerTest; +import org.jclouds.util.DateService; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Sets; + +/** + * Tests behavior of {@code RunInstancesResponseHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.RunInstancesResponseHandlerTest") +public class RunInstancesResponseHandlerTest extends BaseHandlerTest { + + private DateService dateService; + + @BeforeTest + @Override + protected void setUpInjector() { + super.setUpInjector(); + dateService = injector.getInstance(DateService.class); + assert dateService != null; + } + + public void testApplyInputStream() { + + InputStream is = getClass().getResourceAsStream("/ec2/run_instances.xml"); + + Reservation expected = new Reservation(ImmutableSortedSet.of("default"), ImmutableSortedSet + .of( + new RunningInstance("0", null, "ami-60a54009", "i-2ba64342", + InstanceState.PENDING, InstanceType.M1_SMALL, (InetAddress) null, null, + "example-key-name", dateService + .iso8601DateParse("2007-08-07T11:51:50.000Z"), true, "us-east-1b", + null, null, (InetAddress) null, Sets. newTreeSet(), null, null, + null, null), new RunningInstance("0", null, "ami-60a54009", + "i-2bc64242", InstanceState.PENDING, InstanceType.M1_SMALL, + (InetAddress) null, null, "example-key-name", dateService + .iso8601DateParse("2007-08-07T11:51:50.000Z"), true, "us-east-1b", + null, null, (InetAddress) null, Sets. newTreeSet(), null, null, + null, null), new RunningInstance("0", null, "ami-60a54009", + "i-2be64332", InstanceState.PENDING, InstanceType.M1_SMALL, + (InetAddress) null, null, "example-key-name", dateService + .iso8601DateParse("2007-08-07T11:51:50.000Z"), true, "us-east-1b", + null, null, (InetAddress) null, Sets. newTreeSet(), null, null, + null, null) + + ), "AIDADH4IGTRXXKCD", null, "r-47a5402e"); + + Reservation result = factory.create( + injector.getInstance(RunInstancesResponseHandler.class)).parse(is); + + assertEquals(result, expected); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/TerminateInstancesResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/TerminateInstancesResponseHandlerTest.java new file mode 100644 index 0000000000..ed27716333 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/TerminateInstancesResponseHandlerTest.java @@ -0,0 +1,70 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.util.SortedSet; + +import org.jclouds.aws.ec2.domain.InstanceState; +import org.jclouds.aws.ec2.domain.TerminatedInstance; +import org.jclouds.http.functions.BaseHandlerTest; +import org.jclouds.util.DateService; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSortedSet; + +/** + * Tests behavior of {@code TerminateInstancesResponseHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.TerminateInstancesResponseHandlerTest") +public class TerminateInstancesResponseHandlerTest extends BaseHandlerTest { + + private DateService dateService; + + @BeforeTest + @Override + protected void setUpInjector() { + super.setUpInjector(); + dateService = injector.getInstance(DateService.class); + assert dateService != null; + } + + public void testApplyInputStream() { + + InputStream is = getClass().getResourceAsStream("/ec2/terminate_instances.xml"); + + SortedSet expected = ImmutableSortedSet.of(new TerminatedInstance( + "i-3ea74257", InstanceState.SHUTTING_DOWN, InstanceState.RUNNING)); + + SortedSet result = factory.create( + injector.getInstance(TerminateInstancesResponseHandler.class)).parse(is); + + assertEquals(result, expected); + } +} diff --git a/aws/core/src/test/resources/ec2/create_keypair.xml b/aws/core/src/test/resources/ec2/create_keypair.xml new file mode 100644 index 0000000000..aea18efc61 --- /dev/null +++ b/aws/core/src/test/resources/ec2/create_keypair.xml @@ -0,0 +1,27 @@ + + gsg-keypair + 1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f + -----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQBuLFg5ujHrtm1jnutSuoO8Xe56LlT+HM8v/xkaa39EstM3/aFxTHgElQiJLChp +HungXQ29VTc8rc1bW0lkdi23OH5eqkMHGhvEwqa0HWASUMll4o3o/IX+0f2UcPoKCOVUR+jx71Sg +5AU52EQfanIn3ZQ8lFW7Edp5a3q4DhjGlUKToHVbicL5E+g45zfB95wIyywWZfeW/UUF3LpGZyq/ +ebIUlq1qTbHkLbCC2r7RTn8vpQWp47BGVYGtGSBMpTRP5hnbzzuqj3itkiLHjU39S2sJCJ0TrJx5 +i8BygR4s3mHKBj8l+ePQxG1kGbF6R4yg6sECmXn17MRQVXODNHZbAgMBAAECggEAY1tsiUsIwDl5 +91CXirkYGuVfLyLflXenxfI50mDFms/mumTqloHO7tr0oriHDR5K7wMcY/YY5YkcXNo7mvUVD1pM +ZNUJs7rw9gZRTrf7LylaJ58kOcyajw8TsC4e4LPbFaHwS1d6K8rXh64o6WgW4SrsB6ICmr1kGQI7 +3wcfgt5ecIu4TZf0OE9IHjn+2eRlsrjBdeORi7KiUNC/pAG23I6MdDOFEQRcCSigCj+4/mciFUSA +SWS4dMbrpb9FNSIcf9dcLxVM7/6KxgJNfZc9XWzUw77Jg8x92Zd0fVhHOux5IZC+UvSKWB4dyfcI +tE8C3p9bbU9VGyY5vLCAiIb4qQKBgQDLiO24GXrIkswF32YtBBMuVgLGCwU9h9HlO9mKAc2m8Cm1 +jUE5IpzRjTedc9I2qiIMUTwtgnw42auSCzbUeYMURPtDqyQ7p6AjMujp9EPemcSVOK9vXYL0Ptco +xW9MC0dtV6iPkCN7gOqiZXPRKaFbWADp16p8UAIvS/a5XXk5jwKBgQCKkpHi2EISh1uRkhxljyWC +iDCiK6JBRsMvpLbc0v5dKwP5alo1fmdR5PJaV2qvZSj5CYNpMAy1/EDNTY5OSIJU+0KFmQbyhsbm +rdLNLDL4+TcnT7c62/aH01ohYaf/VCbRhtLlBfqGoQc7+sAc8vmKkesnF7CqCEKDyF/dhrxYdQKB +gC0iZzzNAapayz1+JcVTwwEid6j9JqNXbBc+Z2YwMi+T0Fv/P/hwkX/ypeOXnIUcw0Ih/YtGBVAC +DQbsz7LcY1HqXiHKYNWNvXgwwO+oiChjxvEkSdsTTIfnK4VSCvU9BxDbQHjdiNDJbL6oar92UN7V +rBYvChJZF7LvUH4YmVpHAoGAbZ2X7XvoeEO+uZ58/BGKOIGHByHBDiXtzMhdJr15HTYjxK7OgTZm +gK+8zp4L9IbvLGDMJO8vft32XPEWuvI8twCzFH+CsWLQADZMZKSsBasOZ/h1FwhdMgCMcY+Qlzd4 +JZKjTSu3i7vhvx6RzdSedXEMNTZWN4qlIx3kR5aHcukCgYA9T+Zrvm1F0seQPbLknn7EqhXIjBaT +P8TTvW/6bdPi23ExzxZn7KOdrfclYRph1LHMpAONv/x2xALIf91UB+v5ohy1oDoasL0gij1houRe +2ERKKdwz0ZL9SWq6VTdhr/5G994CK72fy5WhyERbDjUIdHaK3M849JJuf8cSrvSb4g== +-----END RSA PRIVATE KEY----- + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/describe_images.xml b/aws/core/src/test/resources/ec2/describe_images.xml new file mode 100644 index 0000000000..c89b38cfce --- /dev/null +++ b/aws/core/src/test/resources/ec2/describe_images.xml @@ -0,0 +1,16 @@ + + + + ami-be3adfd7 + ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml + + available + 206029621532 + false + i386 + machine + aki-4438dd2d + ari-4538dd2c + + + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/describe_instances.xml b/aws/core/src/test/resources/ec2/describe_instances.xml new file mode 100644 index 0000000000..23cd42e589 --- /dev/null +++ b/aws/core/src/test/resources/ec2/describe_instances.xml @@ -0,0 +1,59 @@ + + + + r-44a5402d + UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM + + + default + + + + + i-28a64341 + ami-6ea54007 + + 0 + running + + 10-251-50-132.ec2.internal + ec2-72-44-33-4.compute-1.amazonaws.com + example-key-name + 23 + + 774F4FF8 + + m1.large + 2007-08-07T11:54:42.000Z + + us-east-1b + + aki-ba3adfd3 + ari-badbad00 + + + i-28a64435 + ami-6ea54007 + + 0 + running + + 10-251-50-134.ec2.internal + ec2-72-44-33-6.compute-1.amazonaws.com + example-key-name + 23 + + 774F4FF8 + + m1.large + 2007-08-07T11:54:42.000Z + + us-east-1b + + aki-ba3adfd3 + ari-badbad00 + + + + + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/describe_instances_running.xml b/aws/core/src/test/resources/ec2/describe_instances_running.xml new file mode 100644 index 0000000000..6bb5c13b63 --- /dev/null +++ b/aws/core/src/test/resources/ec2/describe_instances_running.xml @@ -0,0 +1,44 @@ + + ae75a8cc-b707-4a20-b130-61ad6f20de61 + + + r-a3c508cb + 993194456877 + + + adriancole.ec2ingress + + + + + i-0799056f + ami-1fd73376 + + 16 + running + + ip-10-243-42-70.ec2.internal + + ec2-174-129-81-68.compute-1.amazonaws.com + + + adriancole.ec21 + 0 + + m1.small + 2009-11-09T03:00:34.000Z + + us-east-1c + + aki-a71cf9ce + ari-a51cf9cc + + disabled + + 10.243.42.70 + 174.129.81.68 + + + + + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/describe_keypairs.xml b/aws/core/src/test/resources/ec2/describe_keypairs.xml new file mode 100644 index 0000000000..2dbf3e271c --- /dev/null +++ b/aws/core/src/test/resources/ec2/describe_keypairs.xml @@ -0,0 +1,8 @@ + + + + gsg-keypair + 1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f + + + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/describe_securitygroups.xml b/aws/core/src/test/resources/ec2/describe_securitygroups.xml new file mode 100644 index 0000000000..6b051fd8dd --- /dev/null +++ b/aws/core/src/test/resources/ec2/describe_securitygroups.xml @@ -0,0 +1,36 @@ + + + + UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM + WebServers + Web Servers + + + tcp + 80 + 80 + + + + 0.0.0.0/0 + + + + + + + UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM + RangedPortsBySource + Group A + + + tcp + 6000 + 7000 + + + + + + + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/log4j.xml b/aws/core/src/test/resources/ec2/log4j.xml new file mode 100755 index 0000000000..d712a5bd47 --- /dev/null +++ b/aws/core/src/test/resources/ec2/log4j.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/run_instances.xml b/aws/core/src/test/resources/ec2/run_instances.xml new file mode 100644 index 0000000000..d2591f3eaf --- /dev/null +++ b/aws/core/src/test/resources/ec2/run_instances.xml @@ -0,0 +1,71 @@ + + r-47a5402e + AIDADH4IGTRXXKCD + + + default + + + + + i-2ba64342 + ami-60a54009 + + 0 + pending + + + + example-key-name + 0 + m1.small + 2007-08-07T11:51:50.000Z + + us-east-1b + + + true + + + + i-2bc64242 + ami-60a54009 + + 0 + pending + + + + example-key-name + 1 + m1.small + 2007-08-07T11:51:50.000Z + + us-east-1b + + + true + + + + i-2be64332 + ami-60a54009 + + 0 + pending + + + + example-key-name + 2 + m1.small + 2007-08-07T11:51:50.000Z + + us-east-1b + + + true + + + + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/terminate_instances.xml b/aws/core/src/test/resources/ec2/terminate_instances.xml new file mode 100644 index 0000000000..35380575d8 --- /dev/null +++ b/aws/core/src/test/resources/ec2/terminate_instances.xml @@ -0,0 +1,15 @@ + + + + i-3ea74257 + + 32 + shutting-down + + + 16 + running + + + + \ No newline at end of file diff --git a/aws/ec2/core/pom.xml b/aws/ec2/core/pom.xml deleted file mode 100644 index c3b8c597b7..0000000000 --- a/aws/ec2/core/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - org.jclouds - jclouds-ec2-project - 1.0-SNAPSHOT - ../pom.xml - - 4.0.0 - org.jclouds - jclouds-ec2-core - jclouds Amazon EC2 Components Core - jar - jclouds Core components to access Amazon EC2 - - - scm:svn:http://jclouds.googlecode.com/svn/trunk/aws/ec2/core - scm:svn:https://jclouds.googlecode.com/svn/trunk/aws/ec2/core - http://jclouds.googlecode.com/svn/trunk/aws/ec2/core - - diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/BaseEC2RequestOptions.java b/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/BaseEC2RequestOptions.java deleted file mode 100644 index 1f6a6d18eb..0000000000 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/BaseEC2RequestOptions.java +++ /dev/null @@ -1,271 +0,0 @@ -/** - * - * Copyright (C) 2009 Cloud Conscious, LLC. - * - * ==================================================================== - * 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.commands.options; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.ACTION; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.AWS_ACCESS_KEY_ID; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.EXPIRES; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE_METHOD; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE_VERSION; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.TIMESTAMP; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.VERSION; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -import org.jclouds.aws.ec2.reference.CommonEC2Parameters; -import org.jclouds.aws.util.AWSUtils; -import org.jclouds.aws.util.DateService; -import org.jclouds.http.HttpHeaders; -import org.jclouds.http.options.BaseHttpRequestOptions; -import org.joda.time.DateTime; - -/** - * Contains the base options needed for all EC2 QUERY API operations.

- * Extend this class in the following way to avoid massive boilerplate code: Usage: - *

- * - *

- * public static class MyRequestOptions extends BaseEC2RequestOptions<MyRequestOptions> {
- *    static {
- *       realClass = MyRequestOptions.class;
- *    }
- * 
- *    @Override
- *    public String getAction() {
- *       return "MyRequest";
- *    }
- * 
- *    public String getId() {
- *       return parameters.get("id");
- *    }
- * 
- *    public MyRequestOptions withId(String id) {
- *       encodeAndReplaceParameter("id", id);
- *       return this;
- *    }
- * 
- *    public static class Builder extends BaseEC2RequestOptions.Builder {
- *       public static MyRequestOptions withId(String id) {
- *          MyRequestOptions options = new MyRequestOptions();
- *          return options.withId(id);
- *       }
- *    }
- * }
- * 
- * - * @see
- * @see CommonEC2Parameters - * @author Adrian Cole - * - */ -public abstract class BaseEC2RequestOptions extends - BaseHttpRequestOptions implements EC2RequestOptions { - - public static String[] mandatoryParametersForSignature = new String[] { ACTION, - SIGNATURE_METHOD, SIGNATURE_VERSION, VERSION }; - - static final DateService dateService = new DateService(); - - private String awsSecretAccessKey; - - private String awsAccessKeyId; - - /** - * {@inheritDoc} - * - * @see CommonEC2Parameters#ACTION - */ - public abstract String getAction(); - - protected static Class realClass = BaseEC2RequestOptions.class; - - /** - * Sets the request property Action to the appropriate name - * - */ - public BaseEC2RequestOptions() { - try { - parameters.put(ACTION, URLEncoder.encode(getAction(), "UTF-8")); - parameters.put(SIGNATURE_METHOD, URLEncoder.encode("HmacSHA256", "UTF-8")); - parameters.put(SIGNATURE_VERSION, URLEncoder.encode("2", "UTF-8")); - parameters.put(VERSION, URLEncoder.encode("2009-04-04", "UTF-8")); - } catch (UnsupportedEncodingException e) { - assert false : e.toString(); - // job of unit test to ensure this never happens - } - } - - /** - * {@inheritDoc} - * - * @see CommonEC2Parameters#TIMESTAMP - */ - @SuppressWarnings("unchecked") - public T timeStamp() { - encodeAndReplaceParameter(TIMESTAMP, dateService.iso8601DateFormat()); - return (T) this; - } - - /** - * {@inheritDoc} - * - * @see CommonEC2Parameters#EXPIRES - */ - @SuppressWarnings("unchecked") - public T expireAt(DateTime time) { - try { - parameters.put(EXPIRES, URLEncoder.encode(dateService.iso8601DateFormat(checkNotNull(time, - "time")), "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("bad encoding on datetime: " + time, e); - } - return (T) this; - } - - @Override - public String buildQueryString() { - checkState(awsSecretAccessKey != null, - "request is not ready to sign; awsSecretAccessKey not present"); - checkState(awsAccessKeyId != null, "request is not ready to sign; awsAccessKeyId not present"); - String host = getFirstHeaderOrNull(HttpHeaders.HOST); - checkState(host != null, "request is not ready to sign; host not present"); - // timestamp is incompatible with expires - if (parameters.get(EXPIRES) == null) { - timeStamp(); - } - for (String parameter : mandatoryParametersForSignature) { - checkState(parameters.get(parameter) != null, "parameter " + parameter - + " is required for signature"); - } - parameters.remove(SIGNATURE); - encodeAndReplaceParameter(AWS_ACCESS_KEY_ID, awsAccessKeyId); - - // 1. Sort the UTF-8 query string components by parameter name with natural byte ordering. - // -- as parameters are a SortedSet, they are already sorted. - // 2. URL encode the parameter name and values according to the following rules... - // -- all parameters are URL encoded on the way in - // 3. Separate the encoded parameter names from their encoded values with the equals sign, - // even if the parameter value is empty. - // -- we do not allow null values. - // 4. Separate the name-value pairs with an ampersand. - // -- buildQueryString() does this. - StringBuilder toSign = new StringBuilder(); - toSign.append(HttpMethod.GET).append("\n").append(host.toLowerCase()).append("\n").append("/").append( - "\n"); - String canonicalizedQueryString = super.buildQueryString().replaceFirst("\\?", ""); - toSign.append(canonicalizedQueryString); - String signature; - try { - signature = AWSUtils.hmacSha256Base64(toSign.toString(), awsSecretAccessKey.getBytes()); - encodeAndReplaceParameter(SIGNATURE, signature); - return super.buildQueryString(); - } catch (Exception e) { - throw new RuntimeException("error signing request [" + toSign.toString() + "]"); - } - } - - /** - * {@inheritDoc} - * - * @see CommonEC2Parameters#AWS_ACCESS_KEY_ID - */ - @SuppressWarnings("unchecked") - public T signWith(String awsAccessKeyId, String awsSecretAccessKey) { - this.awsAccessKeyId = checkNotNull(awsAccessKeyId, "awsAccessKeyId"); - this.awsSecretAccessKey = checkNotNull(awsSecretAccessKey, "awsSecretAccessKey"); - return (T) this; - } - - /** - * {@inheritDoc} - * - * @see HttpHeaders#HOST - */ - @SuppressWarnings("unchecked") - public T usingHost(String hostname) { - this.replaceHeader(HttpHeaders.HOST, hostname); - return (T) this; - } - - /** - * The types here are parameterized in effort to return the proper type of the subclass - * - * @author Adrian Cole - */ - public static abstract class Builder { - - /** - * @see BaseEC2RequestOptions#expireAt(DateTime) - */ - @SuppressWarnings("unchecked") - public static T expireAt(DateTime time) { - T options; - try { - options = (T) realClass.newInstance(); - } catch (Exception e) { - throw new Error("incorrect configuration, class: " + realClass - + " should extend BaseEC2RequestOptions", e); - } - return (T) options.expireAt(time); - } - - /** - * @see BaseEC2RequestOptions#usingHost(String) - */ - @SuppressWarnings("unchecked") - public static T usingHost(String hostname) { - T options; - try { - options = (T) realClass.newInstance(); - } catch (Exception e) { - throw new Error("incorrect configuration, class: " + realClass - + " should extend BaseEC2RequestOptions", e); - } - return (T) options.usingHost(hostname); - } - - /** - * @see BaseEC2RequestOptions#signWith(String,String) - */ - @SuppressWarnings("unchecked") - public static T signWith(String awsAccessKeyId, - String awsSecretAccessKey) { - T options; - try { - options = (T) realClass.newInstance(); - } catch (Exception e) { - throw new Error("incorrect configuration, class: " + realClass - + " should extend BaseEC2RequestOptions", e); - } - return (T) options.signWith(awsAccessKeyId, awsSecretAccessKey); - } - - } -} diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/EC2QuerySigner.java b/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/EC2QuerySigner.java deleted file mode 100755 index c0917f3080..0000000000 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/EC2QuerySigner.java +++ /dev/null @@ -1,176 +0,0 @@ -/** - * - * Copyright (C) 2009 Cloud Conscious, LLC. - * - * ==================================================================== - * 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.commands.options; - -import static com.google.common.base.Preconditions.checkState; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.ACTION; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.AWS_ACCESS_KEY_ID; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.EXPIRES; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE_METHOD; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE_VERSION; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.TIMESTAMP; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.VERSION; - -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.UriBuilder; - -import org.jclouds.aws.ec2.reference.CommonEC2Parameters; -import org.jclouds.aws.reference.AWSConstants; -import org.jclouds.http.HttpException; -import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpRequestFilter; -import org.jclouds.http.HttpUtils; -import org.jclouds.util.DateService; - -import javax.inject.Inject; -import javax.inject.Named; - -/** - * Contains the base options needed for all EC2 QUERY API operations.

- * Extend this class in the following way to avoid massive boilerplate code: Usage: - *

- * - *

- * public static class MyRequestOptions extends BaseEC2RequestOptions<MyRequestOptions> {
- *    static {
- *       realClass = MyRequestOptions.class;
- *    }
- * 
- *    @Override
- *    public String getAction() {
- *       return "MyRequest";
- *    }
- * 
- *    public String getId() {
- *       return queryParameters.get("id");
- *    }
- * 
- *    public MyRequestOptions withId(String id) {
- *       encodeAndReplaceParameter("id", id);
- *       return this;
- *    }
- * 
- *    public static class Builder extends BaseEC2RequestOptions.Builder {
- *       public static MyRequestOptions withId(String id) {
- *          MyRequestOptions options = new MyRequestOptions();
- *          return options.withId(id);
- *       }
- *    }
- * }
- * 
- * - * @see
- * @see CommonEC2Parameters - * @author Adrian Cole - * - */ -public class EC2QuerySigner implements HttpRequestFilter { - - public static String[] mandatoryParametersForSignature = new String[] { ACTION, - SIGNATURE_METHOD, SIGNATURE_VERSION, VERSION }; - private final String accessKey; - private final String secretKey; - private final DateService dateService; - - @Inject - public EC2QuerySigner(@Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String accessKey, - @Named(AWSConstants.PROPERTY_AWS_SECRETACCESSKEY) String secretKey, - DateService dateService) { - this.accessKey = accessKey; - this.secretKey = secretKey; - this.dateService = dateService; - } - - public HttpRequest filter(HttpRequest request) throws HttpException { - validateRequest(request); - request = addSigningParamsToRequest(request); - String stringToSign = buildStringToSign(request); - String signature = sign(stringToSign); - return addSignatureToRequest(request, signature); - } - - private void validateRequest(HttpRequest request) { - for (String parameter : mandatoryParametersForSignature) { - checkState(request.getEndpoint().getQuery().contains(parameter), "parameter " + parameter - + " is required for signature"); - } - checkState(request.getHeaders().get(HttpHeaders.HOST) != null, - "request is not ready to sign; host not present"); - } - - private HttpRequest addSignatureToRequest(HttpRequest request, String signature) { - UriBuilder builder = UriBuilder.fromUri(request.getEndpoint()); - builder.queryParam(SIGNATURE, signature); - return new HttpRequest(request.getMethod(), builder.build(), request.getHeaders(), request - .getEntity()); - } - - private String sign(String stringToSign) { - String signature; - try { - signature = HttpUtils.hmacSha256Base64(stringToSign, secretKey.getBytes()); - } catch (Exception e) { - throw new HttpException("error signing request", e); - } - return signature; - } - - private String buildStringToSign(HttpRequest request) { - // 1. Sort the UTF-8 query string components by parameter name with natural byte ordering. - // -- as queryParameters are a SortedSet, they are already sorted. - // 2. URL encode the parameter name and values according to the following rules... - // -- all queryParameters are URL encoded on the way in - // 3. Separate the encoded parameter names from their encoded values with the equals sign, - // even if the parameter value is empty. - // -- we do not allow null values. - // 4. Separate the name-value pairs with an ampersand. - // -- buildQueryString() does this. - StringBuilder toSign = new StringBuilder(); - toSign.append(request.getMethod()).append("\n").append( - request.getEndpoint().getHost().toLowerCase()).append("\n").append("/").append("\n"); - toSign.append(request.getEndpoint().getQuery()); - String stringToSign = toSign.toString(); - return stringToSign; - } - - private HttpRequest addSigningParamsToRequest(HttpRequest request) { - UriBuilder builder = UriBuilder.fromUri(request.getEndpoint()); - builder.queryParam(SIGNATURE_METHOD, "HmacSHA256"); - builder.queryParam(SIGNATURE_VERSION, "2"); - builder.queryParam(VERSION, "2009-04-04"); - - // timestamp is incompatible with expires - if (request.getEndpoint().getQuery().contains(EXPIRES)) { - // TODO tune this if necessary - builder.queryParam(TIMESTAMP, dateService.iso8601DateFormat()); - } - builder.queryParam(AWS_ACCESS_KEY_ID, accessKey); - return new HttpRequest(request.getMethod(), builder.build(), request.getHeaders(), request - .getEntity()); - } - -} diff --git a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/EC2RequestOptions.java b/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/EC2RequestOptions.java deleted file mode 100644 index fd22943ac4..0000000000 --- a/aws/ec2/core/src/main/java/org/jclouds/aws/ec2/commands/options/EC2RequestOptions.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * - * Copyright (C) 2009 Cloud Conscious, LLC. - * - * ==================================================================== - * 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.commands.options; - -import org.jclouds.aws.ec2.reference.EC2Constants; -import org.jclouds.http.options.HttpRequestOptions; -import org.joda.time.DateTime; - -import javax.inject.Named; - -/** - * - * defines the interface needed to properly sign EC2 QUERY requests. - * - * @author Adrian Cole - */ -public interface EC2RequestOptions extends HttpRequestOptions { - - /** - * @see org.jclouds.aws.ec2.reference.CommonEC2Parameters#ACTION - */ - String getAction(); - - /** - * @see org.jclouds.aws.ec2.reference.CommonEC2Parameters#AWS_ACCESS_KEY_ID - * @see org.jclouds.aws.ec2.reference.CommonEC2Parameters#SIGNATURE - */ - EC2RequestOptions signWith(@Named(EC2Constants.PROPERTY_AWS_ACCESSKEYID) String accessKey, - @Named(EC2Constants.PROPERTY_AWS_SECRETACCESSKEY) String secretKey); - - /** - * @see org.jclouds.aws.ec2.reference.CommonEC2Parameters#EXPIRES - */ - EC2RequestOptions expireAt(DateTime time); - - /** - * @see org.jclouds.aws.ec2.reference.CommonEC2Parameters#TIMESTAMP - */ - EC2RequestOptions timeStamp(); - - /** - * @see org.jclouds.http.HttpHeaders#HOST - */ - EC2RequestOptions usingHost(String hostname); - -} diff --git a/aws/ec2/core/src/test/java/org/jclouds/aws/ec2/commands/options/BaseEC2RequestOptionsTest.java b/aws/ec2/core/src/test/java/org/jclouds/aws/ec2/commands/options/BaseEC2RequestOptionsTest.java deleted file mode 100644 index ee11f88293..0000000000 --- a/aws/ec2/core/src/test/java/org/jclouds/aws/ec2/commands/options/BaseEC2RequestOptionsTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * - * Copyright (C) 2009 Cloud Conscious, LLC. - * - * ==================================================================== - * 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.commands.options; - -import static org.testng.Assert.assertEquals; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -import org.jclouds.util.DateService; -import org.joda.time.DateTime; -import org.testng.annotations.Test; - -/** - * Tests possible uses of BaseEC2RequestOptions and BaseEC2RequestOptions.Builder.* - * - * @author Adrian Cole - */ -@Test(groups = "unit", testName = "ec2.BaseEC2RequestOptionsTest") -public class BaseEC2RequestOptionsTest { - - @Test - public void testGetAction() { - EC2RequestOptions options = new MyRequestOptions(); - assertEquals(options.getAction(), "MyRequest"); - } - - public static class MyRequestOptions extends BaseEC2RequestOptions { - static { - realClass = MyRequestOptions.class; - } - - @Override - public String getAction() { - return "MyRequest"; - } - - /** - * @see MyRequestOptions#withId(String) - */ - public String getId() { - return queryParameters.get("id"); - } - - /** - * add the 'id' parameter to the query string - */ - public MyRequestOptions withId(String id) { - encodeAndReplaceParameter("id", id); - return this; - } - - public static class Builder extends BaseEC2RequestOptions.Builder { - /** - * @see MyRequestOptions#withId(String) - */ - public static MyRequestOptions withId(String id) { - MyRequestOptions options = new MyRequestOptions(); - return options.withId(id); - } - } - } - - @Test - public void testExpireAt() throws UnsupportedEncodingException { - DateTime date = new DateTime(); - BaseEC2RequestOptions options = MyRequestOptions.Builder.expireAt(date); - String dateString = URLEncoder.encode(new DateService().iso8601DateFormat(date), "UTF-8"); - makeReady(options); - assert options.buildQueryString().contains("Expires=" + dateString) : String.format( - "%1s$ should have contained %2s$", options.buildQueryString(), dateString); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testBuildQueryWithNotEnoughArguments() { - EC2RequestOptions options = new MyRequestOptions(); - options.buildQueryString(); - } - - @Test - public void testBuildQueryWithEnoughArguments() { - EC2RequestOptions options = new MyRequestOptions(); - makeReady(options); - options.buildQueryString(); - } - - private void makeReady(EC2RequestOptions options) { - options.signWith("meow", "bark"); - options.usingHost("localhost"); - } - - @Test - public void testExpireAtStatic() throws UnsupportedEncodingException { - DateTime date = new DateTime(); - BaseEC2RequestOptions options = MyRequestOptions.Builder.expireAt(date); - String dateString = URLEncoder.encode(new DateService().iso8601DateFormat(date), "UTF-8"); - makeReady(options); - assert options.buildQueryString().contains("Expires=" + dateString) : String.format( - "%1s$ should have contained %2s$", options.buildQueryString(), dateString); - - } - -} diff --git a/aws/ec2/core/src/test/java/org/jclouds/aws/ec2/commands/options/EC2QuerySignerTest.java b/aws/ec2/core/src/test/java/org/jclouds/aws/ec2/commands/options/EC2QuerySignerTest.java deleted file mode 100755 index 12e62095c3..0000000000 --- a/aws/ec2/core/src/test/java/org/jclouds/aws/ec2/commands/options/EC2QuerySignerTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * - * Copyright (C) 2009 Cloud Conscious, LLC. - * - * ==================================================================== - * 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.commands.options; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.ACTION; -import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.EXPIRES; -import static org.testng.Assert.assertEquals; - -import java.net.URI; - -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.UriBuilder; - -import org.jclouds.aws.reference.AWSConstants; -import org.jclouds.http.HttpRequest; -import org.jclouds.util.DateService; -import org.testng.annotations.Test; - -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import org.jclouds.util.Jsr330; - -@Test(groups = "unit", testName = "s3.EC2QuerySignerTest") -public class EC2QuerySignerTest { - - @Test - void testExpires() { - UriBuilder builder = UriBuilder.fromUri(URI.create("https://ec2.amazonaws.com/")); - builder.queryParam(ACTION,"DescribeImages"); - builder.queryParam(EXPIRES,"2008-02-10T12%3A00%3A00Z"); - builder.queryParam("ImageId.1","ami-2bb65342"); - HttpRequest request = new HttpRequest(HttpMethod.GET,builder.build()); - createFilter(); - } - - @Test - void testAclQueryString() { - URI host = URI.create("http://s3.amazonaws.com:80/?acl"); - HttpRequest request = new HttpRequest(HttpMethod.GET, host); - StringBuilder builder = new StringBuilder(); - createFilter().appendUriPath(request, builder); - assertEquals(builder.toString(), "/?acl"); - } - - // "?acl", "?location", "?logging", or "?torrent" - - @Test - void testAppendBucketNameHostHeaderService() { - URI host = URI.create("http://s3.amazonaws.com:80"); - HttpRequest request = new HttpRequest(HttpMethod.GET, host); - request.getHeaders().put(HttpHeaders.HOST, "s3.amazonaws.com"); - StringBuilder builder = new StringBuilder(); - createFilter().appendBucketName(request, builder); - assertEquals(builder.toString(), ""); - } - - @Test - void testAppendBucketNameURIHost() { - URI host = URI.create("http://adriancole.s3int5.s3-external-3.amazonaws.com:80"); - HttpRequest request = new HttpRequest(HttpMethod.GET, host); - StringBuilder builder = new StringBuilder(); - createFilter().appendBucketName(request, builder); - assertEquals(builder.toString(), "/adriancole.s3int5"); - } - - - - private EC2QuerySigner createFilter() { - return Guice.createInjector(new AbstractModule() { - - protected void configure() { - bindConstant().annotatedWith(Jsr330.named(AWSConstants.PROPERTY_AWS_ACCESSKEYID)).to( - "foo"); - bindConstant().annotatedWith(Jsr330.named(AWSConstants.PROPERTY_AWS_SECRETACCESSKEY)).to( - "bar"); - bind(DateService.class); - - } - }).getInstance(EC2QuerySigner.class); - } - -} \ No newline at end of file diff --git a/aws/ec2/pom.xml b/aws/ec2/pom.xml deleted file mode 100644 index 214f47cbdd..0000000000 --- a/aws/ec2/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - jclouds-aws-project - org.jclouds - 1.0-SNAPSHOT - ../pom.xml - - 4.0.0 - jclouds-ec2-project - pom - jclouds ec2 aggregator - - core - - - - ${project.groupId} - jclouds-aws-core - ${project.version} - - - ${project.groupId} - jclouds-aws-core - ${project.version} - test-jar - test - - -