added windows password parsing to ec2

This commit is contained in:
Adrian Cole 2012-07-30 01:02:23 -07:00
parent 3c2a9c9614
commit 53fe278f03
26 changed files with 802 additions and 238 deletions

View File

@ -41,6 +41,8 @@
<test.ec2.credential>${test.aws.credential}</test.ec2.credential>
<test.ec2.template></test.ec2.template>
<test.ec2.ebs-template>hardwareId=m1.small,imageId=us-west-2/ami-38c64a08</test.ec2.ebs-template>
<test.ec2.windows-template>hardwareId=m1.small,imageNameMatches=Windows_Server-2008-R2_SP1-English-64Bit-Base-WinRM-</test.ec2.windows-template>
<test.ec2.windows-owner>449550055360</test.ec2.windows-owner>
<jclouds.osgi.export>org.jclouds.ec2*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>
org.jclouds.compute.internal;version="${project.version}",
@ -128,6 +130,8 @@
<test.ec2.credential>${test.ec2.credential}</test.ec2.credential>
<test.ec2.template>${test.ec2.template}</test.ec2.template>
<test.ec2.ebs-template>${test.ec2.ebs-template}</test.ec2.ebs-template>
<test.ec2.windows-template>${test.ec2.windows-template}</test.ec2.windows-template>
<test.ec2.windows-owner>${test.ec2.windows-owner}</test.ec2.windows-owner>
</systemPropertyVariables>
</configuration>
</execution>

View File

@ -18,6 +18,11 @@
*/
package org.jclouds.ec2;
import java.util.Set;
import javax.annotation.Nullable;
import org.jclouds.ec2.features.WindowsAsyncApi;
import org.jclouds.ec2.services.AMIAsyncClient;
import org.jclouds.ec2.services.AvailabilityZoneAndRegionAsyncClient;
import org.jclouds.ec2.services.ElasticBlockStoreAsyncClient;
@ -26,7 +31,13 @@ import org.jclouds.ec2.services.InstanceAsyncClient;
import org.jclouds.ec2.services.KeyPairAsyncClient;
import org.jclouds.ec2.services.SecurityGroupAsyncClient;
import org.jclouds.ec2.services.WindowsAsyncClient;
import org.jclouds.location.Region;
import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
import com.google.common.annotations.Beta;
import com.google.inject.Provides;
/**
* Provides asynchronous access to EC2 services.
@ -35,6 +46,13 @@ import org.jclouds.rest.annotations.Delegate;
*/
public interface EC2AsyncClient {
public final static String VERSION = "2010-06-15";
/**
*
* @return the Region codes configured
*/
@Provides
@Region
Set<String> getConfiguredRegions();
/**
* Provides asynchronous access to AMI services.
@ -84,4 +102,15 @@ public interface EC2AsyncClient {
@Delegate
ElasticBlockStoreAsyncClient getElasticBlockStoreServices();
/**
* Provides asynchronous access to Windows features.
*/
@Delegate
@Beta
WindowsAsyncApi getWindowsApi();
@Delegate
@Beta
WindowsAsyncApi getWindowsApiForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
}

View File

