mirror of https://github.com/apache/jclouds.git
Merge pull request #349 from andreisavu/windows-passwords
Support retrieving and decrypting the Administrator password of EC2 Windows instances
This commit is contained in:
commit
db53c00748
|
@ -83,6 +83,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jclouds.driver</groupId>
|
||||
<artifactId>jclouds-bouncycastle</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
|
|
@ -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<ComputeServiceContext>() {
|
||||
}).to(new TypeLiteral<ComputeServiceContextImpl<EC2Client, EC2AsyncClient>>() {
|
||||
}).in(Scopes.SINGLETON);
|
||||
bind(WindowsLoginCredentialsFromEncryptedData.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -86,6 +86,7 @@ public class EC2ImageParser implements Function<org.jclouds.ec2.domain.Image, Im
|
|||
ImageBuilder builder = new ImageBuilder();
|
||||
builder.providerId(from.getId());
|
||||
builder.id(from.getRegion() + "/" + from.getId());
|
||||
builder.name(from.getName());
|
||||
builder.description(from.getDescription() != null ? from.getDescription() : from.getImageLocation());
|
||||
builder.userMetadata(ImmutableMap.<String, String> builder().put("owner", from.getImageOwnerId()).put(
|
||||
"rootDeviceType", from.getRootDeviceType().value()).put("virtualizationType",
|
||||
|
|
|
@ -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<PasswordDataAndPrivateKey, LoginCredentials> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -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#getPasswordDataInRegion
|
||||
*/
|
||||
@POST
|
||||
@Path("/")
|
||||
@FormParams(keys = ACTION, values = "GetPasswordData")
|
||||
@XMLResponseParser(GetPasswordDataResponseHandler.class)
|
||||
ListenableFuture<PasswordData> getPasswordData(
|
||||
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region,
|
||||
@FormParam("InstanceId") String instanceId);
|
||||
|
||||
}
|
||||
|
|
|
@ -18,14 +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
|
||||
|
@ -109,4 +109,14 @@ public interface WindowsClient {
|
|||
* />
|
||||
*/
|
||||
Set<BundleTask> 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 <a href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-GetPasswordData.html" />
|
||||
*/
|
||||
PasswordData getPasswordDataInRegion(@Nullable String region, String instanceId);
|
||||
}
|
||||
|
|
|
@ -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<PasswordData> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -18,19 +18,44 @@
|
|||
*/
|
||||
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.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.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 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 +68,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<EC2Client, EC2AsyncClient> 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.<Module> of(new Log4JLoggingModule()), overrides).getProviderSpecificContext();
|
||||
overrides.put(EC2Constants.PROPERTY_EC2_AMI_OWNERS, "206029621532"); /* Amazon Owner ID */
|
||||
ComputeServiceContext serviceContext = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider,
|
||||
ImmutableSet.<Module>of(new Log4JLoggingModule(), new BouncyCastleCryptoModule()), overrides);
|
||||
computeService = serviceContext.getComputeService();
|
||||
context = serviceContext.getProviderSpecificContext();
|
||||
client = context.getApi().getWindowsServices();
|
||||
}
|
||||
|
||||
|
@ -82,4 +118,52 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest {
|
|||
public void testDescribeBundleTasksInRegion() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPasswordDataInRegion() 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<? extends NodeMetadata> 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<String> passwordReady = new Predicate<String>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable String s) {
|
||||
if (Strings.isNullOrEmpty(s)) return false;
|
||||
PasswordData data = client.getPasswordDataInRegion(null, s);
|
||||
if (data == null) return false;
|
||||
return !Strings.isNullOrEmpty(data.getPasswordData());
|
||||
}
|
||||
};
|
||||
RetryablePredicate<String> passwordReadyRetryable = new RetryablePredicate<String>(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.getPasswordDataInRegion(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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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";
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue