From 7c9fb31d0ff6b6b6937382dc4e85b7666f044754 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Wed, 1 Feb 2012 16:57:05 +0000 Subject: [PATCH 1/6] EC2ImageParser was not setting the name --- .../org/jclouds/ec2/compute/functions/EC2ImageParser.java | 1 + .../jclouds/ec2/compute/functions/EC2ImageParserTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java index 1cdaf72b5e..7f14e25ad5 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java @@ -86,6 +86,7 @@ public class EC2ImageParser implements Function builder().put("owner", from.getImageOwnerId()).put( "rootDeviceType", from.getRootDeviceType().value()).put("virtualizationType", diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2ImageParserTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2ImageParserTest.java index 54dafaa650..147210fdcc 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2ImageParserTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2ImageParserTest.java @@ -61,16 +61,16 @@ public class EC2ImageParserTest { assertEquals(Iterables.get(result, 0), new ImageBuilder().operatingSystem( new OperatingSystem.Builder().family(OsFamily.UNRECOGNIZED).arch("paravirtual").version("").description( "137112412989/amzn-ami-0.9.7-beta.i386-ebs").is64Bit(false).build()).description("Amazon") - .defaultCredentials(new LoginCredentials("ec2-user", false)).id("us-east-1/ami-82e4b5c7").providerId( - "ami-82e4b5c7").location(defaultLocation).userMetadata( + .defaultCredentials(new LoginCredentials("ec2-user", false)).id("us-east-1/ami-82e4b5c7").name("amzn-ami-0.9.7-beta.i386-ebs") + .providerId("ami-82e4b5c7").location(defaultLocation).userMetadata( ImmutableMap.of("owner", "137112412989", "rootDeviceType", "ebs")).build()); assertEquals(Iterables.get(result, 3), new ImageBuilder().operatingSystem( new OperatingSystem.Builder().family(OsFamily.UNRECOGNIZED).arch("paravirtual").version("").description( "amzn-ami-us-west-1/amzn-ami-0.9.7-beta.x86_64.manifest.xml").is64Bit(true).build()) .description("Amazon Linux AMI x86_64 S3").defaultCredentials(new LoginCredentials("ec2-user", false)).id( - "us-east-1/ami-f2e4b5b7").providerId("ami-f2e4b5b7").location(defaultLocation).userMetadata( - ImmutableMap.of("owner", "137112412989", "rootDeviceType", "ebs")).build()); + "us-east-1/ami-f2e4b5b7").providerId("ami-f2e4b5b7").name("amzn-ami-0.9.7-beta.x86_64-S3").location(defaultLocation) + .userMetadata(ImmutableMap.of("owner", "137112412989", "rootDeviceType", "ebs")).build()); } static Location defaultLocation = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1").description( From a50c09283613b2f6a6f4fbe105348e4f2b46feac Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Wed, 1 Feb 2012 17:03:00 +0000 Subject: [PATCH 2/6] Add the EC2 API call "GetPasswordData" to retrieve the encrypted Windows Administrator password. --- .../org/jclouds/ec2/domain/PasswordData.java | 129 ++++++++++++++++++ .../ec2/services/WindowsAsyncClient.java | 13 ++ .../jclouds/ec2/services/WindowsClient.java | 11 ++ .../xml/GetPasswordDataResponseHandler.java | 58 ++++++++ 4 files changed, 211 insertions(+) create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/domain/PasswordData.java create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/xml/GetPasswordDataResponseHandler.java diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/PasswordData.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/PasswordData.java new file mode 100644 index 0000000000..cea5639dd5 --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/PasswordData.java @@ -0,0 +1,129 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.ec2.domain; + +import java.util.Date; + +/** + * Holds the encrypted Windows Administrator password for an instance. + * + * @author Richard Downer + */ +public class PasswordData { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String requestId; + private String instanceId; + private Date timestamp; + private String passwordData; + + private Builder() {} + + public Builder requestId(String requestId) { + this.requestId = requestId; + return this; + } + + public Builder instanceId(String instanceId) { + this.instanceId = instanceId; + return this; + } + + public Builder timestamp(Date timestamp) { + this.timestamp = timestamp; + return this; + } + + public Builder passwordData(String passwordData) { + this.passwordData = passwordData; + return this; + } + + public PasswordData build() { + return new PasswordData(requestId, instanceId, timestamp, passwordData); + } + } + + private String requestId; + private String instanceId; + private Date timestamp; + private String passwordData; + + public PasswordData(String requestId, String instanceId, Date timestamp, String passwordData) { + this.requestId = requestId; + this.instanceId = instanceId; + this.timestamp = timestamp; + this.passwordData = passwordData; + } + + public String getRequestId() { + return requestId; + } + + public String getInstanceId() { + return instanceId; + } + + public Date getTimestamp() { + return timestamp; + } + + public String getPasswordData() { + return passwordData; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PasswordData that = (PasswordData) o; + + if (instanceId != null ? !instanceId.equals(that.instanceId) : that.instanceId != null) return false; + if (passwordData != null ? !passwordData.equals(that.passwordData) : that.passwordData != null) return false; + if (requestId != null ? !requestId.equals(that.requestId) : that.requestId != null) return false; + if (timestamp != null ? !timestamp.equals(that.timestamp) : that.timestamp != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = requestId != null ? requestId.hashCode() : 0; + result = 31 * result + (instanceId != null ? instanceId.hashCode() : 0); + result = 31 * result + (timestamp != null ? timestamp.hashCode() : 0); + result = 31 * result + (passwordData != null ? passwordData.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "PasswordData{" + + "requestId='" + requestId + '\'' + + ", instanceId='" + instanceId + '\'' + + ", timestamp=" + timestamp + + ", passwordData='" + passwordData + '\'' + + '}'; + } +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java index 5710656611..c5e2383c73 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java @@ -23,6 +23,8 @@ import static org.jclouds.aws.reference.FormParameters.VERSION; import java.util.Set; +import org.jclouds.ec2.domain.PasswordData; +import org.jclouds.ec2.xml.GetPasswordDataResponseHandler; import org.jclouds.javax.annotation.Nullable; import javax.ws.rs.FormParam; import javax.ws.rs.POST; @@ -96,4 +98,15 @@ public interface WindowsAsyncClient { @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, @BinderParam(BindBundleIdsToIndexedFormParams.class) String... bundleTaskIds); + /** + * @see WindowsClient#getPasswordData + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "GetPasswordData") + @XMLResponseParser(GetPasswordDataResponseHandler.class) + ListenableFuture getPasswordData( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); + } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsClient.java index ae04093cea..e22284b118 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsClient.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsClient.java @@ -21,6 +21,7 @@ package org.jclouds.ec2.services; import java.util.Set; import java.util.concurrent.TimeUnit; +import org.jclouds.ec2.domain.PasswordData; import org.jclouds.javax.annotation.Nullable; import org.jclouds.ec2.domain.BundleTask; @@ -109,4 +110,14 @@ public interface WindowsClient { * /> */ Set describeBundleTasksInRegion(@Nullable String region, String... bundleTaskIds); + + /** + * + * Retrieves the encrypted administrator password for the instances running Windows. + * + * @param region The region where the instance is based + * @param instanceId The ID of the instance to query + * @see + */ + PasswordData getPasswordData(@Nullable String region, String instanceId); } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/GetPasswordDataResponseHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/GetPasswordDataResponseHandler.java new file mode 100644 index 0000000000..3cdd7d80e9 --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/GetPasswordDataResponseHandler.java @@ -0,0 +1,58 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.ec2.xml; + +import org.jclouds.date.DateService; +import org.jclouds.ec2.domain.PasswordData; +import org.jclouds.http.functions.ParseSax; + +import javax.inject.Inject; + +/** + * @author Richard Downer + */ +public class GetPasswordDataResponseHandler extends ParseSax.HandlerWithResult { + + private StringBuilder currentText = new StringBuilder(); + private PasswordData.Builder builder = PasswordData.builder(); + @Inject protected DateService dateService; + + @Override + public PasswordData getResult() { + return builder.build(); + } + + public void endElement(String uri, String name, String qName) { + if (qName.equals("requestId")) { + builder.requestId(currentText.toString().trim()); + } else if (qName.equals("instanceId")) { + builder.instanceId(currentText.toString().trim()); + } else if (qName.equals("timestamp")) { + builder.timestamp(dateService.iso8601DateParse(currentText.toString().trim())); + } else if (qName.equals("passwordData")) { + builder.passwordData(currentText.toString().trim()); + } + currentText = new StringBuilder(); + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } + +} From 3a53a4e5fa60db8461bdc19bd7e4afe252d39c41 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Wed, 1 Feb 2012 17:06:25 +0000 Subject: [PATCH 3/6] Add support for getting ciphers through the Crypto interface --- core/src/main/java/org/jclouds/crypto/Crypto.java | 4 ++++ .../java/org/jclouds/encryption/internal/JCECrypto.java | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/core/src/main/java/org/jclouds/crypto/Crypto.java b/core/src/main/java/org/jclouds/crypto/Crypto.java index 9c17654817..b6617c7ce7 100644 --- a/core/src/main/java/org/jclouds/crypto/Crypto.java +++ b/core/src/main/java/org/jclouds/crypto/Crypto.java @@ -25,7 +25,9 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateFactory; +import javax.crypto.Cipher; import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; import org.jclouds.encryption.internal.JCECrypto; @@ -61,4 +63,6 @@ public interface Crypto { MessageDigest sha512(); + Cipher cipher(String algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException; + } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java b/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java index 4efa54abe0..4c6dfd6fee 100644 --- a/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java +++ b/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java @@ -28,7 +28,10 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import org.jclouds.javax.annotation.Nullable; + +import javax.crypto.Cipher; import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; import javax.inject.Inject; import javax.inject.Singleton; @@ -86,6 +89,11 @@ public class JCECrypto implements Crypto { return provider == null ? MessageDigest.getInstance(algorithm) : MessageDigest.getInstance(algorithm, provider); } + @Override + public Cipher cipher(String algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException { + return provider == null ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, provider); + } + public final static String MD5 = "MD5"; public final static String SHA1 = "SHA1"; public final static String SHA256 = "SHA-256"; From c665eb6a6b3326d0ea16cc1089157e4244989f67 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Wed, 1 Feb 2012 17:08:29 +0000 Subject: [PATCH 4/6] Add a function that decrypts the Windows Administrator password generated by Amazon and returns a LoginCredentials instance. --- .../EC2ComputeServiceDependenciesModule.java | 2 + .../domain/PasswordDataAndPrivateKey.java | 45 +++++++++++ ...dowsLoginCredentialsFromEncryptedData.java | 78 +++++++++++++++++++ ...LoginCredentialsFromEncryptedDataTest.java | 74 ++++++++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/compute/domain/PasswordDataAndPrivateKey.java create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedData.java create mode 100644 apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedDataTest.java diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java index a97476765f..6281f63be4 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java @@ -48,6 +48,7 @@ import org.jclouds.ec2.compute.functions.CredentialsForInstance; import org.jclouds.ec2.compute.functions.LoadPublicIpForInstanceOrNull; import org.jclouds.ec2.compute.functions.RegionAndIdToImage; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; +import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.compute.predicates.SecurityGroupPresent; @@ -110,6 +111,7 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { bind(new TypeLiteral() { }).to(new TypeLiteral>() { }).in(Scopes.SINGLETON); + bind(WindowsLoginCredentialsFromEncryptedData.class); } /** diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/domain/PasswordDataAndPrivateKey.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/domain/PasswordDataAndPrivateKey.java new file mode 100644 index 0000000000..7cee4b7228 --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/domain/PasswordDataAndPrivateKey.java @@ -0,0 +1,45 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.ec2.compute.domain; + +import org.jclouds.ec2.domain.PasswordData; + +/** + * An encrypted Windows Administrator password, and the private key that can decrypt it. + * + * @author Richard Downer + */ +public class PasswordDataAndPrivateKey { + + private final PasswordData passwordData; + private final String privateKey; + + public PasswordDataAndPrivateKey(PasswordData passwordData, String privateKey) { + this.passwordData = passwordData; + this.privateKey = privateKey; + } + + public PasswordData getPasswordData() { + return passwordData; + } + + public String getPrivateKey() { + return privateKey; + } +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedData.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedData.java new file mode 100644 index 0000000000..5873b73f12 --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedData.java @@ -0,0 +1,78 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.ec2.compute.functions; + +import com.google.common.base.Function; +import com.google.common.base.Throwables; +import com.google.inject.Singleton; +import org.jclouds.crypto.Crypto; +import org.jclouds.crypto.Pems; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey; +import org.jclouds.encryption.internal.Base64; + +import javax.annotation.Nullable; +import javax.crypto.Cipher; +import javax.inject.Inject; +import java.nio.charset.Charset; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.spec.KeySpec; + +/** + * Given an encrypted Windows Administrator password and the decryption key, return a LoginCredentials instance. + * + * @author Richard Downer + */ +@Singleton +public class WindowsLoginCredentialsFromEncryptedData implements Function { + + private final Crypto crypto; + + @Inject + public WindowsLoginCredentialsFromEncryptedData(Crypto crypto) { + this.crypto = crypto; + } + + @Override + public LoginCredentials apply(@Nullable PasswordDataAndPrivateKey dataAndKey) { + if(dataAndKey == null) + return null; + + try { + KeySpec keySpec = Pems.privateKeySpec(dataAndKey.getPrivateKey()); + KeyFactory kf = crypto.rsaKeyFactory(); + PrivateKey privKey = kf.generatePrivate(keySpec); + + Cipher cipher = crypto.cipher("RSA/NONE/PKCS1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privKey); + byte[] cipherText = Base64.decode(dataAndKey.getPasswordData().getPasswordData()); + byte[] plainText = cipher.doFinal(cipherText); + String password = new String(plainText, Charset.forName("ASCII")); + + return LoginCredentials.builder() + .user("Administrator") + .password(password) + .noPrivateKey() + .build(); + } catch(Exception e) { + throw Throwables.propagate(e); + } + } +} diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedDataTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedDataTest.java new file mode 100644 index 0000000000..e8ebd12c36 --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedDataTest.java @@ -0,0 +1,74 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.ec2.compute.functions; + +import org.jclouds.crypto.Crypto; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey; +import org.jclouds.ec2.domain.PasswordData; +import org.jclouds.encryption.bouncycastle.BouncyCastleCrypto; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +/** + * @author Richard Downer + */ +public class WindowsLoginCredentialsFromEncryptedDataTest { + + private static final String PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" + + "MIIEowIBAAKCAQEAmN6GOSMnyGNWN19ETBh11tJB5OGs3Dps8kPWqAhF9RyL/mKwkW26vH+h/5Z5\n" + + "cA5T80pK72kNnXObFaMHNoX3lavrc6yXF+8F3f1tlFX2Z+iB1pYXz1oBPqT6oOmc2XzcsJuJRakd\n" + + "zwRwHDaqljpaW7+TZlxhMa1DmUkD/HHMxDCK8jbUIZDc6BZSrnj2uPwHwW737NRE4aC3fcu4LMwf\n" + + "b2VotbNGNiAnNmrb/vtIIGkFE8NYEMpiz0WYTWX4eVKpJImv1PR6G1fMLSvudJs0ARObuLDvuonn\n" + + "SCFFdkibrwMKYbHVGGh6FoY1Vy0sqI55dgQU1kSNouiDgOGxgx+TIwIDAQABAoIBAHCS/nk5QGS7\n" + + "cpRYXa1EHhNSxx/MaUXM6MoH1x3q6cm1egqdlrWh/vAtdZkIsOkqQ/xX65Me493dcomegwNN6KOZ\n" + + "9Uw7/xCq/sEZjga8vzaJ7IOgCGy0NVJyn/a70rv+zW5pO8/G2KLI+95rC3iSBFSoYd3xjcnNdIh/\n" + + "UqYnD8oxYpKmf7418pMPsBrkglkFlbVBPiDXdpoSziqSN6uWQG4Yh0WR87aElhM9JJW50Hh6h7g5\n" + + "OvgCBzS8G+KXCjqimk108+/ed5Nl6VhPAf79yCVZUueKBhaf2r0Kkyxg7M/Y+LJwcoUusIP7Cv7G\n" + + "xyzG2vi21prWRCm2sVCUDyQy5qECgYEA92jGVAaB3OGEUIXn7eVE3U3FQH37XcJMGsHqBIzDG13p\n" + + "C97HdN21rwRkz+G2eAsIxA+p9BsO7dSmtKC60kl6iMRgltS3W7xoC37N9BtjhpciHcLg8c70oyDx\n" + + "qHiLKuDi90mZ1FPmWupO4FJnGEB3evHUKZSpTrVVMzt+tyEn/psCgYEAni1hrYoMkQgN3sEC3CKB\n" + + "0jQkrOMvY219B8Tdf9LXSuP6z9POagDBDhkeT3xn8rAOmOfVGHYdO0CvPqmAkmXhf+g+OREdecQa\n" + + "uY0FmvcTt+Dx0c6pRZmm5AhvUVXFXqONsSg79iviXbUy5Hik0k5HTs5E6B4obrh5W+xfMTUXghkC\n" + + "gYBn92uAW8uumkYT4HF6EuJBbTD6zPYYjFGW3O4OQ2ip02jfSBrhDVoP1fTXNq6K+3gPi9WLcuNv\n" + + "JfF37iMTwzTuzDcaqwDyV9YRHpRFhEzqfhAkGYSVmLZM5scmWKGCv0YhTJiMFUWz5sqGkZopIs4S\n" + + "qBTT9FjBbooDIXk6U4CPCQKBgFdVBxEhnz6UC9RpDIMuKi88yuMJrChhUx7u+ryQVH3s0ZXdg6HT\n" + + "OMPn6mxIa7v6qJSTq3wN+qW0WQ1n2Kz7wz0zpOctI/EO7RJ1YhrlP+XONLV6PMtIwnQ0lAF8MbTG\n" + + "6HxfknugTyMd4DN0yMu0nHpOOI1P2VMIVzkBkK1CevBBAoGBALROGR7a+eijHdp0/A0chfUoBmud\n" + + "/TsUt+0g/vf1p69rMt6DqEGMgMtp2jIRnwvLElS7gVqnCTEclxNU/0rCXR+V7ImJm8J4f0ff8m0Y\n" + + "Fir9nfCYStszo25NvLFfynS9d/aoBuvqGJaiQyNXiyBJ4MaxxFYagzAWTnDX+kzTlkZ2\n" + + "-----END RSA PRIVATE KEY-----"; + private static final String ENCRYPTED_PASSWORD = "gO1oMoIjjIifv2iqcfIKiQD7ziOTVXsuaBJFEQrZdb8uJH/LsAiJXZeGKEeXlHl/oMoR3HEIoYuHxl+p5iHdrpP889RmxWBDGOWC5iTUzK6CRa5mFmF1I5Lpt7v2YeVoQWihSM8B19BEdBdY1svQp9nyhPB4AqLDrY28x/OrmRh/qYq953i6Y4Z8c76OHqqGcUYM4ePysRlcizSgQjdkEDmKC10Ak3OFRRx3/LqYsFIMiOHeg47APg+UANNTyRiTIia5FDhSeHJzaeYCBRQ7UYH0z2rg4cX3YjOz/MoznjHiaaN4MO+5N3v84VawnqwKOvlwPyI2bmz0+9Tr6DKzqA=="; + + @Test + public void testApply() throws Exception { + Crypto crypto = new BouncyCastleCrypto(); + WindowsLoginCredentialsFromEncryptedData f = new WindowsLoginCredentialsFromEncryptedData(crypto); + + PasswordData passwordData = PasswordData.builder().passwordData(ENCRYPTED_PASSWORD).build(); + + LoginCredentials credentials = f.apply(new PasswordDataAndPrivateKey(passwordData, PRIVATE_KEY)); + + assertEquals(credentials.getUser(), "Administrator"); + assertEquals(credentials.getPassword(), "u4.y9mb;nR."); + assertFalse(credentials.getOptionalPrivateKey().isPresent()); + } +} From 27aaafe139d93cfbbce2f06c50c13f47af125632 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Wed, 1 Feb 2012 17:40:06 +0000 Subject: [PATCH 5/6] Test to demonstrate getting the Windows Administrator password --- apis/ec2/pom.xml | 6 + .../ec2/services/WindowsClientLiveTest.java | 104 +++++++++++++++++- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/apis/ec2/pom.xml b/apis/ec2/pom.xml index ccaffbf752..24e64f9bcc 100644 --- a/apis/ec2/pom.xml +++ b/apis/ec2/pom.xml @@ -83,6 +83,12 @@ ${project.version} test + + org.jclouds.driver + jclouds-bouncycastle + ${project.version} + test + diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java index a11244ec77..2b7f57fce4 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java @@ -18,19 +18,52 @@ */ package org.jclouds.ec2.services; -import java.util.Properties; - +import com.google.common.base.Predicate; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.Module; import org.jclouds.compute.BaseVersionedServiceLiveTest; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.EC2ContextBuilder; +import org.jclouds.ec2.EC2PropertiesBuilder; +import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey; +import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData; +import org.jclouds.ec2.domain.InstanceType; +import org.jclouds.ec2.domain.PasswordData; +import org.jclouds.ec2.reference.EC2Constants; +import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule; +import org.jclouds.logging.Logger; import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.predicates.RetryablePredicate; import org.jclouds.rest.RestContext; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableSet; -import com.google.inject.Module; +import javax.annotation.Nullable; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; /** * Tests behavior of {@code WindowsClient} @@ -43,19 +76,30 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest { provider = "ec2"; } + private ComputeService computeService; private WindowsClient client; private static final String DEFAULT_INSTANCE = "i-TODO"; private static final String DEFAULT_BUCKET = "TODO"; private RestContext context; + @Override + public Properties setupRestProperties() { + Properties rest = super.setupRestProperties(); + rest.put("ec2.contextbuilder", EC2ContextBuilder.class.getName()); + rest.put("ec2.propertiesbuilder", EC2PropertiesBuilder.class.getName()); + return rest; + } @BeforeGroups(groups = { "live" }) public void setupClient() { setupCredentials(); Properties overrides = setupProperties(); - context = new ComputeServiceContextFactory().createContext(provider, - ImmutableSet. of(new Log4JLoggingModule()), overrides).getProviderSpecificContext(); + overrides.put(EC2Constants.PROPERTY_EC2_AMI_OWNERS, "206029621532"); + ComputeServiceContext serviceContext = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, + ImmutableSet.of(new Log4JLoggingModule(), new BouncyCastleCryptoModule()), overrides); + computeService = serviceContext.getComputeService(); + context = serviceContext.getProviderSpecificContext(); client = context.getApi().getWindowsServices(); } @@ -82,4 +126,52 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest { public void testDescribeBundleTasksInRegion() { } + + @Test + public void testGetPasswordData() throws Exception { + + // Spin up a new node. Make sure to open the RDP port 3389 + Template template = computeService.templateBuilder() + .osFamily(OsFamily.WINDOWS) + .os64Bit(true) + .imageNameMatches("Windows-2008R2-SP1-English-Base-") + .hardwareId(InstanceType.M1_LARGE) + .options(TemplateOptions.Builder.inboundPorts(3389)) + .build(); + Set nodes = computeService.createNodesInGroup("test", 1, template); + NodeMetadata node = Iterables.getOnlyElement(nodes); + + boolean shutdown = true; + try { + + // The Administrator password will take some time before it is ready - Amazon says sometimes 15 minutes. + // So we create a predicate that tests if the password is ready, and wrap it in a retryable predicate. + Predicate passwordReady = new Predicate() { + @Override + public boolean apply(@Nullable String s) { + if (Strings.isNullOrEmpty(s)) return false; + PasswordData data = client.getPasswordData(null, s); + if (data == null) return false; + return !Strings.isNullOrEmpty(data.getPasswordData()); + } + }; + RetryablePredicate passwordReadyRetryable = new RetryablePredicate(passwordReady, 600, 10, TimeUnit.SECONDS); + assertTrue(passwordReadyRetryable.apply(node.getProviderId())); + + // Now pull together Amazon's encrypted password blob, and the private key that jclouds generated + PasswordDataAndPrivateKey dataAndKey = new PasswordDataAndPrivateKey( + client.getPasswordData(null, node.getProviderId()), + node.getCredentials().getPrivateKey()); + + // And apply it to the decryption function + WindowsLoginCredentialsFromEncryptedData f = context.getUtils().getInjector().getInstance(WindowsLoginCredentialsFromEncryptedData.class); + LoginCredentials credentials = f.apply(dataAndKey); + + assertEquals(credentials.getUser(), "Administrator"); + assertFalse(Strings.isNullOrEmpty(credentials.getPassword())); + } finally { + computeService.destroyNode(node.getId()); + } + } + } From 4ed8f8f85e358dd303a409bea6caf7f174e33e2d Mon Sep 17 00:00:00 2001 From: Andrei Savu Date: Sat, 4 Feb 2012 20:18:50 +0200 Subject: [PATCH 6/6] Renamed getPasswordData to getPasswordDataInRegion & fixed broken unit tests --- .../ec2/services/WindowsAsyncClient.java | 2 +- .../jclouds/ec2/services/WindowsClient.java | 13 ++-- .../ec2/services/WindowsClientLiveTest.java | 16 ++--- .../strategy/AWSEC2ImageParserTest.java | 68 +++++++++++++------ 4 files changed, 59 insertions(+), 40 deletions(-) diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java index c5e2383c73..305310bc08 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java @@ -99,7 +99,7 @@ public interface WindowsAsyncClient { @BinderParam(BindBundleIdsToIndexedFormParams.class) String... bundleTaskIds); /** - * @see WindowsClient#getPasswordData + * @see WindowsClient#getPasswordDataInRegion */ @POST @Path("/") diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsClient.java index e22284b118..5a1af0e0ad 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsClient.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsClient.java @@ -18,15 +18,14 @@ */ package org.jclouds.ec2.services; -import java.util.Set; -import java.util.concurrent.TimeUnit; - +import org.jclouds.concurrent.Timeout; +import org.jclouds.ec2.domain.BundleTask; import org.jclouds.ec2.domain.PasswordData; +import org.jclouds.ec2.options.BundleInstanceS3StorageOptions; import org.jclouds.javax.annotation.Nullable; -import org.jclouds.ec2.domain.BundleTask; -import org.jclouds.ec2.options.BundleInstanceS3StorageOptions; -import org.jclouds.concurrent.Timeout; +import java.util.Set; +import java.util.concurrent.TimeUnit; /** * Provides windows services for EC2. For more information, refer to the Amazon @@ -119,5 +118,5 @@ public interface WindowsClient { * @param instanceId The ID of the instance to query * @see */ - PasswordData getPasswordData(@Nullable String region, String instanceId); + PasswordData getPasswordDataInRegion(@Nullable String region, String instanceId); } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java index 2b7f57fce4..3f1b888eaf 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java @@ -27,7 +27,6 @@ import org.jclouds.compute.BaseVersionedServiceLiveTest; import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; -import org.jclouds.compute.RunNodesException; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Template; @@ -43,7 +42,6 @@ import org.jclouds.ec2.domain.InstanceType; import org.jclouds.ec2.domain.PasswordData; import org.jclouds.ec2.reference.EC2Constants; import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule; -import org.jclouds.logging.Logger; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.rest.RestContext; @@ -51,12 +49,6 @@ import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; import javax.annotation.Nullable; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -95,7 +87,7 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest { public void setupClient() { setupCredentials(); Properties overrides = setupProperties(); - overrides.put(EC2Constants.PROPERTY_EC2_AMI_OWNERS, "206029621532"); + overrides.put(EC2Constants.PROPERTY_EC2_AMI_OWNERS, "206029621532"); /* Amazon Owner ID */ ComputeServiceContext serviceContext = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, ImmutableSet.of(new Log4JLoggingModule(), new BouncyCastleCryptoModule()), overrides); computeService = serviceContext.getComputeService(); @@ -128,7 +120,7 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest { } @Test - public void testGetPasswordData() throws Exception { + public void testGetPasswordDataInRegion() throws Exception { // Spin up a new node. Make sure to open the RDP port 3389 Template template = computeService.templateBuilder() @@ -150,7 +142,7 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest { @Override public boolean apply(@Nullable String s) { if (Strings.isNullOrEmpty(s)) return false; - PasswordData data = client.getPasswordData(null, s); + PasswordData data = client.getPasswordDataInRegion(null, s); if (data == null) return false; return !Strings.isNullOrEmpty(data.getPasswordData()); } @@ -160,7 +152,7 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest { // Now pull together Amazon's encrypted password blob, and the private key that jclouds generated PasswordDataAndPrivateKey dataAndKey = new PasswordDataAndPrivateKey( - client.getPasswordData(null, node.getProviderId()), + client.getPasswordDataInRegion(null, node.getProviderId()), node.getCredentials().getPrivateKey()); // And apply it to the decryption function diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ImageParserTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ImageParserTest.java index 7e2498a842..e21dbd8d01 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ImageParserTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2ImageParserTest.java @@ -18,11 +18,14 @@ */ package org.jclouds.aws.ec2.compute.strategy; -import static org.testng.Assert.assertEquals; - -import java.util.Map; -import java.util.Set; - +import com.google.common.base.Predicates; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.inject.Guice; import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.domain.ImageBuilder; import org.jclouds.compute.domain.OperatingSystem; @@ -40,14 +43,10 @@ import org.jclouds.json.Json; import org.jclouds.json.config.GsonModule; import org.testng.annotations.Test; -import com.google.common.base.Predicates; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; -import com.google.gson.Gson; -import com.google.inject.Guice; +import java.util.Map; +import java.util.Set; + +import static org.testng.Assert.assertEquals; /** * @author Adrian Cole @@ -68,7 +67,12 @@ public class AWSEC2ImageParserTest { .description("ubuntu-images-us/ubuntu-hardy-8.04-i386-server-20091130.manifest.xml") .defaultCredentials(new LoginCredentials("ubuntu", false)).id("us-east-1/ami-7e28ca17") .providerId("ami-7e28ca17").location(defaultLocation).version("20091130") - .userMetadata(ImmutableMap.of("owner", "099720109477", "rootDeviceType", "instance-store")).build()); + .userMetadata(ImmutableMap.of( + "owner", "099720109477", + "rootDeviceType", "instance-store", + "virtualizationType", "paravirtual", + "hypervisor", "xen")) + .build()); assertEquals( Iterables.get(result, 4), @@ -84,6 +88,7 @@ public class AWSEC2ImageParserTest { assertEquals( Iterables.get(result, 6), new ImageBuilder() + .name("ebs/ubuntu-images/ubuntu-lucid-10.04-i386-server-20100827") .operatingSystem( new OperatingSystem.Builder().family(OsFamily.UBUNTU).arch("paravirtual").version("10.04") .description("099720109477/ebs/ubuntu-images/ubuntu-lucid-10.04-i386-server-20100827") @@ -91,7 +96,12 @@ public class AWSEC2ImageParserTest { .description("099720109477/ebs/ubuntu-images/ubuntu-lucid-10.04-i386-server-20100827") .defaultCredentials(new LoginCredentials("ubuntu", false)).id("us-east-1/ami-10f3a255") .providerId("ami-10f3a255").location(defaultLocation).version("20100827") - .userMetadata(ImmutableMap.of("owner", "099720109477", "rootDeviceType", "ebs")).build()); + .userMetadata(ImmutableMap.of( + "owner", "099720109477", + "rootDeviceType", "ebs", + "virtualizationType", "paravirtual", + "hypervisor", "xen")) + .build()); } @@ -120,13 +130,19 @@ public class AWSEC2ImageParserTest { assertEquals( Iterables.get(result, 0), new ImageBuilder() + .name("EC2 CentOS 5.4 HVM AMI") .operatingSystem( new OperatingSystem.Builder().family(OsFamily.CENTOS).arch("hvm").version("5.4") .description("amazon/EC2 CentOS 5.4 HVM AMI").is64Bit(true).build()) .description("EC2 CentOS 5.4 HVM AMI") .defaultCredentials(new LoginCredentials("root", false)).id("us-east-1/ami-7ea24a17") .providerId("ami-7ea24a17").location(defaultLocation) - .userMetadata(ImmutableMap.of("owner", "206029621532", "rootDeviceType", "ebs")).build()); + .userMetadata(ImmutableMap.of( + "owner", "206029621532", + "rootDeviceType", "ebs", + "virtualizationType", "hvm", + "hypervisor", "xen")) + .build()); } @@ -147,11 +163,11 @@ public class AWSEC2ImageParserTest { assertEquals( new Gson().toJson(Iterables.get(result, 1)), - "{\"operatingSystem\":{\"family\":\"UBUNTU\",\"arch\":\"paravirtual\",\"version\":\"9.10\",\"description\":\"411009282317/RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha\",\"is64Bit\":true},\"version\":\"4.5.3_EBS_Alpha\",\"description\":\"RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha\",\"defaultCredentials\":{\"authenticateSudo\":false,\"identity\":\"root\"},\"id\":\"us-east-1/ami-c19db6b5\",\"type\":\"IMAGE\",\"tags\":[],\"providerId\":\"ami-c19db6b5\",\"location\":{\"scope\":\"REGION\",\"id\":\"us-east-1\",\"description\":\"us-east-1\",\"iso3166Codes\":[],\"metadata\":{}},\"userMetadata\":{\"owner\":\"411009282317\",\"rootDeviceType\":\"ebs\",\"virtualizationType\":\"paravirtual\",\"hypervisor\":\"xen\"}}"); + "{\"operatingSystem\":{\"family\":\"UBUNTU\",\"arch\":\"paravirtual\",\"version\":\"9.10\",\"description\":\"411009282317/RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha\",\"is64Bit\":true},\"version\":\"4.5.3_EBS_Alpha\",\"description\":\"RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha\",\"defaultCredentials\":{\"authenticateSudo\":false,\"identity\":\"root\"},\"id\":\"us-east-1/ami-c19db6b5\",\"type\":\"IMAGE\",\"tags\":[],\"providerId\":\"ami-c19db6b5\",\"name\":\"RightImage_Ubuntu_9.10_x64_v4.5.3_EBS_Alpha\",\"location\":{\"scope\":\"REGION\",\"id\":\"us-east-1\",\"description\":\"us-east-1\",\"iso3166Codes\":[],\"metadata\":{}},\"userMetadata\":{\"owner\":\"411009282317\",\"rootDeviceType\":\"ebs\",\"virtualizationType\":\"paravirtual\",\"hypervisor\":\"xen\"}}"); assertEquals( new Gson().toJson(Iterables.get(result, 2)), - "{\"operatingSystem\":{\"family\":\"WINDOWS\",\"arch\":\"hvm\",\"version\":\"2003\",\"description\":\"411009282317/RightImage Windows_2003_i386_v5.4.3\",\"is64Bit\":false},\"version\":\"5.4.3\",\"description\":\"Built by RightScale\",\"defaultCredentials\":{\"authenticateSudo\":false,\"identity\":\"root\"},\"id\":\"us-east-1/ami-710c2605\",\"type\":\"IMAGE\",\"tags\":[],\"providerId\":\"ami-710c2605\",\"location\":{\"scope\":\"REGION\",\"id\":\"us-east-1\",\"description\":\"us-east-1\",\"iso3166Codes\":[],\"metadata\":{}},\"userMetadata\":{\"owner\":\"411009282317\",\"rootDeviceType\":\"ebs\",\"virtualizationType\":\"hvm\",\"hypervisor\":\"xen\"}}"); + "{\"operatingSystem\":{\"family\":\"WINDOWS\",\"arch\":\"hvm\",\"version\":\"2003\",\"description\":\"411009282317/RightImage Windows_2003_i386_v5.4.3\",\"is64Bit\":false},\"version\":\"5.4.3\",\"description\":\"Built by RightScale\",\"defaultCredentials\":{\"authenticateSudo\":false,\"identity\":\"root\"},\"id\":\"us-east-1/ami-710c2605\",\"type\":\"IMAGE\",\"tags\":[],\"providerId\":\"ami-710c2605\",\"name\":\"RightImage Windows_2003_i386_v5.4.3\",\"location\":{\"scope\":\"REGION\",\"id\":\"us-east-1\",\"description\":\"us-east-1\",\"iso3166Codes\":[],\"metadata\":{}},\"userMetadata\":{\"owner\":\"411009282317\",\"rootDeviceType\":\"ebs\",\"virtualizationType\":\"hvm\",\"hypervisor\":\"xen\"}}"); } public void testParseAmznImage() { @@ -161,17 +177,24 @@ public class AWSEC2ImageParserTest { assertEquals( Iterables.get(result, 0), new ImageBuilder() + .name("amzn-ami-0.9.7-beta.i386-ebs") .operatingSystem( new OperatingSystem.Builder().family(OsFamily.AMZN_LINUX).arch("paravirtual") .version("0.9.7-beta").description("137112412989/amzn-ami-0.9.7-beta.i386-ebs") .is64Bit(false).build()).description("Amazon") .defaultCredentials(new LoginCredentials("ec2-user", false)).id("us-east-1/ami-82e4b5c7") .providerId("ami-82e4b5c7").location(defaultLocation).version("0.9.7-beta") - .userMetadata(ImmutableMap.of("owner", "137112412989", "rootDeviceType", "ebs")).build()); + .userMetadata(ImmutableMap.of( + "owner", "137112412989", + "rootDeviceType", "ebs", + "virtualizationType", "paravirtual", + "hypervisor", "xen")) + .build()); assertEquals( Iterables.get(result, 3), new ImageBuilder() + .name("amzn-ami-0.9.7-beta.x86_64-S3") .operatingSystem( new OperatingSystem.Builder().family(OsFamily.AMZN_LINUX).arch("paravirtual") .version("0.9.7-beta") @@ -179,7 +202,12 @@ public class AWSEC2ImageParserTest { .build()).description("Amazon Linux AMI x86_64 S3") .defaultCredentials(new LoginCredentials("ec2-user", false)).id("us-east-1/ami-f2e4b5b7") .providerId("ami-f2e4b5b7").location(defaultLocation).version("0.9.7-beta") - .userMetadata(ImmutableMap.of("owner", "137112412989", "rootDeviceType", "ebs")).build()); + .userMetadata(ImmutableMap.of( + "owner", "137112412989", + "rootDeviceType", "ebs", + "virtualizationType", "paravirtual", + "hypervisor", "xen")) + .build()); } static Location defaultLocation = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1")