From f7df4af65b8542002368d5a49bbc744653ef2db4 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 17 Jul 2010 10:57:46 -0500 Subject: [PATCH] added windows bundling functionality to ec2 --- .../org/jclouds/aws/ec2/EC2AsyncClient.java | 7 + .../java/org/jclouds/aws/ec2/EC2Client.java | 7 + .../BindBundleIdsToIndexedFormParams.java | 40 +++ .../BindS3UploadPolicyAndSignature.java | 54 ++++ .../aws/ec2/config/EC2RestClientModule.java | 29 +- .../ec2/domain/BundleInstanceS3Storage.java | 152 +++++++++ .../jclouds/aws/ec2/domain/BundleTask.java | 294 ++++++++++++++++++ .../BundleInstanceS3StorageOptions.java | 95 ++++++ .../aws/ec2/services/WindowsAsyncClient.java | 95 ++++++ .../aws/ec2/services/WindowsClient.java | 113 +++++++ .../aws/ec2/xml/AttachmentHandler.java | 2 - .../aws/ec2/xml/BundleTaskHandler.java | 112 +++++++ .../DescribeBundleTasksResponseHandler.java | 57 ++++ .../jclouds/aws/ec2/EC2AsyncClientTest.java | 9 +- .../BundleInstanceS3StorageOptionsTest.java | 74 +++++ .../ec2/services/InstanceAsyncClientTest.java | 6 +- .../ec2/services/WindowsAsyncClientTest.java | 127 ++++++++ .../ec2/services/WindowsClientLiveTest.java | 83 +++++ .../aws/ec2/xml/BundleTaskHandlerTest.java | 76 +++++ ...escribeBundleTasksResponseHandlerTest.java | 64 ++++ .../test/resources/ec2/bundle_instance.xml | 18 ++ .../test/resources/ec2/cancel_bundle_task.xml | 16 + .../resources/ec2/describe_bundle_tasks.xml | 18 ++ .../main/java/org/jclouds/http/HttpUtils.java | 1 - 24 files changed, 1524 insertions(+), 25 deletions(-) create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindBundleIdsToIndexedFormParams.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindS3UploadPolicyAndSignature.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/BundleInstanceS3Storage.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/BundleTask.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/options/BundleInstanceS3StorageOptions.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/services/WindowsAsyncClient.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/services/WindowsClient.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/BundleTaskHandler.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeBundleTasksResponseHandler.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/options/BundleInstanceS3StorageOptionsTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/services/WindowsAsyncClientTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/services/WindowsClientLiveTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/BundleTaskHandlerTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeBundleTasksResponseHandlerTest.java create mode 100644 aws/core/src/test/resources/ec2/bundle_instance.xml create mode 100644 aws/core/src/test/resources/ec2/cancel_bundle_task.xml create mode 100644 aws/core/src/test/resources/ec2/describe_bundle_tasks.xml diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/EC2AsyncClient.java b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2AsyncClient.java index 899ac42be1..1b8f98f062 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/EC2AsyncClient.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2AsyncClient.java @@ -26,6 +26,7 @@ import org.jclouds.aws.ec2.services.InstanceAsyncClient; import org.jclouds.aws.ec2.services.KeyPairAsyncClient; import org.jclouds.aws.ec2.services.MonitoringAsyncClient; import org.jclouds.aws.ec2.services.SecurityGroupAsyncClient; +import org.jclouds.aws.ec2.services.WindowsAsyncClient; import org.jclouds.rest.annotations.Delegate; /** @@ -72,6 +73,12 @@ public interface EC2AsyncClient { @Delegate MonitoringAsyncClient getMonitoringServices(); + /** + * Provides asynchronous access to Windows services. + */ + @Delegate + WindowsAsyncClient getWindowsServices(); + /** * Provides asynchronous access to Availability Zones and Regions services. */ 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 index 0f12d5f14c..25767b5148 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/EC2Client.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/EC2Client.java @@ -28,6 +28,7 @@ import org.jclouds.aws.ec2.services.InstanceClient; import org.jclouds.aws.ec2.services.KeyPairClient; import org.jclouds.aws.ec2.services.MonitoringClient; import org.jclouds.aws.ec2.services.SecurityGroupClient; +import org.jclouds.aws.ec2.services.WindowsClient; import org.jclouds.concurrent.Timeout; import org.jclouds.rest.annotations.Delegate; @@ -74,6 +75,12 @@ public interface EC2Client { @Delegate MonitoringClient getMonitoringServices(); + /** + * Provides asynchronous access to Windows services. + */ + @Delegate + WindowsClient getWindowsServices(); + /** * Provides synchronous access to Availability Zones and Regions services. */ diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindBundleIdsToIndexedFormParams.java b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindBundleIdsToIndexedFormParams.java new file mode 100644 index 0000000000..cf64fa0312 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindBundleIdsToIndexedFormParams.java @@ -0,0 +1,40 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.binders; + +import static org.jclouds.aws.ec2.util.EC2Utils.indexStringArrayToFormValuesWithPrefix; + +import javax.inject.Singleton; + +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; + +/** + * Binds the String [] to form parameters named with BundleId.index + * + * @author Adrian Cole + */ +@Singleton +public class BindBundleIdsToIndexedFormParams implements Binder { + + public void bindToRequest(HttpRequest request, Object input) { + indexStringArrayToFormValuesWithPrefix(request, "BundleId", input); + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindS3UploadPolicyAndSignature.java b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindS3UploadPolicyAndSignature.java new file mode 100644 index 0000000000..3a5c35c05e --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindS3UploadPolicyAndSignature.java @@ -0,0 +1,54 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.binders; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.http.HttpUtils.addFormParamTo; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.aws.filters.FormSigner; +import org.jclouds.encryption.EncryptionService; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class BindS3UploadPolicyAndSignature implements Binder { + private final FormSigner signer; + private final EncryptionService encryptionService; + + @Inject + BindS3UploadPolicyAndSignature(FormSigner signer, EncryptionService encryptionService) { + this.signer = signer; + this.encryptionService = encryptionService; + } + + public void bindToRequest(HttpRequest request, Object input) { + String encodedJson = encryptionService.base64(checkNotNull(input, "json").toString().getBytes()); + addFormParamTo(request, "Storage.S3.UploadPolicy", encodedJson); + String signature = signer.sign(encodedJson); + addFormParamTo(request, "Storage.S3.UploadPolicySignature", signature); + } + +} \ 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 index 121a098b97..2fddc57401 100755 --- 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 @@ -44,6 +44,8 @@ import org.jclouds.aws.ec2.services.MonitoringAsyncClient; import org.jclouds.aws.ec2.services.MonitoringClient; import org.jclouds.aws.ec2.services.SecurityGroupAsyncClient; import org.jclouds.aws.ec2.services.SecurityGroupClient; +import org.jclouds.aws.ec2.services.WindowsAsyncClient; +import org.jclouds.aws.ec2.services.WindowsClient; import org.jclouds.http.RequiresHttp; import org.jclouds.rest.ConfiguresRestClient; @@ -61,17 +63,17 @@ import com.google.inject.Provides; @ConfiguresRestClient public class EC2RestClientModule extends AWSFormSigningRestClientModule { - public static final Map, Class> DELEGATE_MAP = ImmutableMap - ., Class> builder()// - .put(AMIClient.class, AMIAsyncClient.class)// - .put(ElasticIPAddressClient.class, ElasticIPAddressAsyncClient.class)// - .put(InstanceClient.class, InstanceAsyncClient.class)// - .put(KeyPairClient.class, KeyPairAsyncClient.class)// - .put(SecurityGroupClient.class, SecurityGroupAsyncClient.class)// - .put(MonitoringClient.class, MonitoringAsyncClient.class)// - .put(AvailabilityZoneAndRegionClient.class, AvailabilityZoneAndRegionAsyncClient.class)// - .put(ElasticBlockStoreClient.class, ElasticBlockStoreAsyncClient.class)// - .build(); + public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder()// + .put(AMIClient.class, AMIAsyncClient.class)// + .put(ElasticIPAddressClient.class, ElasticIPAddressAsyncClient.class)// + .put(InstanceClient.class, InstanceAsyncClient.class)// + .put(KeyPairClient.class, KeyPairAsyncClient.class)// + .put(SecurityGroupClient.class, SecurityGroupAsyncClient.class)// + .put(MonitoringClient.class, MonitoringAsyncClient.class)// + .put(WindowsClient.class, WindowsAsyncClient.class)// + .put(AvailabilityZoneAndRegionClient.class, AvailabilityZoneAndRegionAsyncClient.class)// + .put(ElasticBlockStoreClient.class, ElasticBlockStoreAsyncClient.class)// + .build(); public EC2RestClientModule() { super(EC2Client.class, EC2AsyncClient.class, DELEGATE_MAP); @@ -99,12 +101,11 @@ public class EC2RestClientModule extends AWSFormSigningRestClientModule provideAvailabilityZoneToRegions(EC2Client client, - @Region Map regions) { + protected Map provideAvailabilityZoneToRegions(EC2Client client, @Region Map regions) { Map map = Maps.newHashMap(); for (String region : regions.keySet()) { for (AvailabilityZoneInfo zoneInfo : client.getAvailabilityZoneAndRegionServices() - .describeAvailabilityZonesInRegion(region)) { + .describeAvailabilityZonesInRegion(region)) { map.put(zoneInfo.getZone(), region); } } diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/BundleInstanceS3Storage.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/BundleInstanceS3Storage.java new file mode 100644 index 0000000000..aeb6ed8fa4 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/BundleInstanceS3Storage.java @@ -0,0 +1,152 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Nullable; + +/** + * + * @see + * @author Adrian Cole + */ +public class BundleInstanceS3Storage { + private final String awsAccessKeyId; + private final String bucket; + private final String prefix; + private final String secretAccessKey; + private final String uploadPolicy; + private final String uploadPolicySignature; + + public BundleInstanceS3Storage(@Nullable String awsAccessKeyId, String bucket, String prefix, + @Nullable String secretAccessKey, @Nullable String uploadPolicy, @Nullable String uploadPolicySignature) { + this.awsAccessKeyId = awsAccessKeyId; + this.bucket = checkNotNull(bucket, "bucket"); + this.prefix = checkNotNull(prefix, "prefix"); + this.secretAccessKey = secretAccessKey; + this.uploadPolicy = uploadPolicy; + this.uploadPolicySignature = uploadPolicySignature; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((awsAccessKeyId == null) ? 0 : awsAccessKeyId.hashCode()); + result = prime * result + ((bucket == null) ? 0 : bucket.hashCode()); + result = prime * result + ((prefix == null) ? 0 : prefix.hashCode()); + result = prime * result + ((secretAccessKey == null) ? 0 : secretAccessKey.hashCode()); + result = prime * result + ((uploadPolicy == null) ? 0 : uploadPolicy.hashCode()); + result = prime * result + ((uploadPolicySignature == null) ? 0 : uploadPolicySignature.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; + BundleInstanceS3Storage other = (BundleInstanceS3Storage) obj; + if (awsAccessKeyId == null) { + if (other.awsAccessKeyId != null) + return false; + } else if (!awsAccessKeyId.equals(other.awsAccessKeyId)) + return false; + if (bucket == null) { + if (other.bucket != null) + return false; + } else if (!bucket.equals(other.bucket)) + return false; + if (prefix == null) { + if (other.prefix != null) + return false; + } else if (!prefix.equals(other.prefix)) + return false; + if (secretAccessKey == null) { + if (other.secretAccessKey != null) + return false; + } else if (!secretAccessKey.equals(other.secretAccessKey)) + return false; + if (uploadPolicy == null) { + if (other.uploadPolicy != null) + return false; + } else if (!uploadPolicy.equals(other.uploadPolicy)) + return false; + if (uploadPolicySignature == null) { + if (other.uploadPolicySignature != null) + return false; + } else if (!uploadPolicySignature.equals(other.uploadPolicySignature)) + return false; + return true; + } + + @Override + public String toString() { + return "[awsAccessKeyId=" + awsAccessKeyId + ", bucket=" + bucket + ", prefix=" + prefix + ", secreAccessKey=" + + secretAccessKey + ", uploadPolicy=" + uploadPolicy + ", uploadPolicySignature=" + uploadPolicySignature + + "]"; + } + + + /** + * + * @returnThe bucket in which to store the AMI. You can specify a bucket that + * you already own or a new bucket that Amazon EC2 creates on your + * behalf. If you specify a bucket that belongs to someone else, + * Amazon EC2 returns an error. + */ + public String getBucket() { + return bucket; + } + + /** + * + * @return Specifies the beginning of the file name of the AMI. + */ + public String getPrefix() { + return prefix; + } + + + + /** + * + * @return An Amazon S3 upload policy that gives Amazon EC2 permission to + * upload items into Amazon S3 on the user's behalf. For more + * information on bundling in Windows, go to the Amazon Elastic + * Compute Cloud Developer Guide and Amazon Elastic Compute Cloud + * Getting Started + */ + public String getUploadPolicy() { + return uploadPolicy; + } + + /** + * + * @return The signature of the Base64 encoded JSON document. + */ + public String getUploadPolicySignature() { + return uploadPolicySignature; + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/BundleTask.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/BundleTask.java new file mode 100644 index 0000000000..bea39c41e9 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/BundleTask.java @@ -0,0 +1,294 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Date; + +import javax.annotation.Nullable; + +/** + * + * @see + * @author Adrian Cole + */ +public class BundleTask implements Comparable { + /** + * {@inheritDoc} + */ + public int compareTo(BundleTask o) { + return (this == o) ? 0 : getBundleId().compareTo(o.getBundleId()); + } + + /** + * If the task fails, a description of the error. + * + * @see + * @author Adrian Cole + */ + public static class Error { + private final String code; + private final String message; + + public Error(String code, String message) { + this.code = checkNotNull(code, "code"); + this.message = checkNotNull(message, "message"); + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + @Override + public String toString() { + return "[code=" + code + ", message=" + message + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((code == null) ? 0 : code.hashCode()); + result = prime * result + ((message == null) ? 0 : message.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; + Error other = (Error) obj; + if (code == null) { + if (other.code != null) + return false; + } else if (!code.equals(other.code)) + return false; + if (message == null) { + if (other.message != null) + return false; + } else if (!message.equals(other.message)) + return false; + return true; + } + + } + + private final String region; + private final String bundleId; + private final Error error; + private final String instanceId; + private final int progress; + private final Date startTime; + private final String state; + private final String bucket; + private final String prefix; + private final Date updateTime; + + public BundleTask(String region, String bundleId, @Nullable Error error, String instanceId, int progress, + Date startTime, String state, String bucket, String prefix, Date updateTime) { + this.region = checkNotNull(region, "region"); + this.bundleId = checkNotNull(bundleId, "bundleId"); + this.error = error; + this.instanceId = checkNotNull(instanceId, "instanceId"); + this.progress = checkNotNull(progress, "progress"); + this.startTime = checkNotNull(startTime, "startTime"); + this.state = checkNotNull(state, "state"); + this.bucket = checkNotNull(bucket, "bucket"); + this.prefix = checkNotNull(prefix, "prefix"); + this.updateTime = checkNotNull(updateTime, "updateTime"); + } + + @Override + public String toString() { + return "[bucket=" + bucket + ", bundleId=" + bundleId + ", error=" + error + ", instanceId=" + instanceId + + ", prefix=" + prefix + ", progress=" + progress + ", region=" + region + ", startTime=" + startTime + + ", state=" + state + ", updateTime=" + updateTime + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bucket == null) ? 0 : bucket.hashCode()); + result = prime * result + ((bundleId == null) ? 0 : bundleId.hashCode()); + result = prime * result + ((error == null) ? 0 : error.hashCode()); + result = prime * result + ((instanceId == null) ? 0 : instanceId.hashCode()); + result = prime * result + ((prefix == null) ? 0 : prefix.hashCode()); + result = prime * result + progress; + result = prime * result + ((region == null) ? 0 : region.hashCode()); + result = prime * result + ((startTime == null) ? 0 : startTime.hashCode()); + result = prime * result + ((state == null) ? 0 : state.hashCode()); + result = prime * result + ((updateTime == null) ? 0 : updateTime.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; + BundleTask other = (BundleTask) obj; + if (bucket == null) { + if (other.bucket != null) + return false; + } else if (!bucket.equals(other.bucket)) + return false; + if (bundleId == null) { + if (other.bundleId != null) + return false; + } else if (!bundleId.equals(other.bundleId)) + return false; + if (error == null) { + if (other.error != null) + return false; + } else if (!error.equals(other.error)) + return false; + if (instanceId == null) { + if (other.instanceId != null) + return false; + } else if (!instanceId.equals(other.instanceId)) + return false; + if (prefix == null) { + if (other.prefix != null) + return false; + } else if (!prefix.equals(other.prefix)) + return false; + if (progress != other.progress) + return false; + if (region == null) { + if (other.region != null) + return false; + } else if (!region.equals(other.region)) + return false; + if (startTime == null) { + if (other.startTime != null) + return false; + } else if (!startTime.equals(other.startTime)) + return false; + if (state == null) { + if (other.state != null) + return false; + } else if (!state.equals(other.state)) + return false; + if (updateTime == null) { + if (other.updateTime != null) + return false; + } else if (!updateTime.equals(other.updateTime)) + return false; + return true; + } + + /** + * + * @return region of the bundle task + */ + public String getRegion() { + return region; + } + + /** + * + * @return The bucket in which to store the AMI. You can specify a bucket + * that you already own or a new bucket that Amazon EC2 creates on + * your behalf. If you specify a bucket that belongs to someone e + * lse, Amazon EC2 returns an error. + */ + public String getBucket() { + return bucket; + } + + /** + * + * @return Specifies the beginning of the file name of the AMI. + */ + public String getPrefix() { + return prefix; + } + + /** + * + * @return Identifier for this task. + */ + public String getBundleId() { + return bundleId; + } + + /** + * + * @return If the task fails, a description of the error. + */ + public Error getError() { + return error; + } + + /** + * + * @return Instance associated with this bundle task + */ + public String getInstanceId() { + return instanceId; + } + + /** + * + * @return A percentage description of the progress of the task, such as 20. + */ + public int getProgress() { + return progress; + } + + /** + * + * @return The time this task started. + */ + public Date getStartTime() { + return startTime; + } + + /** + * + * @return The state of the task. + */ + public String getState() { + return state; + } + + /** + * + * @return The time of the most recent update for the task. + */ + public Date getUpdateTime() { + return updateTime; + } + +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/options/BundleInstanceS3StorageOptions.java b/aws/core/src/main/java/org/jclouds/aws/ec2/options/BundleInstanceS3StorageOptions.java new file mode 100644 index 0000000000..1c5ffd120c --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/options/BundleInstanceS3StorageOptions.java @@ -0,0 +1,95 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.options; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.jclouds.Constants.PROPERTY_IDENTITY; + +import javax.inject.Named; + +import org.jclouds.aws.ec2.options.internal.BaseEC2RequestOptions; + +import com.google.common.collect.Multimap; +import com.google.inject.Inject; + +/** + * Contains options supported in the Form API for the RegisterImage operation. + *

+ * Usage

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

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

+ * EC2Client connection = // get connection + * String imageId = connection.getWindowsServices().bundleInstanceInRegion(...bucketOwnedBy(anotherAccessKey)); + * + * + * @author Adrian Cole + * @see + */ +public class BundleInstanceS3StorageOptions extends BaseEC2RequestOptions { + + @Inject(optional = true) + @Named(PROPERTY_IDENTITY) + String currentAwsAccessKeyId; + + @Override + public Multimap buildFormParameters() { + if (getAwsAccessKeyId() == null) { + checkState(currentAwsAccessKeyId != null, "currentAwsAccessKeyId should have been injected"); + bucketOwnedBy(currentAwsAccessKeyId); + } + return super.buildFormParameters(); + } + + /** + * + * @param awsAccessKeyId + * The Access Key ID of the owner of the Amazon S3 bucket. + */ + public BundleInstanceS3StorageOptions bucketOwnedBy(String awsAccessKeyId) { + formParameters.put("Storage.S3.AWSAccessKeyId", checkNotNull(awsAccessKeyId, "awsAccessKeyId")); + return this; + } + + /** + * + * @return The Access Key ID of the owner of the Amazon S3 bucket. + */ + public String getAwsAccessKeyId() { + return getFirstFormOrNull("Storage.S3.AWSAccessKeyId"); + } + + public static class Builder { + /** + * @see BundleInstanceS3StorageOptions#bucketOwnedBy(awsAccessKeyId) + */ + public static BundleInstanceS3StorageOptions bucketOwnedBy(String awsAccessKeyId) { + BundleInstanceS3StorageOptions options = new BundleInstanceS3StorageOptions(); + return options.bucketOwnedBy(awsAccessKeyId); + } + + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/services/WindowsAsyncClient.java b/aws/core/src/main/java/org/jclouds/aws/ec2/services/WindowsAsyncClient.java new file mode 100644 index 0000000000..dd7f75b917 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/services/WindowsAsyncClient.java @@ -0,0 +1,95 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.services; + +import static org.jclouds.aws.ec2.reference.EC2Parameters.ACTION; +import static org.jclouds.aws.ec2.reference.EC2Parameters.VERSION; + +import java.util.Set; + +import javax.annotation.Nullable; +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import org.jclouds.aws.ec2.EC2AsyncClient; +import org.jclouds.aws.ec2.binders.BindBundleIdsToIndexedFormParams; +import org.jclouds.aws.ec2.binders.BindS3UploadPolicyAndSignature; +import org.jclouds.aws.ec2.domain.BundleTask; +import org.jclouds.aws.ec2.options.BundleInstanceS3StorageOptions; +import org.jclouds.aws.ec2.xml.BundleTaskHandler; +import org.jclouds.aws.ec2.xml.DescribeBundleTasksResponseHandler; +import org.jclouds.aws.filters.FormSigner; +import org.jclouds.aws.functions.RegionToEndpoint; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.FormParams; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.VirtualHost; +import org.jclouds.rest.annotations.XMLResponseParser; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides access to EC2 Windows via their REST API. + *

+ * + * @author Adrian Cole + */ +@RequestFilters(FormSigner.class) +@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION) +@VirtualHost +public interface WindowsAsyncClient { + + /** + * @see WindowsClient#bundleInstanceInRegion + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "BundleInstance") + @XMLResponseParser(BundleTaskHandler.class) + ListenableFuture bundleInstanceInRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region, + @FormParam("InstanceId") String instanceId, @FormParam("Storage.S3.Prefix") String prefix, + @FormParam("Storage.S3.Bucket") String bucket, + @BinderParam(BindS3UploadPolicyAndSignature.class) String uploadPolicy, + BundleInstanceS3StorageOptions... options); + + /** + * @see WindowsClient#cancelBundleTaskInRegion + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "CancelBundleTask") + @XMLResponseParser(BundleTaskHandler.class) + ListenableFuture cancelBundleTaskInRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region, @FormParam("BundleId") String bundleId); + + /** + * @see BundleTaskClient#describeBundleTasksInRegion + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DescribeBundleTasks") + @XMLResponseParser(DescribeBundleTasksResponseHandler.class) + ListenableFuture> describeBundleTasksInRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region, + @BinderParam(BindBundleIdsToIndexedFormParams.class) String... bundleTaskIds); + +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/services/WindowsClient.java b/aws/core/src/main/java/org/jclouds/aws/ec2/services/WindowsClient.java new file mode 100644 index 0000000000..d3d9c30a59 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/services/WindowsClient.java @@ -0,0 +1,113 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.services; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nullable; + +import org.jclouds.aws.ec2.domain.BundleTask; +import org.jclouds.aws.ec2.domain.Reservation; +import org.jclouds.aws.ec2.options.BundleInstanceS3StorageOptions; +import org.jclouds.concurrent.Timeout; + +/** + * Provides windows services for EC2. For more information, refer to the Amazon + * EC2 Developer Guide. + *

+ * + * @author Adrian Cole + */ +@Timeout(duration = 45, timeUnit = TimeUnit.SECONDS) +public interface WindowsClient { + /** + * Bundles the Windows instance. This procedure is not applicable for Linux + * and UNIX instances. For more information, go to the Amazon Elastic Compute + * Cloud Developer Guide or Amazon Elastic Compute Cloud Getting Started + * Guide. + * + * @param region + * Bundles are tied to the Region where its files are located + * within Amazon S3. + * + * @param instanceId + * The ID of the instance to bundle. + * @param prefix + * Specifies the beginning of the file name of the AMI. + * @param bucket + * The bucket in which to store the AMI. You can specify a bucket + * that you already own or a new bucket that Amazon EC2 creates on + * your behalf. If you specify a bucket that belongs to som eone + * else, Amazon EC2 returns an error. + * @param uploadPolicy + * An Amazon S3 upload policy that gives Amazon EC2 permission to + * upload items into Amazon S3 on the user's behalf. + *

+ * ex. + * + *

+    * {"expiration": "2008-08-30T08:49:09Z","conditions": ["bucket": "my-bucket"},["starts-with", "$key", "my-new-image"]]}
+    * 
+ * + * @param options + * if the bucket isn't owned by you, use this to set the bucket's + * accesskeyid + * @return status of the work + * + * @see #cancelBundleTaskInRegion + * @see #describeBundleTasksInRegion + * + * @see + */ + BundleTask bundleInstanceInRegion(@Nullable String region, String instanceId, String prefix, String bucket, + String uploadPolicy, BundleInstanceS3StorageOptions... options); + + /** + * Cancels an Amazon EC2 bundling operation. + * + * @param region + * The bundleTask ID is tied to the Region. + * @param bundleId + * The ID of the bundle task to cancel. + * @return task for the cancel. + * + * @see #bundleInstanceInRegion + * @see #describeBundleTasksInRegion + * + * @see + */ + BundleTask cancelBundleTaskInRegion(@Nullable String region, String bundleId); + + /** + * + * Describes current bundling tasks. + * + * @param region + * The bundleTask ID is tied to the Region. + * + * @see #cancelBundleTaskInRegion + * @see #bundleInstanceInRegion + * @see + */ + Set describeBundleTasksInRegion(@Nullable String region, String... bundleTaskIds); +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/AttachmentHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/AttachmentHandler.java index 39f8ab347f..82a19db3fa 100644 --- a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/AttachmentHandler.java +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/AttachmentHandler.java @@ -61,10 +61,8 @@ public class AttachmentHandler extends ParseSax.HandlerForGeneratedRequestWithRe public void endElement(String uri, String name, String qName) { if (qName.equals("volumeId")) { volumeId = currentText.toString().trim(); - } else if (qName.equals("volumeId")) { volumeId = currentText.toString().trim(); - } else if (qName.equals("status")) { attachmentStatus = Attachment.Status.fromValue(currentText.toString().trim()); } else if (qName.equals("instanceId")) { diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/BundleTaskHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/BundleTaskHandler.java new file mode 100644 index 0000000000..f17987e09c --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/BundleTaskHandler.java @@ -0,0 +1,112 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.xml; + +import java.util.Date; + +import javax.annotation.Resource; +import javax.inject.Inject; + +import org.jclouds.aws.Region; +import org.jclouds.aws.ec2.domain.BundleTask; +import org.jclouds.aws.ec2.util.EC2Utils; +import org.jclouds.date.DateService; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.logging.Logger; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +/** + * + * @author Adrian Cole + */ +public class BundleTaskHandler extends ParseSax.HandlerForGeneratedRequestWithResult { + private StringBuilder currentText = new StringBuilder(); + + @Resource + protected Logger logger = Logger.NULL; + @Inject + protected DateService dateService; + @Inject + @Region + String defaultRegion; + + private String bundleId; + private String code; + private String message; + private String instanceId; + private int progress = 0; + private Date startTime; + private String state; + private String bucket; + private String prefix; + private Date updateTime; + + public BundleTask getResult() { + String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest) request); + if (region == null) + region = defaultRegion; + BundleTask.Error error = null; + if (code != null) + error = new BundleTask.Error(code, message); + BundleTask returnVal = new BundleTask(region, bundleId, error, instanceId, progress, startTime, + state, bucket, prefix, updateTime); + this.bundleId = null; + this.code = null; + this.message = null; + this.instanceId = null; + this.progress = 0; + this.startTime = null; + this.state = null; + this.bucket = null; + this.prefix = null; + this.updateTime = null; + return returnVal; + } + + public void endElement(String uri, String name, String qName) { + if (qName.equals("bundleId")) { + bundleId = currentText.toString().trim(); + } else if (qName.equals("code")) { + code = currentText.toString().trim(); + } else if (qName.equals("message")) { + message = currentText.toString().trim(); + } else if (qName.equals("instanceId")) { + instanceId = currentText.toString().trim(); + } else if (qName.equals("progress")) { + String temp = currentText.toString().trim(); + temp = temp.substring(0, temp.length() - 1); + progress = Integer.parseInt(temp); + } else if (qName.equals("startTime")) { + startTime = dateService.iso8601DateParse(currentText.toString().trim()); + } else if (qName.equals("state")) { + state = currentText.toString().trim(); + } else if (qName.equals("bucket")) { + bucket = currentText.toString().trim(); + } else if (qName.equals("prefix")) { + prefix = currentText.toString().trim(); + } else if (qName.equals("updateTime")) { + updateTime = dateService.iso8601DateParse(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/DescribeBundleTasksResponseHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeBundleTasksResponseHandler.java new file mode 100644 index 0000000000..5ed50bb749 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeBundleTasksResponseHandler.java @@ -0,0 +1,57 @@ +package org.jclouds.aws.ec2.xml; + +import java.util.Set; + +import javax.inject.Inject; + +import org.jclouds.aws.ec2.domain.BundleTask; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ParseSax.HandlerWithResult; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +import com.google.common.collect.Sets; + +/** + * @author Adrian Cole + */ +public class DescribeBundleTasksResponseHandler extends ParseSax.HandlerWithResult> { + + private Set bundleTasks = Sets.newLinkedHashSet(); + private final BundleTaskHandler bundleTaskHandler; + + @Inject + public DescribeBundleTasksResponseHandler(BundleTaskHandler bundleTaskHandler) { + this.bundleTaskHandler = bundleTaskHandler; + } + + public Set getResult() { + return bundleTasks; + } + + @Override + public HandlerWithResult> setContext(HttpRequest request) { + bundleTaskHandler.setContext(request); + return super.setContext(request); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (!qName.equals("item")) + bundleTaskHandler.startElement(uri, localName, qName, attributes); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (qName.equals("item")) { + bundleTasks.add(bundleTaskHandler.getResult()); + } + bundleTaskHandler.endElement(uri, localName, qName); + } + + public void characters(char ch[], int start, int length) { + bundleTaskHandler.characters(ch, start, length); + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/EC2AsyncClientTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/EC2AsyncClientTest.java index f5cb4b3698..6b99f8469a 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/EC2AsyncClientTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/EC2AsyncClientTest.java @@ -22,8 +22,7 @@ public class EC2AsyncClientTest extends BaseEC2AsyncClientTest { private EC2AsyncClient asyncClient; private EC2Client syncClient; - public void testSync() throws SecurityException, NoSuchMethodException, InterruptedException, - ExecutionException { + public void testSync() throws SecurityException, NoSuchMethodException, InterruptedException, ExecutionException { assert syncClient.getAMIServices() != null; assert syncClient.getAvailabilityZoneAndRegionServices() != null; assert syncClient.getElasticBlockStoreServices() != null; @@ -32,10 +31,11 @@ public class EC2AsyncClientTest extends BaseEC2AsyncClientTest { assert syncClient.getKeyPairServices() != null; assert syncClient.getMonitoringServices() != null; assert syncClient.getSecurityGroupServices() != null; + assert syncClient.getWindowsServices() != null; + } - public void testAsync() throws SecurityException, NoSuchMethodException, InterruptedException, - ExecutionException { + public void testAsync() throws SecurityException, NoSuchMethodException, InterruptedException, ExecutionException { assert asyncClient.getAMIServices() != null; assert asyncClient.getAvailabilityZoneAndRegionServices() != null; assert asyncClient.getElasticBlockStoreServices() != null; @@ -44,6 +44,7 @@ public class EC2AsyncClientTest extends BaseEC2AsyncClientTest { assert asyncClient.getKeyPairServices() != null; assert asyncClient.getMonitoringServices() != null; assert asyncClient.getSecurityGroupServices() != null; + assert asyncClient.getWindowsServices() != null; } @Override diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/options/BundleInstanceS3StorageOptionsTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/options/BundleInstanceS3StorageOptionsTest.java new file mode 100644 index 0000000000..eaf89709a4 --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/options/BundleInstanceS3StorageOptionsTest.java @@ -0,0 +1,74 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.options; + +import static java.util.Collections.EMPTY_LIST; +import static java.util.Collections.singleton; +import static org.jclouds.aws.ec2.options.BundleInstanceS3StorageOptions.Builder.bucketOwnedBy; +import static org.testng.Assert.assertEquals; + +import org.jclouds.http.options.HttpRequestOptions; +import org.testng.annotations.Test; + +/** + * Tests possible uses of BundleInstanceS3StorageOptions and + * BundleInstanceS3StorageOptions.Builder.* + * + * @author Adrian Cole + */ +public class BundleInstanceS3StorageOptionsTest { + + @Test + public void testAssignability() { + assert HttpRequestOptions.class.isAssignableFrom(BundleInstanceS3StorageOptions.class); + assert !String.class.isAssignableFrom(BundleInstanceS3StorageOptions.class); + } + + @Test + public void testBucketOwnedBy() { + BundleInstanceS3StorageOptions options = new BundleInstanceS3StorageOptions(); + options.bucketOwnedBy("test"); + assertEquals(options.buildFormParameters().get("Storage.S3.AWSAccessKeyId"), singleton("test")); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testNullBucketOwnedByNotInjected() { + BundleInstanceS3StorageOptions options = new BundleInstanceS3StorageOptions(); + assertEquals(options.buildFormParameters().get("Storage.S3.AWSAccessKeyId"), EMPTY_LIST); + } + + @Test + public void testNullBucketOwnedBy() { + BundleInstanceS3StorageOptions options = new BundleInstanceS3StorageOptions(); + options.currentAwsAccessKeyId = "foo"; + assertEquals(options.buildFormParameters().get("Storage.S3.AWSAccessKeyId"), singleton("foo")); + } + + @Test + public void testBucketOwnedByStatic() { + BundleInstanceS3StorageOptions options = bucketOwnedBy("test"); + assertEquals(options.buildFormParameters().get("Storage.S3.AWSAccessKeyId"), singleton("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testBucketOwnedByNPE() { + bucketOwnedBy(null); + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/services/InstanceAsyncClientTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/services/InstanceAsyncClientTest.java index 3b8b1889b9..8ea65b1064 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/services/InstanceAsyncClientTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/services/InstanceAsyncClientTest.java @@ -54,8 +54,7 @@ import com.google.inject.TypeLiteral; @Test(groups = "unit", testName = "ec2.InstanceAsyncClientTest") public class InstanceAsyncClientTest extends BaseEC2AsyncClientTest { public void testDescribeInstances() throws SecurityException, NoSuchMethodException, IOException { - Method method = InstanceAsyncClient.class.getMethod("describeInstancesInRegion", String.class, Array.newInstance( - String.class, 0).getClass()); + Method method = InstanceAsyncClient.class.getMethod("describeInstancesInRegion", String.class, String[].class); HttpRequest request = processor.createRequest(method, (String) null); assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); @@ -71,8 +70,7 @@ public class InstanceAsyncClientTest extends BaseEC2AsyncClientTest + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.services; + +import java.io.IOException; +import java.lang.reflect.Method; + +import org.jclouds.aws.ec2.options.BundleInstanceS3StorageOptions; +import org.jclouds.aws.ec2.xml.BundleTaskHandler; +import org.jclouds.aws.ec2.xml.DescribeBundleTasksResponseHandler; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.testng.annotations.Test; + +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of {@code WindowsAsyncClient} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.WindowsAsyncClientTest") +public class WindowsAsyncClientTest extends BaseEC2AsyncClientTest { + + public void testBundleInstanceInRegion() throws SecurityException, NoSuchMethodException, IOException { + Method method = WindowsAsyncClient.class.getMethod("bundleInstanceInRegion", String.class, String.class, + String.class, String.class, String.class, BundleInstanceS3StorageOptions[].class); + HttpRequest request = processor + .createRequest( + method, + null, + "i-e468cd8d", + "winami", + "my-bucket", + "{\"expiration\": \"2008-08-30T08:49:09Z\",\"conditions\": [{\"bucket\": \"my-bucket\"},[\"starts-with\", \"$key\", \"my-new-image\"]]}"); + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + String payload = "Version=2010-06-15&Action=BundleInstance&Storage.S3.Prefix=winami&InstanceId=i-e468cd8d&Storage.S3.Bucket=my-bucket&Storage.S3.UploadPolicy=eyJleHBpcmF0aW9uIjogIjIwMDgtMDgtMzBUMDg6NDk6MDlaIiwiY29uZGl0aW9ucyI6IFt7ImJ1Y2tldCI6ICJteS1idWNrZXQifSxbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAibXktbmV3LWltYWdlIl1dfQ%3D%3D&Storage.S3.UploadPolicySignature=ih%2FiohGe0A7y4QVRbKaq6BZShzUsmBEJEa9AdFbxM6Y%3D"; + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, payload, "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, BundleTaskHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(request); + } + + public void testBundleInstanceInRegionOptions() throws SecurityException, NoSuchMethodException, IOException { + Method method = WindowsAsyncClient.class.getMethod("bundleInstanceInRegion", String.class, String.class, + String.class, String.class, String.class, BundleInstanceS3StorageOptions[].class); + HttpRequest request = processor + .createRequest( + method, + null, + "i-e468cd8d", + "winami", + "my-bucket", + "{\"expiration\": \"2008-08-30T08:49:09Z\",\"conditions\": [{\"bucket\": \"my-bucket\"},[\"starts-with\", \"$key\", \"my-new-image\"]]}", + BundleInstanceS3StorageOptions.Builder.bucketOwnedBy("10QMXFEV71ZS32XQFTR2")); + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + String payload = "Version=2010-06-15&Action=BundleInstance&Storage.S3.Prefix=winami&InstanceId=i-e468cd8d&Storage.S3.Bucket=my-bucket&Storage.S3.AWSAccessKeyId=10QMXFEV71ZS32XQFTR2&Storage.S3.UploadPolicy=eyJleHBpcmF0aW9uIjogIjIwMDgtMDgtMzBUMDg6NDk6MDlaIiwiY29uZGl0aW9ucyI6IFt7ImJ1Y2tldCI6ICJteS1idWNrZXQifSxbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAibXktbmV3LWltYWdlIl1dfQ%3D%3D&Storage.S3.UploadPolicySignature=ih%2FiohGe0A7y4QVRbKaq6BZShzUsmBEJEa9AdFbxM6Y%3D"; + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, payload, "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, BundleTaskHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(request); + } + + public void testDescribeBundleTasks() throws SecurityException, NoSuchMethodException, IOException { + Method method = WindowsAsyncClient.class.getMethod("describeBundleTasksInRegion", String.class, String[].class); + HttpRequest request = processor.createRequest(method, (String) null); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeBundleTasks", "application/x-www-form-urlencoded", + false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeBundleTasksResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(request); + } + + public void testDescribeBundleTasksArgs() throws SecurityException, NoSuchMethodException, IOException { + Method method = WindowsAsyncClient.class.getMethod("describeBundleTasksInRegion", String.class, String[].class); + HttpRequest request = processor.createRequest(method, null, "1", "2"); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeBundleTasks&BundleId.1=1&BundleId.2=2", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeBundleTasksResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(request); + } + + @Override + protected TypeLiteral> createTypeLiteral() { + return new TypeLiteral>() { + }; + } + +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/services/WindowsClientLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/services/WindowsClientLiveTest.java new file mode 100644 index 0000000000..03304259da --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/services/WindowsClientLiveTest.java @@ -0,0 +1,83 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.services; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; + +import org.jclouds.aws.ec2.EC2AsyncClient; +import org.jclouds.aws.ec2.EC2Client; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.rest.RestContext; +import org.jclouds.rest.RestContextFactory; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; + +/** + * Tests behavior of {@code WindowsClient} + * + * @author Adrian Cole + */ +@Test(groups = "live", sequential = true, testName = "ec2.WindowsClientLiveTest") +public class WindowsClientLiveTest { + + private WindowsClient client; + private static final String DEFAULT_INSTANCE = "i-TODO"; + private static final String DEFAULT_BUCKET = "TODO"; + + private RestContext context; + + @BeforeGroups(groups = { "live" }) + public void setupClient() throws IOException { + String identity = checkNotNull(System.getProperty("jclouds.test.identity"), "jclouds.test.identity"); + String credential = checkNotNull(System.getProperty("jclouds.test.credential"), "jclouds.test.credential"); + + context = new RestContextFactory().createContext("ec2", identity, credential, ImmutableSet + . of(new Log4JLoggingModule())); + client = context.getApi().getWindowsServices(); + } + + @Test(enabled = false) + // TODO get instance + public void testBundleInstanceInRegion() { + client + .bundleInstanceInRegion( + null, + DEFAULT_INSTANCE, + "prefix", + DEFAULT_BUCKET, + "{\"expiration\": \"2008-08-30T08:49:09Z\",\"conditions\": [{\"bucket\": \"my-bucket\"},[\"starts-with\", \"$key\", \"my-new-image\"]]}"); + } + + @Test(enabled = false) + // TODO get instance + public void testCancelBundleTaskInRegion() { + + } + + @Test(enabled = false) + // TODO get instance + public void testDescribeBundleTasksInRegion() { + + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/BundleTaskHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/BundleTaskHandlerTest.java new file mode 100644 index 0000000000..1267e337af --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/BundleTaskHandlerTest.java @@ -0,0 +1,76 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.xml; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; + +import org.jclouds.aws.ec2.domain.BundleTask; +import org.jclouds.date.DateService; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code BundleTaskHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.BundleTaskHandlerTest") +public class BundleTaskHandlerTest extends BaseEC2HandlerTest { + public void testBundleInstance() { + DateService dateService = injector.getInstance(DateService.class); + InputStream is = getClass().getResourceAsStream("/ec2/bundle_instance.xml"); + + BundleTask expected = new BundleTask(defaultRegion, "bun-c1a540a8", null, "i-12345678", 70, dateService + .iso8601DateParse("2008-10-07T11:41:50.000Z"), "bundling", "my-bucket", "winami", dateService + .iso8601DateParse("2008-10-07T11:51:50.000Z")); + + BundleTaskHandler handler = injector.getInstance(BundleTaskHandler.class); + addDefaultRegionToHandler(handler); + BundleTask result = factory.create(handler).parse(is); + + assertEquals(result, expected); + } + + public void testCancleBundleTask() { + DateService dateService = injector.getInstance(DateService.class); + InputStream is = getClass().getResourceAsStream("/ec2/cancel_bundle_task.xml"); + BundleTask expected = new BundleTask(defaultRegion, "bun-cla322b9", null, "i-12345678", 20, dateService + .iso8601DateParse("2008-10-07T11:41:50.000Z"), "canceling", "my-bucket", "my-new-image", dateService + .iso8601DateParse("2008-10-07T11:51:50.000Z")); + + BundleTaskHandler handler = injector.getInstance(BundleTaskHandler.class); + addDefaultRegionToHandler(handler); + BundleTask result = factory.create(handler).parse(is); + + assertEquals(result, expected); + } + + private void addDefaultRegionToHandler(ParseSax.HandlerWithResult handler) { + GeneratedHttpRequest request = createMock(GeneratedHttpRequest.class); + expect(request.getArgs()).andReturn(new Object[] { null }); + replay(request); + handler.setContext(request); + } +} \ No newline at end of file diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeBundleTasksResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeBundleTasksResponseHandlerTest.java new file mode 100644 index 0000000000..1c8bfdfc5a --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeBundleTasksResponseHandlerTest.java @@ -0,0 +1,64 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.aws.ec2.xml; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; + +import org.jclouds.aws.ec2.domain.BundleTask; +import org.jclouds.date.DateService; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.testng.annotations.Test; + +import com.google.inject.internal.Iterables; + +/** + * Tests behavior of {@code DescribeBundleTasksResponseHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.DescribeBundleTasksResponseHandlerTest") +public class DescribeBundleTasksResponseHandlerTest extends BaseEC2HandlerTest { + public void testApplyInputStream() { + DateService dateService = injector.getInstance(DateService.class); + InputStream is = getClass().getResourceAsStream("/ec2/describe_bundle_tasks.xml"); + + BundleTask expected = new BundleTask(defaultRegion, "bun-c1a540a8", null, "i-12345678", 20, dateService + .iso8601DateParse("2008-10-07T11:41:50.000Z"), "canceling", "my-bucket", "winami", dateService + .iso8601DateParse("2008-10-07T11:51:50.000Z")); + + DescribeBundleTasksResponseHandler handler = injector.getInstance(DescribeBundleTasksResponseHandler.class); + addDefaultRegionToHandler(handler); + BundleTask result = Iterables.getOnlyElement(factory.create(handler).parse(is)); + + assertEquals(result, expected); + } + + private void addDefaultRegionToHandler(ParseSax.HandlerWithResult handler) { + GeneratedHttpRequest request = createMock(GeneratedHttpRequest.class); + expect(request.getArgs()).andReturn(new Object[] { null }); + replay(request); + handler.setContext(request); + } +} \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/bundle_instance.xml b/aws/core/src/test/resources/ec2/bundle_instance.xml new file mode 100644 index 0000000000..326b8ba71f --- /dev/null +++ b/aws/core/src/test/resources/ec2/bundle_instance.xml @@ -0,0 +1,18 @@ + + bun-c1a540a8 + + i-12345678 + bun-c1a540a8 + bundling + 2008-10-07T11:41:50.000Z + 2008-10-07T11:51:50.000Z + 70% + + + my-bucket + winami + + + + + diff --git a/aws/core/src/test/resources/ec2/cancel_bundle_task.xml b/aws/core/src/test/resources/ec2/cancel_bundle_task.xml new file mode 100644 index 0000000000..da737f24bc --- /dev/null +++ b/aws/core/src/test/resources/ec2/cancel_bundle_task.xml @@ -0,0 +1,16 @@ + + + i-12345678 + bun-cla322b9 + canceling + 2008-10-07T11:41:50.000Z + 2008-10-07T11:51:50.000Z + 20% + + + my-bucket + my-new-image + + + + \ No newline at end of file diff --git a/aws/core/src/test/resources/ec2/describe_bundle_tasks.xml b/aws/core/src/test/resources/ec2/describe_bundle_tasks.xml new file mode 100644 index 0000000000..d45e8f6e89 --- /dev/null +++ b/aws/core/src/test/resources/ec2/describe_bundle_tasks.xml @@ -0,0 +1,18 @@ + + + + i-12345678 + bun-c1a540a8 + canceling + 2008-10-07T11:41:50.000Z + 2008-10-07T11:51:50.000Z + 20% + + + my-bucket + winami + + + + + \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/http/HttpUtils.java b/core/src/main/java/org/jclouds/http/HttpUtils.java index 3e67b1e02a..87da781ebe 100644 --- a/core/src/main/java/org/jclouds/http/HttpUtils.java +++ b/core/src/main/java/org/jclouds/http/HttpUtils.java @@ -417,7 +417,6 @@ public class HttpUtils { public static void addFormParamTo(HttpRequest request, String key, String value) { addFormParamTo(request, key, ImmutableSet. of(value)); - } public static void addFormParamTo(HttpRequest request, String key, Iterable values) {