diff --git a/apis/ec2/pom.xml b/apis/ec2/pom.xml index ccaffbf752..24e64f9bcc 100644 --- a/apis/ec2/pom.xml +++ b/apis/ec2/pom.xml @@ -83,6 +83,12 @@ ${project.version} test + + org.jclouds.driver + jclouds-bouncycastle + ${project.version} + test + diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java index a11244ec77..2b7f57fce4 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java @@ -18,19 +18,52 @@ */ package org.jclouds.ec2.services; -import java.util.Properties; - +import com.google.common.base.Predicate; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.Module; import org.jclouds.compute.BaseVersionedServiceLiveTest; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.EC2ContextBuilder; +import org.jclouds.ec2.EC2PropertiesBuilder; +import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey; +import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData; +import org.jclouds.ec2.domain.InstanceType; +import org.jclouds.ec2.domain.PasswordData; +import org.jclouds.ec2.reference.EC2Constants; +import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule; +import org.jclouds.logging.Logger; import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.predicates.RetryablePredicate; import org.jclouds.rest.RestContext; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableSet; -import com.google.inject.Module; +import javax.annotation.Nullable; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; /** * Tests behavior of {@code WindowsClient} @@ -43,19 +76,30 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest { provider = "ec2"; } + private ComputeService computeService; private WindowsClient client; private static final String DEFAULT_INSTANCE = "i-TODO"; private static final String DEFAULT_BUCKET = "TODO"; private RestContext context; + @Override + public Properties setupRestProperties() { + Properties rest = super.setupRestProperties(); + rest.put("ec2.contextbuilder", EC2ContextBuilder.class.getName()); + rest.put("ec2.propertiesbuilder", EC2PropertiesBuilder.class.getName()); + return rest; + } @BeforeGroups(groups = { "live" }) public void setupClient() { setupCredentials(); Properties overrides = setupProperties(); - context = new ComputeServiceContextFactory().createContext(provider, - ImmutableSet. of(new Log4JLoggingModule()), overrides).getProviderSpecificContext(); + overrides.put(EC2Constants.PROPERTY_EC2_AMI_OWNERS, "206029621532"); + ComputeServiceContext serviceContext = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, + ImmutableSet.of(new Log4JLoggingModule(), new BouncyCastleCryptoModule()), overrides); + computeService = serviceContext.getComputeService(); + context = serviceContext.getProviderSpecificContext(); client = context.getApi().getWindowsServices(); } @@ -82,4 +126,52 @@ public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest { public void testDescribeBundleTasksInRegion() { } + + @Test + public void testGetPasswordData() throws Exception { + + // Spin up a new node. Make sure to open the RDP port 3389 + Template template = computeService.templateBuilder() + .osFamily(OsFamily.WINDOWS) + .os64Bit(true) + .imageNameMatches("Windows-2008R2-SP1-English-Base-") + .hardwareId(InstanceType.M1_LARGE) + .options(TemplateOptions.Builder.inboundPorts(3389)) + .build(); + Set nodes = computeService.createNodesInGroup("test", 1, template); + NodeMetadata node = Iterables.getOnlyElement(nodes); + + boolean shutdown = true; + try { + + // The Administrator password will take some time before it is ready - Amazon says sometimes 15 minutes. + // So we create a predicate that tests if the password is ready, and wrap it in a retryable predicate. + Predicate passwordReady = new Predicate() { + @Override + public boolean apply(@Nullable String s) { + if (Strings.isNullOrEmpty(s)) return false; + PasswordData data = client.getPasswordData(null, s); + if (data == null) return false; + return !Strings.isNullOrEmpty(data.getPasswordData()); + } + }; + RetryablePredicate passwordReadyRetryable = new RetryablePredicate(passwordReady, 600, 10, TimeUnit.SECONDS); + assertTrue(passwordReadyRetryable.apply(node.getProviderId())); + + // Now pull together Amazon's encrypted password blob, and the private key that jclouds generated + PasswordDataAndPrivateKey dataAndKey = new PasswordDataAndPrivateKey( + client.getPasswordData(null, node.getProviderId()), + node.getCredentials().getPrivateKey()); + + // And apply it to the decryption function + WindowsLoginCredentialsFromEncryptedData f = context.getUtils().getInjector().getInstance(WindowsLoginCredentialsFromEncryptedData.class); + LoginCredentials credentials = f.apply(dataAndKey); + + assertEquals(credentials.getUser(), "Administrator"); + assertFalse(Strings.isNullOrEmpty(credentials.getPassword())); + } finally { + computeService.destroyNode(node.getId()); + } + } + }