@ -18,9 +18,13 @@
*/
package org.jclouds.ec2;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.jclouds.concurrent.Timeout;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.ec2.services.AMIClient;
import org.jclouds.ec2.services.AvailabilityZoneAndRegionClient;
import org.jclouds.ec2.services.ElasticBlockStoreClient;
@ -29,7 +33,13 @@ import org.jclouds.ec2.services.InstanceClient;
import org.jclouds.ec2.services.KeyPairClient;
import org.jclouds.ec2.services.SecurityGroupClient;
import org.jclouds.ec2.services.WindowsClient;
import org.jclouds.location.Region;
import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
import com.google.common.annotations.Beta;
import com.google.inject.Provides;
/**
* Provides synchronous access to EC2 services.
@ -38,6 +48,14 @@ import org.jclouds.rest.annotations.Delegate;
*/
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
public interface EC2Client {
/**
*
* @return the Region codes configured
*/
@Provides
@Region
Set<String> getConfiguredRegions();
/**
* Provides synchronous access to AMI services.
*/
@ -86,4 +104,16 @@ public interface EC2Client {
@Delegate
ElasticBlockStoreClient getElasticBlockStoreServices();
/**
* Provides synchronous access to Windows features.
*/
@Delegate
@Beta
WindowsApi getWindowsApi();
@Delegate
@Beta
WindowsApi getWindowsApiForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
}

View File

@ -31,18 +31,21 @@ import javax.inject.Singleton;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.compute.EC2ComputeService;
import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.extensions.EC2ImageExtension;
import org.jclouds.ec2.compute.functions.AddElasticIpsToNodemetadata;
import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair;
import org.jclouds.ec2.compute.functions.CredentialsForInstance;
import org.jclouds.ec2.compute.functions.EC2ImageParser;
import org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstance;
import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata;
import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
@ -52,10 +55,10 @@ import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.ec2.compute.predicates.GetImageWhenStatusAvailablePredicateWithResult;
import org.jclouds.ec2.compute.predicates.SecurityGroupPresent;
import org.jclouds.ec2.domain.Image.ImageState;
import org.jclouds.ec2.domain.InstanceState;
import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.domain.Image.ImageState;
import org.jclouds.ec2.reference.EC2Constants;
import org.jclouds.predicates.PredicateWithResult;
import org.jclouds.predicates.RetryablePredicate;
@ -127,7 +130,10 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule {
}).annotatedWith(Names.named("SECURITY")).to(CreateSecurityGroupIfNeeded.class);
bind(new TypeLiteral<CacheLoader<RegionAndName, String>>() {
}).annotatedWith(Names.named("ELASTICIP")).to(LoadPublicIpForInstanceOrNull.class);
bind(WindowsLoginCredentialsFromEncryptedData.class);
bind(new TypeLiteral<Function<PasswordDataAndPrivateKey, LoginCredentials>>() {
}).to(WindowsLoginCredentialsFromEncryptedData.class);
bind(new TypeLiteral<Function<RunningInstance, LoginCredentials>>() {
}).to(PasswordCredentialsFromWindowsInstance.class);
bind(new TypeLiteral<Function<org.jclouds.ec2.domain.Image, Image>>() {
}).to(EC2ImageParser.class);
bind(new TypeLiteral<ImageExtension>() {

View File

@ -20,6 +20,8 @@ package org.jclouds.ec2.compute.domain;
import org.jclouds.ec2.domain.PasswordData;
import com.google.common.base.Objects;
/**
* An encrypted Windows Administrator password, and the private key that can decrypt it.
*
@ -42,4 +44,35 @@ public class PasswordDataAndPrivateKey {
public String getPrivateKey() {
return privateKey;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hashCode(passwordData, privateKey);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PasswordDataAndPrivateKey other = PasswordDataAndPrivateKey.class.cast(obj);
return Objects.equal(this.passwordData, other.passwordData) && Objects.equal(this.privateKey, other.privateKey);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper(this).omitNullValues().add("passwordData", passwordData).toString();
}
}

View File

@ -28,11 +28,13 @@ import javax.inject.Singleton;
import org.jclouds.compute.domain.Image;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.domain.RunningInstance;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@ -43,27 +45,31 @@ import com.google.common.cache.LoadingCache;
*/
@Singleton
public class CredentialsForInstance extends CacheLoader<RunningInstance, Credentials> {
private final ConcurrentMap<RegionAndName, KeyPair> credentialsMap;
private final Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap;
private final Function<RunningInstance, LoginCredentials> passwordCredentialsFromWindowsInstance;
@Inject
CredentialsForInstance(ConcurrentMap<RegionAndName, KeyPair> credentialsMap,
Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap) {
Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap, Function<RunningInstance, LoginCredentials> passwordCredentialsFromWindowsInstance) {
this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap");
this.imageMap = imageMap;
this.imageMap = checkNotNull(imageMap, "imageMap");
this.passwordCredentialsFromWindowsInstance = checkNotNull(passwordCredentialsFromWindowsInstance, "passwordCredentialsFromWindowsInstance");
}
@Override
public Credentials load(RunningInstance instance) throws ExecutionException {
Credentials credentials = null;// default if no keypair exists
if (instance.getKeyName() != null) {
credentials = new Credentials(getLoginAccountFor(instance), getPrivateKeyOrNull(instance));
public Credentials load(final RunningInstance instance) throws ExecutionException {
if ("windows".equals(instance.getPlatform())) {
return passwordCredentialsFromWindowsInstance.apply(instance);
} else if (instance.getKeyName() != null) {
return new Credentials(getLoginAccountFor(instance), getPrivateKeyOrNull(instance));
}
return credentials;
return null;
}
@VisibleForTesting
String getPrivateKeyOrNull(RunningInstance instance) throws ExecutionException {
String getPrivateKeyOrNull(RunningInstance instance) {
KeyPair keyPair = credentialsMap.get(new RegionAndName(instance.getRegion(), instance.getKeyName()));
return keyPair != null ? keyPair.getKeyMaterial() : null;
}

View File

@ -0,0 +1,117 @@
/**
* 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 static com.google.common.base.Preconditions.checkNotNull;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.domain.PasswordData;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.logging.Logger;
import org.jclouds.predicates.RetryablePredicate;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
/**
* @author Adrian Cole
*/
@Singleton
public class PasswordCredentialsFromWindowsInstance implements Function<RunningInstance, LoginCredentials> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final ConcurrentMap<RegionAndName, KeyPair> credentialsMap;
private final EC2Client ec2Client;
private final Function<PasswordDataAndPrivateKey, LoginCredentials> pwDataToLoginCredentials;
@Inject
protected PasswordCredentialsFromWindowsInstance(ConcurrentMap<RegionAndName, KeyPair> credentialsMap,
EC2Client ec2Client, Function<PasswordDataAndPrivateKey, LoginCredentials> pwDataToLoginCredentials) {
this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap");
this.ec2Client = checkNotNull(ec2Client, "ec2Client");
this.pwDataToLoginCredentials = checkNotNull(pwDataToLoginCredentials, "pwDataToLoginCredentials");
}
@Override
public LoginCredentials apply(final RunningInstance instance) {
LoginCredentials credentials = LoginCredentials.builder().user("Administrator").noPrivateKey().build();
String privateKey = getPrivateKeyOrNull(instance);
if (privateKey == null) {
return credentials;
}
final WindowsApi windowsApi = ec2Client.getWindowsApiForRegion(instance.getRegion());
// 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.
final AtomicReference<PasswordData> data = new AtomicReference<PasswordData>();
Predicate<String> passwordReady = new Predicate<String>() {
@Override
public boolean apply(@Nullable String s) {
if (Strings.isNullOrEmpty(s))
return false;
data.set(windowsApi.getPasswordDataForInstance(instance.getId()));
if (data.get() == null)
return false;
return !Strings.isNullOrEmpty(data.get().getPasswordData());
}
};
// TODO: parameterize
RetryablePredicate<String> passwordReadyRetryable = new RetryablePredicate<String>(passwordReady, 600, 10,
TimeUnit.SECONDS);
logger.debug(">> awaiting password data for instance(%s/%s)", instance.getRegion(), instance.getId());
if (passwordReadyRetryable.apply(instance.getId())) {
credentials = pwDataToLoginCredentials.apply(new PasswordDataAndPrivateKey(data.get(), privateKey));
logger.debug("<< obtained password data for instance(%s/%s)", instance.getRegion(), instance.getId());
} else {
logger.debug("<< unable to get password data for instance(%s/%s)", instance.getRegion(), instance.getId());
}
return credentials;
}
@VisibleForTesting
String getPrivateKeyOrNull(RunningInstance instance) {
KeyPair keyPair = credentialsMap.get(new RegionAndName(instance.getRegion(), instance.getKeyName()));
return keyPair != null ? keyPair.getKeyMaterial() : null;
}
}

View File

@ -24,9 +24,9 @@ import static com.google.common.collect.Iterables.filter;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.inject.Singleton;
@ -36,9 +36,9 @@ import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.internal.VolumeImpl;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.domain.Credentials;
@ -50,7 +50,6 @@ import org.jclouds.ec2.domain.InstanceState;
import org.jclouds.ec2.domain.RootDeviceType;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.logging.Logger;
import org.jclouds.util.InetAddresses2;
import org.jclouds.util.InetAddresses2.IsPrivateIPAddress;
import com.google.common.annotations.VisibleForTesting;
@ -61,10 +60,10 @@ import com.google.common.base.Supplier;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.inject.Inject;

View File

@ -54,7 +54,7 @@ public class WindowsLoginCredentialsFromEncryptedData implements Function<Passwo
@Override
public LoginCredentials apply(@Nullable PasswordDataAndPrivateKey dataAndKey) {
if(dataAndKey == null)
if (dataAndKey == null)
return null;
try {

View File

@ -23,6 +23,8 @@ import java.util.Map;
import org.jclouds.aws.config.WithZonesFormSigningRestClientModule;
import org.jclouds.ec2.EC2AsyncClient;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.ec2.features.WindowsAsyncApi;
import org.jclouds.ec2.services.AMIAsyncClient;
import org.jclouds.ec2.services.AMIClient;
import org.jclouds.ec2.services.AvailabilityZoneAndRegionAsyncClient;
@ -73,6 +75,7 @@ public class EC2RestClientModule<S extends EC2Client, A extends EC2AsyncClient>
.put(WindowsClient.class, WindowsAsyncClient.class)//
.put(AvailabilityZoneAndRegionClient.class, AvailabilityZoneAndRegionAsyncClient.class)//
.put(ElasticBlockStoreClient.class, ElasticBlockStoreAsyncClient.class)//
.put(WindowsApi.class, WindowsAsyncApi.class)//
.build();
@SuppressWarnings("unchecked")

View File

@ -18,10 +18,25 @@
*/
package org.jclouds.ec2.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
import com.google.common.base.Objects;
/**
* Holds the encrypted Windows Administrator password for an instance.
* The encrypted administrator password for an instance running Windows.
*
* <h4>Note</h4>
*
* The Windows password is only generated the first time an AMI is launched. It is not generated for
* rebundled AMIs or after the password is changed on an instance.
*
* The password is encrypted using the key pair that you provided.
*
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-GetPasswordData.html"
* >doc</a>
*
* @author Richard Downer
*/
@ -31,99 +46,110 @@ public class PasswordData {
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 toBuilder() {
return new Builder().fromPasswordData(this);
}
public static class Builder {
protected String instanceId;
protected Date timestamp;
protected String passwordData;
/**
* @see PasswordData#getInstanceId()
*/
public Builder instanceId(String instanceId) {
this.instanceId = instanceId;
return this;
}
/**
* @see PasswordData#getTimestamp()
*/
public Builder timestamp(Date timestamp) {
this.timestamp = timestamp;
return this;
}
/**
* @see PasswordData#getPasswordData()
*/
public Builder passwordData(String passwordData) {
this.passwordData = passwordData;
return this;
}
public PasswordData build() {
return new PasswordData(requestId, instanceId, timestamp, passwordData);
return new PasswordData(instanceId, timestamp, passwordData);
}
public Builder fromPasswordData(PasswordData in) {
return this.instanceId(in.getInstanceId()).timestamp(in.getTimestamp()).passwordData(in.getPasswordData());
}
}
private String requestId;
private String instanceId;
private Date timestamp;
private String passwordData;
protected final String instanceId;
protected final Date timestamp;
protected final 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;
protected PasswordData(String instanceId, Date timestamp, String passwordData) {
this.instanceId = checkNotNull(instanceId, "instanceId");
this.timestamp = checkNotNull(timestamp, "timestamp");
this.passwordData = checkNotNull(passwordData, "passwordData");
}
/**
* The ID of the instance.
*/
public String getInstanceId() {
return instanceId;
}
/**
* The time the data was last updated.
*/
public Date getTimestamp() {
return timestamp;
}
/**
* The password of the instance.
*/
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;
}
/**
* {@inheritDoc}
*/
@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;
return Objects.hashCode(instanceId);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PasswordData other = PasswordData.class.cast(obj);
return Objects.equal(this.instanceId, other.instanceId);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "PasswordData{" +
"requestId='" + requestId + '\'' +
", instanceId='" + instanceId + '\'' +
", timestamp=" + timestamp +
", passwordData='" + passwordData + '\'' +
'}';
return Objects.toStringHelper(this).omitNullValues().add("instanceId", instanceId).add("timestamp", timestamp)
.add("passwordData", passwordData).toString();
}
}

View File

@ -0,0 +1,57 @@
/**
* 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.features;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.ec2.domain.PasswordData;
import com.google.common.annotations.Beta;
/**
* Provides access to EC2 Windows Features via the Query API
* <p/>
*
* @see <a href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference" >doc</a>
* @see WindowsAsyncApi
* @author Adrian Cole
*/
@Timeout(duration = 45, timeUnit = TimeUnit.SECONDS)
@Beta
public interface WindowsApi {
/**
*
* Retrieves the encrypted administrator password for the instances running Windows. <h4>Note</h4>
*
* The Windows password is only generated the first time an AMI is launched. It is not generated
* for rebundled AMIs or after the password is changed on an instance.
*
* The password is encrypted using the key pair that you provided.
*
* @param instanceId
* The ID of the instance to query
* @return password data or null if not available
* @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-GetPasswordData.html"
* />
*/
PasswordData getPasswordDataForInstance(String instanceId);
}

View File

@ -0,0 +1,63 @@
/**
* 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.features;
import static org.jclouds.aws.reference.FormParameters.ACTION;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import org.jclouds.aws.filters.FormSigner;
import org.jclouds.ec2.domain.PasswordData;
import org.jclouds.ec2.xml.GetPasswordDataResponseHandler;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.FormParams;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.annotations.Beta;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides access to EC2 Windows Features via the Query API
* <p/>
*
* @see <a href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference" >doc</a>
* @see WindowsAsyncApi
* @author Adrian Cole
*/
@RequestFilters(FormSigner.class)
@VirtualHost
@Beta
public interface WindowsAsyncApi {
/**
* @see WindowsApi#getPasswordDataForInstance
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "GetPasswordData")
@XMLResponseParser(GetPasswordDataResponseHandler.class)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<PasswordData> getPasswordDataForInstance(@FormParam("InstanceId") String instanceId);
}

View File

@ -46,9 +46,7 @@ public class GetPasswordDataResponseHandler extends ParseSax.HandlerWithResult<P
}
public void endElement(String uri, String name, String qName) {
if (qName.equals("requestId")) {
builder.requestId(currentText.toString().trim());
} else if (qName.equals("instanceId")) {
if (qName.equals("instanceId")) {
builder.instanceId(currentText.toString().trim());
} else if (qName.equals("timestamp")) {
builder.timestamp(dateCodec.toDate(currentText.toString().trim()));

View File

@ -0,0 +1,89 @@
/**
* 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 static org.jclouds.compute.options.TemplateOptions.Builder.inboundPorts;
import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import java.util.Properties;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilderSpec;
import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
import org.jclouds.compute.predicates.NodePredicates;
import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule;
import org.testng.annotations.Test;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Module;
/**
* Tests behavior of {@code WindowsApi}
*
* @author Adrian Cole
*/
@Test(groups = "live", singleThreaded = true, testName = "PasswordCredentialsFromWindowsInstanceLiveTest")
public class PasswordCredentialsFromWindowsInstanceLiveTest extends BaseComputeServiceContextLiveTest {
protected TemplateBuilderSpec windowsTemplate;
public PasswordCredentialsFromWindowsInstanceLiveTest() {
provider = "ec2";
}
@Override
protected Properties setupProperties() {
Properties overrides = super.setupProperties();
String windowsSpec = setIfTestSystemPropertyPresent(overrides, provider + ".windows-template");
if (Strings.emptyToNull(windowsSpec) != null) {
windowsTemplate = TemplateBuilderSpec.parse(windowsSpec);
}
String windowsOwner = setIfTestSystemPropertyPresent(overrides, provider + ".windows-owner");
if (Strings.emptyToNull(windowsOwner) != null) {
overrides.setProperty(PROPERTY_EC2_AMI_OWNERS, windowsOwner);
}
return overrides;
}
// TODO: refactor so that we don't need to use bouncycastle
@Override
protected Iterable<Module> setupModules() {
return ImmutableSet.<Module> builder().addAll(super.setupModules()).add(new BouncyCastleCryptoModule()).build();
}
@Test
public void testWindowsAdminWorks() throws Exception {
String group = "winadm";
// Spin up a new node. Make sure to open the RDP port 3389
Template template = view.getComputeService().templateBuilder().from(windowsTemplate).options(inboundPorts(3389))
.build();
try {
NodeMetadata node = Iterables.getOnlyElement(view.getComputeService().createNodesInGroup(group, 1, template));
assertEquals(node.getCredentials().getUser(), "Administrator");
assertFalse(Strings.isNullOrEmpty(node.getCredentials().getPassword()));
} finally {
view.getComputeService().destroyNodesMatching(NodePredicates.inGroup(group));
}
}
}

View File

@ -22,6 +22,8 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import org.jclouds.crypto.Crypto;
import org.jclouds.date.DateService;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
import org.jclouds.ec2.domain.PasswordData;
@ -58,12 +60,17 @@ public class WindowsLoginCredentialsFromEncryptedDataTest {
"-----END RSA PRIVATE KEY-----";
private static final String ENCRYPTED_PASSWORD = "gO1oMoIjjIifv2iqcfIKiQD7ziOTVXsuaBJFEQrZdb8uJH/LsAiJXZeGKEeXlHl/oMoR3HEIoYuHxl+p5iHdrpP889RmxWBDGOWC5iTUzK6CRa5mFmF1I5Lpt7v2YeVoQWihSM8B19BEdBdY1svQp9nyhPB4AqLDrY28x/OrmRh/qYq953i6Y4Z8c76OHqqGcUYM4ePysRlcizSgQjdkEDmKC10Ak3OFRRx3/LqYsFIMiOHeg47APg+UANNTyRiTIia5FDhSeHJzaeYCBRQ7UYH0z2rg4cX3YjOz/MoznjHiaaN4MO+5N3v84VawnqwKOvlwPyI2bmz0+9Tr6DKzqA==";
protected final DateService dateService = new SimpleDateFormatDateService();
@Test
public void testApply() throws Exception {
Crypto crypto = new BouncyCastleCrypto();
WindowsLoginCredentialsFromEncryptedData f = new WindowsLoginCredentialsFromEncryptedData(crypto);
PasswordData passwordData = PasswordData.builder().passwordData(ENCRYPTED_PASSWORD).build();
PasswordData passwordData = PasswordData.builder()
.instanceId("i-2574e22a")
.timestamp(dateService.iso8601DateParse("2012-07-30T07:27:23.000+0000"))
.passwordData(ENCRYPTED_PASSWORD).build();
LoginCredentials credentials = f.apply(new PasswordDataAndPrivateKey(passwordData, PRIVATE_KEY));

View File

@ -0,0 +1,81 @@
/**
* 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
*
* Unles 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 expres or implied. See the License for the
* specific language governing permisions and limitations
* under the License.
*/
package org.jclouds.ec2.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import java.util.TimeZone;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.internal.BaseEC2ExpectTest;
import org.jclouds.ec2.parse.GetPasswordDataResponseTest;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "WindowsApiExpectTest")
public class WindowsApiExpectTest extends BaseEC2ExpectTest<EC2Client> {
public WindowsApiExpectTest() {
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
}
HttpRequest get = HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=GetPasswordData" +
"&InstanceId=i-2574e22a" +
"&Signature=vX1Tskc4VuBUWPqsJ%2BzcjEj6%2F2iMCKzqjWnKFXRkDdA%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-06-15" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
public void testGetPasswordDataWhenResponseIs2xx() throws Exception {
HttpResponse getResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/get_passworddata.xml", "text/xml")).build();
EC2Client apiWhenExist = requestSendsResponse(
get, getResponse);
assertEquals(apiWhenExist.getWindowsApi().getPasswordDataForInstance("i-2574e22a").toString(), new GetPasswordDataResponseTest().expected().toString());
}
public void testGetPasswordDataWhenResponseIs404() throws Exception {
HttpResponse getResponse = HttpResponse.builder().statusCode(404).build();
EC2Client apiWhenDontExist = requestSendsResponse(
get, getResponse);
assertNull(apiWhenDontExist.getWindowsApi().getPasswordDataForInstance("i-2574e22a"));
}
}

View File

@ -0,0 +1,35 @@
/**
* 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.features;
import org.jclouds.ec2.internal.BaseEC2ClientLiveTest;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
@Test(groups = "live", testName = "WindowsApiLiveTest")
public class WindowsApiLiveTest extends BaseEC2ClientLiveTest {
protected WindowsApi api() {
return context.getApi().getWindowsApi();
}
}

View File

@ -0,0 +1,26 @@
package org.jclouds.ec2.internal;
import org.jclouds.apis.BaseContextLiveTest;
import org.jclouds.ec2.EC2ApiMetadata;
import org.jclouds.ec2.EC2AsyncClient;
import org.jclouds.ec2.EC2Client;
import org.jclouds.rest.RestContext;
import com.google.common.reflect.TypeToken;
/**
*
* @author Adrian Cole
*/
public class BaseEC2ClientLiveTest extends BaseContextLiveTest<RestContext<? extends EC2Client, ? extends EC2AsyncClient>> {
public BaseEC2ClientLiveTest() {
provider = "ec2";
}
@Override
protected TypeToken<RestContext<? extends EC2Client, ? extends EC2AsyncClient>> contextType() {
return EC2ApiMetadata.CONTEXT_TOKEN;
}
}

View File

@ -0,0 +1,57 @@
/**
* 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.parse;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.date.DateService;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.ec2.domain.PasswordData;
import org.jclouds.ec2.xml.GetPasswordDataResponseHandler;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
@Test(groups = "unit", testName = "GetPasswordDataResponseTest")
public class GetPasswordDataResponseTest extends BaseHandlerTest {
protected final DateService dateService = new SimpleDateFormatDateService();
public void test() {
InputStream is = getClass().getResourceAsStream("/get_passworddata.xml");
PasswordData expected = expected();
GetPasswordDataResponseHandler handler = injector.getInstance(GetPasswordDataResponseHandler.class);
PasswordData result = factory.create(handler).parse(is);
assertEquals(result.toString(), expected.toString());
}
public PasswordData expected() {
return PasswordData.builder()
.instanceId("i-2574e22a")
.timestamp(dateService.iso8601DateParse("2012-07-30T07:27:23.000+0000"))
.passwordData("TGludXggdmVyc2lvbiAyLjYuMTYteGVuVSAoYnVpbGRlckBwYXRjaGJhdC5hbWF6b25zYSkgKGdj").build();
}
}

View File

@ -0,0 +1,6 @@
<GetPasswordDataResponse xmlns="http://ec2.amazonaws.com/doc/2012-06-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<instanceId>i-2574e22a</instanceId>
<timestamp>2012-07-30T07:27:23.000+0000</timestamp>
<passwordData>TGludXggdmVyc2lvbiAyLjYuMTYteGVuVSAoYnVpbGRlckBwYXRjaGJhdC5hbWF6b25zYSkgKGdj</passwordData>
</GetPasswordDataResponse>

View File

@ -41,6 +41,8 @@
<test.aws-ec2.credential>${test.aws.credential}</test.aws-ec2.credential>
<test.aws-ec2.template></test.aws-ec2.template>
<test.aws-ec2.ebs-template>hardwareId=m1.small,imageId=us-west-2/ami-38c64a08</test.aws-ec2.ebs-template>
<test.aws-ec2.windows-template>hardwareId=m1.small,imageNameMatches=Windows_Server-2008-R2_SP1-English-64Bit-Base-WinRM-</test.aws-ec2.windows-template>
<test.aws-ec2.windows-owner>449550055360</test.aws-ec2.windows-owner>
<jclouds.osgi.export>org.jclouds.aws.ec2*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>
org.jclouds.compute.internal;version="${project.version}",
@ -137,6 +139,8 @@
<test.aws-ec2.credential>${test.aws-ec2.credential}</test.aws-ec2.credential>
<test.aws-ec2.template>${test.aws-ec2.template}</test.aws-ec2.template>
<test.aws-ec2.ebs-template>${test.aws-ec2.ebs-template}</test.aws-ec2.ebs-template>
<test.aws-ec2.windows-template>${test.aws-ec2.windows-template}</test.aws-ec2.windows-template>
<test.aws-ec2.windows-owner>${test.aws-ec2.windows-owner}</test.aws-ec2.windows-owner>
</systemPropertyVariables>
</configuration>
</execution>

View File

@ -46,12 +46,16 @@ import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.config.ValueOfConfigurationKeyOrNull;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule;
import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.extensions.EC2ImageExtension;
import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair;
import org.jclouds.ec2.compute.functions.CredentialsForInstance;
import org.jclouds.ec2.compute.functions.EC2ImageParser;
import org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstance;
import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
import org.jclouds.ec2.compute.loaders.CreateSecurityGroupIfNeeded;
import org.jclouds.ec2.compute.loaders.LoadPublicIpForInstanceOrNull;
@ -92,6 +96,10 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep
}).annotatedWith(Names.named("SECURITY")).to(CreateSecurityGroupIfNeeded.class);
bind(new TypeLiteral<CacheLoader<RegionAndName, String>>() {
}).annotatedWith(Names.named("ELASTICIP")).to(LoadPublicIpForInstanceOrNull.class);
bind(new TypeLiteral<Function<PasswordDataAndPrivateKey, LoginCredentials>>() {
}).to(WindowsLoginCredentialsFromEncryptedData.class);
bind(new TypeLiteral<Function<RunningInstance, LoginCredentials>>() {
}).to(PasswordCredentialsFromWindowsInstance.class);
bind(new TypeLiteral<Function<RegionAndName, KeyPair>>() {
}).to(CreateUniqueKeyPair.class);
bind(new TypeLiteral<Function<RegionNameAndPublicKeyMaterial, KeyPair>>() {

View File

@ -46,6 +46,8 @@ import org.jclouds.ec2.EC2AsyncClient;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.config.EC2RestClientModule;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.ec2.features.WindowsAsyncApi;
import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.ec2.services.AMIAsyncClient;
import org.jclouds.ec2.services.AMIClient;
@ -88,6 +90,7 @@ public class AWSEC2RestClientModule extends EC2RestClientModule<AWSEC2Client, AW
.put(ElasticBlockStoreClient.class, ElasticBlockStoreAsyncClient.class)//
.put(SpotInstanceClient.class, SpotInstanceAsyncClient.class)//
.put(TagClient.class, TagAsyncClient.class)//
.put(WindowsApi.class, WindowsAsyncApi.class)//
.build();
public AWSEC2RestClientModule() {

View File

@ -0,0 +1,35 @@
/**
* 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.aws.ec2.compute.functions;
import org.jclouds.ec2.compute.functions.PasswordCredentialsFromWindowsInstanceLiveTest;
import org.testng.annotations.Test;
/**
*
* @author Adrian Cole
*/
@Test(groups = "live", singleThreaded = true, testName = "AWSPasswordCredentialsFromWindowsInstanceLiveTest")
public class AWSPasswordCredentialsFromWindowsInstanceLiveTest extends PasswordCredentialsFromWindowsInstanceLiveTest {
public AWSPasswordCredentialsFromWindowsInstanceLiveTest() {
provider = "aws-ec2";
}
}

View File

@ -1,158 +0,0 @@
/**
* 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.aws.ec2.services;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.EC2ApiMetadata;
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.services.WindowsClient;
import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule;
import org.jclouds.predicates.RetryablePredicate;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.inject.Module;
/**
* Tests behavior of {@code WindowsClient}
*
* @author Adrian Cole
*/
@Test(groups = "live", singleThreaded = true, testName = "WindowsClientLiveTest")
public class WindowsClientLiveTest extends BaseComputeServiceContextLiveTest {
public WindowsClientLiveTest() {
provider = "aws-ec2";
}
private ComputeService computeService;
private WindowsClient client;
private static final String DEFAULT_INSTANCE = "i-TODO";
private static final String DEFAULT_BUCKET = "TODO";
@Override
@BeforeClass(groups = { "integration", "live" })
public void setupContext() {
super.setupContext();
client = view.unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi().getWindowsServices();
computeService = view.getComputeService();
}
@Override
protected Iterable<Module> setupModules() {
Iterable<Module> superModules = super.setupModules();
List<Module> modules = new ArrayList<Module>();
Iterables.addAll(modules, superModules);
modules.add(new BouncyCastleCryptoModule());
return modules;
}
@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() {
}
@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_Server-2008-R2_SP1-English-64Bit-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);
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 = view.getUtils().getInjector().getInstance(WindowsLoginCredentialsFromEncryptedData.class);
LoginCredentials credentials = f.apply(dataAndKey);
assertEquals(credentials.getUser(), "Administrator");
assertFalse(Strings.isNullOrEmpty(credentials.getPassword()));
} finally {
computeService.destroyNode(node.getId());
}
}
}