mirror of https://github.com/apache/jclouds.git
added windows password parsing to ec2
This commit is contained in:
parent
3c2a9c9614
commit
53fe278f03
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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>() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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()));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>>() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue