http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java new file mode 100644 index 0000000..8cd20b9 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java @@ -0,0 +1,357 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.brooklyn.location.jclouds; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.apache.brooklyn.location.basic.LocationInternal; +import org.apache.brooklyn.location.cloud.CloudLocationConfig; +import org.apache.brooklyn.test.entity.LocalManagementContextForTests; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.config.BrooklynProperties; +import brooklyn.event.basic.MapConfigKey; +import brooklyn.event.basic.SetConfigKey; +import brooklyn.management.internal.LocalManagementContext; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.collections.MutableSet; + +public class JcloudsLocationResolverTest { + + private static final Logger log = LoggerFactory.getLogger(JcloudsLocationResolverTest.class); + + private LocalManagementContext managementContext; + private BrooklynProperties brooklynProperties; + + @BeforeMethod(alwaysRun = true) + public void setUp() throws Exception { + managementContext = LocalManagementContextForTests.newInstance(); + brooklynProperties = managementContext.getBrooklynProperties(); + + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.identity", "aws-ec2-id"); + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.credential", "aws-ec2-cred"); + brooklynProperties.put("brooklyn.location.jclouds.rackspace-cloudservers-uk.identity", "cloudservers-uk-id"); + brooklynProperties.put("brooklyn.location.jclouds.rackspace-cloudservers-uk.credential", "cloudservers-uk-cred"); + } + + @AfterMethod(alwaysRun = true) + public void tearDown() throws Exception { + if (managementContext != null) + managementContext.terminate(); + } + + @Test + public void testJcloudsTakesDotSeparateProperty() { + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.loginUser.privateKeyFile", "myfile"); + String file = resolve("jclouds:aws-ec2").getConfig(JcloudsLocation.LOGIN_USER_PRIVATE_KEY_FILE); + assertEquals(file, "myfile"); + } + + @Test + public void testJcloudsTakesProviderScopedProperties() { + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.privateKeyFile", "myprivatekeyfile"); + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.publicKeyFile", "mypublickeyfile"); + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.privateKeyData", "myprivateKeyData"); + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.publicKeyData", "myPublicKeyData"); + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.privateKeyPassphrase", "myprivateKeyPassphrase"); + Map<String, Object> conf = resolve("jclouds:aws-ec2").config().getBag().getAllConfig(); + + assertEquals(conf.get("privateKeyFile"), "myprivatekeyfile"); + assertEquals(conf.get("publicKeyFile"), "mypublickeyfile"); + assertEquals(conf.get("privateKeyData"), "myprivateKeyData"); + assertEquals(conf.get("publicKeyData"), "myPublicKeyData"); + assertEquals(conf.get("privateKeyPassphrase"), "myprivateKeyPassphrase"); + } + + @Test + public void testJcloudsTakesGenericScopedProperties() { + brooklynProperties.put("brooklyn.location.jclouds.privateKeyFile", "myprivatekeyfile"); + brooklynProperties.put("brooklyn.location.jclouds.publicKeyFile", "mypublickeyfile"); + brooklynProperties.put("brooklyn.location.jclouds.privateKeyData", "myprivateKeyData"); + brooklynProperties.put("brooklyn.location.jclouds.publicKeyData", "myPublicKeyData"); + brooklynProperties.put("brooklyn.location.jclouds.privateKeyPassphrase", "myprivateKeyPassphrase"); + Map<String, Object> conf = resolve("jclouds:aws-ec2").config().getBag().getAllConfig(); + + assertEquals(conf.get("privateKeyFile"), "myprivatekeyfile"); + assertEquals(conf.get("publicKeyFile"), "mypublickeyfile"); + assertEquals(conf.get("privateKeyData"), "myprivateKeyData"); + assertEquals(conf.get("publicKeyData"), "myPublicKeyData"); + assertEquals(conf.get("privateKeyPassphrase"), "myprivateKeyPassphrase"); + } + + @Test + public void testJcloudsTakesDeprecatedProperties() { + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.private-key-file", "myprivatekeyfile"); + brooklynProperties.put("brooklyn.location.jclouds.public-key-file", "mypublickeyfile"); + brooklynProperties.put("brooklyn.location.jclouds.private-key-data", "myprivateKeyData"); + brooklynProperties.put("brooklyn.location.jclouds.public-key-data", "myPublicKeyData"); + brooklynProperties.put("brooklyn.location.jclouds.private-key-passphrase", "myprivateKeyPassphrase"); + brooklynProperties.put("brooklyn.location.jclouds.image-id", "myimageid"); + Map<String, Object> conf = resolve("jclouds:aws-ec2").config().getBag().getAllConfig(); + + assertEquals(conf.get("privateKeyFile"), "myprivatekeyfile"); + assertEquals(conf.get("publicKeyFile"), "mypublickeyfile"); + assertEquals(conf.get("privateKeyData"), "myprivateKeyData"); + assertEquals(conf.get("publicKeyData"), "myPublicKeyData"); + assertEquals(conf.get("privateKeyPassphrase"), "myprivateKeyPassphrase"); + assertEquals(conf.get("imageId"), "myimageid"); + } + + @Test + public void testJcloudsPropertiesPrecedence() { + brooklynProperties.put("brooklyn.location.named.myaws-ec2", "jclouds:aws-ec2"); + + // prefer those in "named" over everything else + brooklynProperties.put("brooklyn.location.named.myaws-ec2.privateKeyFile", "privateKeyFile-inNamed"); + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.privateKeyFile", "privateKeyFile-inProviderSpecific"); + brooklynProperties.put("brooklyn.location.jclouds.privateKeyFile", "privateKeyFile-inJcloudsGeneric"); + + // prefer those in provider-specific over generic + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.publicKeyFile", "publicKeyFile-inProviderSpecific"); + brooklynProperties.put("brooklyn.location.jclouds.publicKeyFile", "publicKeyFile-inJcloudsGeneric"); + + // prefer deprecated properties in "named" over those less specific + brooklynProperties.put("brooklyn.location.named.myaws-ec2.private-key-data", "privateKeyData-inNamed"); + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.privateKeyData", "privateKeyData-inProviderSpecific"); + brooklynProperties.put("brooklyn.location.jclouds.privateKeyData", "privateKeyData-inJcloudsGeneric"); + + // prefer generic if nothing else + brooklynProperties.put("brooklyn.location.jclouds.publicKeyData", "publicKeyData-inJcloudsGeneric"); + + // prefer "named" over everything else: confirm deprecated don't get + // transformed to overwrite it accidentally + brooklynProperties + .put("brooklyn.location.named.myaws-ec2.privateKeyPassphrase", "privateKeyPassphrase-inNamed"); + brooklynProperties.put("brooklyn.location.jclouds.aws-ec2.private-key-passphrase", + "privateKeyPassphrase-inProviderSpecific"); + brooklynProperties.put("brooklyn.location.jclouds.private-key-passphrase", "privateKeyPassphrase-inJcloudsGeneric"); + + Map<String, Object> conf = resolve("named:myaws-ec2").config().getBag().getAllConfig(); + + assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed"); + assertEquals(conf.get("publicKeyFile"), "publicKeyFile-inProviderSpecific"); + assertEquals(conf.get("privateKeyData"), "privateKeyData-inNamed"); + assertEquals(conf.get("publicKeyData"), "publicKeyData-inJcloudsGeneric"); + assertEquals(conf.get("privateKeyPassphrase"), "privateKeyPassphrase-inNamed"); + } + + @Test + public void testJcloudsLoads() { + Assert.assertTrue(resolve("jclouds:aws-ec2") instanceof JcloudsLocation); + } + + @Test + public void testJcloudsImplicitLoads() { + Assert.assertTrue(resolve("aws-ec2") instanceof JcloudsLocation); + } + + @Test + public void testJcloudsLocationLoads() { + Assert.assertTrue(resolve("aws-ec2:eu-west-1") instanceof JcloudsLocation); + } + + @Test + public void testJcloudsRegionOnlyLoads() { + Assert.assertTrue(resolve("eu-west-1") instanceof JcloudsLocation); + } + + @Test + public void testJcloudsEndpointLoads() { + JcloudsLocation loc = resolve("jclouds:openstack-nova:http://foo/api"); + assertEquals(loc.getProvider(), "openstack-nova"); + assertEquals(loc.getEndpoint(), "http://foo/api"); + } + + @Test + public void testJcloudsEndpointLoadsAsProperty() { + brooklynProperties.put("brooklyn.location.jclouds.openstack-nova.endpoint", "myendpoint"); + JcloudsLocation loc = resolve("jclouds:openstack-nova"); + // just checking + Assert.assertEquals(loc.config().getLocalBag().getStringKey("endpoint"), "myendpoint"); + Assert.assertEquals(loc.getConfig(CloudLocationConfig.CLOUD_ENDPOINT), "myendpoint"); + // this is the one we really care about!: + assertEquals(loc.getEndpoint(), "myendpoint"); + } + + @Test + public void testJcloudsLegacyRandomProperty() { + brooklynProperties.put("brooklyn.location.jclouds.openstack-nova.foo", "bar"); + JcloudsLocation loc = resolve("jclouds:openstack-nova"); + Assert.assertEquals(loc.config().getLocalBag().getStringKey("foo"), "bar"); + } + + @Test + public void testJcloudsRandomProperty() { + brooklynProperties.put("brooklyn.location.jclouds.openstack-nova.foo", "bar"); + JcloudsLocation loc = resolve("jclouds:openstack-nova"); + Assert.assertEquals(loc.config().getLocalBag().getStringKey("foo"), "bar"); + } + + @Test + public void testThrowsOnInvalid() throws Exception { + // Tries to treat "wrongprefix" as a cloud provider + assertThrows("wrongprefix:aws-ec2:us-east-1", NoSuchElementException.class); + + // no provider + assertThrows("jclouds", IllegalArgumentException.class); + + // empty provider + assertThrows("jclouds:", IllegalArgumentException.class); + + // invalid provider + assertThrows("jclouds:doesnotexist", NoSuchElementException.class); + } + + @Test + public void testResolvesJclouds() throws Exception { + // test with provider + region + assertJcloudsEquals(resolve("jclouds:aws-ec2:us-east-1"), "aws-ec2", "us-east-1"); + + // test with provider that has no region + assertJcloudsEquals(resolve("jclouds:rackspace-cloudservers-uk"), "rackspace-cloudservers-uk", null); + } + + @Test + public void testJcloudsRegionOverridesParent() { + Map<String, Object> conf; + + brooklynProperties.put("brooklyn.location.named.softlayer-was", "jclouds:softlayer:was01"); + brooklynProperties.put("brooklyn.location.named.softlayer-was2", "jclouds:softlayer:was01"); + brooklynProperties.put("brooklyn.location.named.softlayer-was2.region", "was02"); + conf = resolve("named:softlayer-was").config().getBag().getAllConfig(); + assertEquals(conf.get("region"), "was01"); + + conf = resolve("named:softlayer-was2").config().getBag().getAllConfig(); + assertEquals(conf.get("region"), "was02"); + + conf = ((LocationInternal) managementContext.getLocationRegistry().resolve("named:softlayer-was2", MutableMap.of("region", "was03"))) + .config().getBag().getAllConfig();; + assertEquals(conf.get("region"), "was03"); + } + + // TODO Visual inspection test that it logs warnings + @Test + public void testLogsWarnings() throws Exception { + assertJcloudsEquals(resolve("jclouds:jclouds:aws-ec2:us-east-1"), "aws-ec2", "us-east-1"); + assertJcloudsEquals(resolve("us-east-1"), "aws-ec2", "us-east-1"); + + // TODO Should we enforce a jclouds prefix? Currently we don't + // assertJcloudsEquals(resolve("aws-ec2:us-east-1"), "aws-ec2", + // "us-east-1"); + + } + + @Test + public void testResolvesJcloudsFromNamedOfNamedWithPropertiesOverriddenCorrectly() throws Exception { + brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop1", "1"); + brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop2", "1"); + brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop3", "1"); + brooklynProperties.put("brooklyn.location.named.foo", "jclouds:softlayer:138124"); + brooklynProperties.put("brooklyn.location.named.foo.prop2", "2"); + brooklynProperties.put("brooklyn.location.named.foo.prop3", "2"); + brooklynProperties.put("brooklyn.location.named.bar", "named:foo"); + brooklynProperties.put("brooklyn.location.named.bar.prop3", "3"); + + JcloudsLocation l = resolve("named:bar"); + assertJcloudsEquals(l, "softlayer", "138124"); + Assert.assertEquals(l.config().getLocalBag().getStringKey("prop3"), "3"); + Assert.assertEquals(l.config().getLocalBag().getStringKey("prop2"), "2"); + Assert.assertEquals(l.config().getLocalBag().getStringKey("prop1"), "1"); + } + + @Test + public void testResolvesListAndMapProperties() throws Exception { + brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop1", "[ a, b ]"); + brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop2", "{ a: 1, b: 2 }"); + brooklynProperties.put("brooklyn.location.named.foo", "jclouds:softlayer:ams01"); + + JcloudsLocation l = resolve("named:foo"); + assertJcloudsEquals(l, "softlayer", "ams01"); + assertEquals(l.config().get(new SetConfigKey<String>(String.class, "prop1")), MutableSet.of("a", "b")); + assertEquals(l.config().get(new MapConfigKey<String>(String.class, "prop2")), MutableMap.of("a", 1, "b", 2)); + } + + @Test + public void testResolvesListAndMapPropertiesWithoutMergeOnInheritance() throws Exception { + // when we have a yaml way to specify config we may wish to have different semantics; + // it could depend on the collection config key whether to merge on inheritance + brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop1", "[ a, b ]"); + brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop2", "{ a: 1, b: 2 }"); + brooklynProperties.put("brooklyn.location.named.foo", "jclouds:softlayer:ams01"); + + brooklynProperties.put("brooklyn.location.named.foo.prop1", "[ a: 1, c: 3 ]"); + brooklynProperties.put("brooklyn.location.named.foo.prop2", "{ b: 3, c: 3 }"); + brooklynProperties.put("brooklyn.location.named.bar", "named:foo"); + brooklynProperties.put("brooklyn.location.named.bar.prop2", "{ c: 4, d: 4 }"); + + // these do NOT affect the maps + brooklynProperties.put("brooklyn.location.named.foo.prop2.z", "9"); + brooklynProperties.put("brooklyn.location.named.foo.prop3.z", "9"); + + JcloudsLocation l = resolve("named:bar"); + assertJcloudsEquals(l, "softlayer", "ams01"); + + Set<? extends String> prop1 = l.config().get(new SetConfigKey<String>(String.class, "prop1")); + log.info("prop1: "+prop1); + assertEquals(prop1, MutableSet.of("a: 1", "c: 3")); + + Map<String, String> prop2 = l.config().get(new MapConfigKey<String>(String.class, "prop2")); + log.info("prop2: "+prop2); + assertEquals(prop2, MutableMap.of("c", 4, "d", 4)); + + Map<String, String> prop3 = l.config().get(new MapConfigKey<String>(String.class, "prop3")); + log.info("prop3: "+prop3); + assertEquals(prop3, null); + } + + private void assertJcloudsEquals(JcloudsLocation loc, String expectedProvider, String expectedRegion) { + assertEquals(loc.getProvider(), expectedProvider); + assertEquals(loc.getRegion(), expectedRegion); + } + + private void assertThrows(String val, Class<?> expectedExceptionType) throws Exception { + try { + resolve(val); + fail(); + } catch (Exception e) { + if (!expectedExceptionType.isInstance(e)) + throw e; // otherwise success + + } + } + + @Test(expectedExceptions = { NoSuchElementException.class, IllegalArgumentException.class }, expectedExceptionsMessageRegExp = ".*insufficient.*") + public void testJcloudsOnlyFails() { + resolve("jclouds"); + } + + private JcloudsLocation resolve(String spec) { + return (JcloudsLocation) managementContext.getLocationRegistry().resolve(spec); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java new file mode 100644 index 0000000..f173646 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.brooklyn.location.jclouds; + +import brooklyn.config.ConfigKey; +import brooklyn.util.config.ConfigBag; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.ec2.domain.BlockDeviceMapping; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +public class JcloudsLocationTemplateOptionsCustomisersLiveTest extends AbstractJcloudsLiveTest { + + private static final String LOCATION_SPEC = AWS_EC2_PROVIDER + ":" + AWS_EC2_USEAST_REGION_NAME; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + jcloudsLocation = resolve(LOCATION_SPEC); + } + + // Doesn't actually do much with the cloud, but jclouds requires identity and credential before it will work + @Test(groups = "Live") + public void testGeneralPurposeTemplateOptionCustomisation() throws Exception { + ConfigKey<Map<String, Object>> key = JcloudsLocationConfig.TEMPLATE_OPTIONS; + + ConfigBag config = ConfigBag.newInstance() + .configure(key, ImmutableMap.of("iamInstanceProfileName", (Object)"helloworld")); + AWSEC2TemplateOptions templateOptions = jcloudsLocation.getComputeService().templateOptions().as(AWSEC2TemplateOptions.class); + + invokeCustomizeTemplateOptions(templateOptions, JcloudsLocationConfig.TEMPLATE_OPTIONS, config); + + assertEquals(templateOptions.getIAMInstanceProfileName(), "helloworld"); + } + + // Doesn't actually do much with the cloud, but jclouds requires identity and credential before it will work + @Test(groups = "Live") + public void testGeneralPurposeTemplateOptionCustomisationWithList() throws Exception { + ConfigKey<Map<String, Object>> key = JcloudsLocationConfig.TEMPLATE_OPTIONS; + + ConfigBag config = ConfigBag.newInstance() + .configure(key, ImmutableMap.of( + "iamInstanceProfileName", (Object) "helloworld", + "mapNewVolumeToDeviceName", (Object) ImmutableList.of("/dev/sda1/", 123, true))); + AWSEC2TemplateOptions templateOptions = jcloudsLocation.getComputeService().templateOptions().as(AWSEC2TemplateOptions.class); + + invokeCustomizeTemplateOptions(templateOptions, JcloudsLocationConfig.TEMPLATE_OPTIONS, config); + + assertEquals(templateOptions.getIAMInstanceProfileName(), "helloworld"); + assertEquals(templateOptions.getBlockDeviceMappings().size(), 1); + BlockDeviceMapping blockDeviceMapping = templateOptions.getBlockDeviceMappings().iterator().next(); + assertEquals(blockDeviceMapping.getDeviceName(), "/dev/sda1/"); + assertEquals(blockDeviceMapping.getEbsVolumeSize(), (Integer)123); + assertTrue(blockDeviceMapping.getEbsDeleteOnTermination()); + } + + /** + * Invoke a specific template options customizer on a TemplateOptions instance. + * + * @param templateOptions the TemplateOptions instance that you expect the customizer to modify. + * @param keyToTest the config key that identifies the customizer. This must be present in both @{code locationConfig} and @{link JcloudsLocation.SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES}. + * @param locationConfig simulated configuration for the location. This must contain at least an entry for @{code keyToTest}. + */ + private void invokeCustomizeTemplateOptions(TemplateOptions templateOptions, ConfigKey<?> keyToTest, ConfigBag locationConfig) { + checkNotNull(templateOptions, "templateOptions"); + checkNotNull(keyToTest, "keyToTest"); + checkNotNull(locationConfig, "locationConfig"); + checkState(JcloudsLocation.SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES.containsKey(keyToTest), + "SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES does not contain a customiser for the key " + keyToTest.getName()); + checkState(locationConfig.containsKey(keyToTest), + "location config does not contain the key " + keyToTest.getName()); + + JcloudsLocation.CustomizeTemplateOptions code = JcloudsLocation.SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES.get(keyToTest); + code.apply(templateOptions, locationConfig, locationConfig.get(keyToTest)); + } + + private JcloudsLocation resolve(String spec) { + return (JcloudsLocation) managementContext.getLocationRegistry().resolve("jclouds:"+spec); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java new file mode 100644 index 0000000..4c5788f --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java @@ -0,0 +1,510 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.brooklyn.location.jclouds; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.annotation.Nullable; + +import org.apache.brooklyn.location.MachineLocationCustomizer; +import org.apache.brooklyn.location.cloud.names.CustomMachineNamer; +import org.apache.brooklyn.test.entity.LocalManagementContextForTests; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.StatementList; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.reflect.TypeToken; + +import brooklyn.config.BrooklynProperties; +import brooklyn.config.ConfigKey; +import brooklyn.entity.basic.ConfigKeys; +import brooklyn.entity.basic.Entities; +import org.apache.brooklyn.location.LocationSpec; +import org.apache.brooklyn.location.MachineLocation; +import org.apache.brooklyn.location.NoMachinesAvailableException; +import org.apache.brooklyn.location.basic.LocationConfigKeys; +import org.apache.brooklyn.location.geo.HostGeoInfo; +import org.apache.brooklyn.location.jclouds.JcloudsLocation.UserCreation; +import brooklyn.management.internal.LocalManagementContext; +import brooklyn.test.Asserts; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.config.ConfigBag; +import brooklyn.util.exceptions.Exceptions; + +/** + * @author Shane Witbeck + */ +public class JcloudsLocationTest implements JcloudsLocationConfig { + + private static final Logger log = LoggerFactory.getLogger(JcloudsLocationTest.class); + + public static Predicate<ConfigBag> checkerFor(final String user, final Integer minRam, final Integer minCores) { + return new Predicate<ConfigBag>() { + @Override + public boolean apply(@Nullable ConfigBag input) { + Assert.assertEquals(input.get(USER), user); + Assert.assertEquals(input.get(MIN_RAM), minRam); + Assert.assertEquals(input.get(MIN_CORES), minCores); + return true; + } + }; + } + + public static Predicate<ConfigBag> templateCheckerFor(final String ports) { + return new Predicate<ConfigBag>() { + @Override + public boolean apply(@Nullable ConfigBag input) { + Assert.assertEquals(input.get(INBOUND_PORTS), ports); + return false; + } + }; + } + + private LocalManagementContext managementContext; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + managementContext = LocalManagementContextForTests.newInstance(BrooklynProperties.Factory.builderEmpty().build()); + } + + @AfterMethod(alwaysRun=true) + public void tearUp() throws Exception { + if (managementContext != null) Entities.destroyAll(managementContext); + } + + @Test + public void testCreateWithFlagsDirectly() throws Exception { + BailOutJcloudsLocation jcl = BailOutJcloudsLocation.newBailOutJcloudsLocation(managementContext); + jcl.tryObtainAndCheck(MutableMap.of(MIN_CORES, 2), checkerFor("fred", 16, 2)); + } + + @Test + public void testCreateWithFlagsDirectlyAndOverride() throws Exception { + BailOutJcloudsLocation jcl = BailOutJcloudsLocation.newBailOutJcloudsLocation(managementContext); + jcl.tryObtainAndCheck(MutableMap.of(MIN_CORES, 2, MIN_RAM, 8), checkerFor("fred", 8, 2)); + } + + @Test + public void testCreateWithFlagsSubLocation() throws Exception { + BailOutJcloudsLocation jcl = BailOutJcloudsLocation.newBailOutJcloudsLocation(managementContext); + jcl = (BailOutJcloudsLocation) jcl.newSubLocation(MutableMap.of(USER, "jon", MIN_CORES, 2)); + jcl.tryObtainAndCheck(MutableMap.of(MIN_CORES, 3), checkerFor("jon", 16, 3)); + } + + @Test + public void testStringListToIntArray() { + String listString = "[1, 2, 3, 4]"; + int[] intArray = new int[] {1, 2, 3, 4}; + Assert.assertEquals(JcloudsLocation.toIntArray(listString), intArray); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testMalformedStringListToIntArray() { + String listString = "1, 2, 3, 4"; + JcloudsLocation.toIntArray(listString); + } + + @Test + public void testEmptyStringListToIntArray() { + String listString = "[]"; + int[] intArray = new int[] {}; + Assert.assertEquals(JcloudsLocation.toIntArray(listString), intArray); + } + + @Test + public void testIntArrayToIntArray() { + int[] intArray = new int[] {1, 2, 3, 4}; + Assert.assertEquals(JcloudsLocation.toIntArray(intArray), intArray); + } + + @Test + public void testObjectArrayToIntArray() { + Object[] longArray = new Object[] {1, 2, 3, 4}; + int[] intArray = new int[] {1, 2, 3, 4}; + Assert.assertEquals(JcloudsLocation.toIntArray(longArray), intArray); + } + + @Test(expectedExceptions = ClassCastException.class) + public void testInvalidObjectArrayToIntArray() { + String[] stringArray = new String[] {"1", "2", "3"}; + JcloudsLocation.toIntArray(stringArray); + } + + @Test + public void testVMCreationIsRetriedOnFailure() { + final AtomicInteger count = new AtomicInteger(); + Function<ConfigBag, Void> countingInterceptor = new Function<ConfigBag, Void>() { + @Override public Void apply(ConfigBag input) { + count.incrementAndGet(); + return null; + } + }; + BailOutJcloudsLocation loc = BailOutJcloudsLocation.newBailOutJcloudsLocation(managementContext, ImmutableMap.<ConfigKey<?>, Object>of( + MACHINE_CREATE_ATTEMPTS, 3, + BailOutJcloudsLocation.BUILD_TEMPLATE_INTERCEPTOR, countingInterceptor)); + loc.tryObtain(); + Assert.assertEquals(count.get(), 3); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testCreateWithInboundPorts() { + BailOutJcloudsLocation jcloudsLocation = BailOutJcloudsLocation.newBailOutJcloudsLocationForLiveTest(managementContext); + jcloudsLocation = (BailOutJcloudsLocation) jcloudsLocation.newSubLocation(MutableMap.of()); + jcloudsLocation.tryObtainAndCheck(MutableMap.of(), templateCheckerFor("[22, 80, 9999]")); + int[] ports = new int[] {22, 80, 9999}; + Assert.assertEquals(jcloudsLocation.getTemplate().getOptions().getInboundPorts(), ports); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testCreateWithInboundPortsOverride() { + BailOutJcloudsLocation jcloudsLocation = BailOutJcloudsLocation.newBailOutJcloudsLocationForLiveTest(managementContext); + jcloudsLocation = (BailOutJcloudsLocation) jcloudsLocation.newSubLocation(MutableMap.of()); + jcloudsLocation.tryObtainAndCheck(MutableMap.of(INBOUND_PORTS, "[23, 81, 9998]"), templateCheckerFor("[23, 81, 9998]")); + int[] ports = new int[] {23, 81, 9998}; + Assert.assertEquals(jcloudsLocation.getTemplate().getOptions().getInboundPorts(), ports); + } + + @Test + public void testCreateWithMaxConcurrentCallsUnboundedByDefault() throws Exception { + final int numCalls = 20; + ConcurrencyTracker interceptor = new ConcurrencyTracker(); + ExecutorService executor = Executors.newCachedThreadPool(); + + try { + final BailOutJcloudsLocation jcloudsLocation = BailOutJcloudsLocation.newBailOutJcloudsLocation( + managementContext, ImmutableMap.<ConfigKey<?>, Object>of( + BailOutJcloudsLocation.BUILD_TEMPLATE_INTERCEPTOR, interceptor)); + for (int i = 0; i < numCalls; i++) { + executor.execute(new Runnable() { + @Override + public void run() { + jcloudsLocation.tryObtain(); + } + }); + } + interceptor.assertCallCountEventually(numCalls); + interceptor.unblock(); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + } finally { + executor.shutdownNow(); + } + } + + @Test(groups="Integration") // because takes 1 sec + public void testCreateWithMaxConcurrentCallsRespectsConfig() throws Exception { + final int numCalls = 4; + final int maxConcurrentCreations = 2; + ConcurrencyTracker interceptor = new ConcurrencyTracker(); + ExecutorService executor = Executors.newCachedThreadPool(); + + try { + final BailOutJcloudsLocation jcloudsLocation = BailOutJcloudsLocation.newBailOutJcloudsLocation( + managementContext, ImmutableMap.of( + BailOutJcloudsLocation.BUILD_TEMPLATE_INTERCEPTOR, interceptor, + MAX_CONCURRENT_MACHINE_CREATIONS, maxConcurrentCreations)); + + for (int i = 0; i < numCalls; i++) { + executor.execute(new Runnable() { + @Override + public void run() { + jcloudsLocation.tryObtain(); + } + }); + } + + interceptor.assertCallCountEventually(maxConcurrentCreations); + interceptor.assertCallCountContinually(maxConcurrentCreations); + + interceptor.unblock(); + interceptor.assertCallCountEventually(numCalls); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + + } finally { + executor.shutdownNow(); + } + } + + @Test(groups="Integration") // because takes 1 sec + public void testCreateWithMaxConcurrentCallsAppliesToSubLocations() throws Exception { + final int numCalls = 4; + final int maxConcurrentCreations = 2; + ConcurrencyTracker interceptor = new ConcurrencyTracker(); + ExecutorService executor = Executors.newCachedThreadPool(); + + try { + final BailOutJcloudsLocation jcloudsLocation = BailOutJcloudsLocation.newBailOutJcloudsLocation( + managementContext, ImmutableMap.of( + BailOutJcloudsLocation.BUILD_TEMPLATE_INTERCEPTOR, interceptor, + MAX_CONCURRENT_MACHINE_CREATIONS, maxConcurrentCreations)); + + for (int i = 0; i < numCalls; i++) { + final BailOutJcloudsLocation subLocation = (BailOutJcloudsLocation) jcloudsLocation.newSubLocation(MutableMap.of()); + executor.execute(new Runnable() { + @Override + public void run() { + subLocation.tryObtain(); + } + }); + } + + interceptor.assertCallCountEventually(maxConcurrentCreations); + interceptor.assertCallCountContinually(maxConcurrentCreations); + + interceptor.unblock(); + interceptor.assertCallCountEventually(numCalls); + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + + } finally { + executor.shutdownNow(); + } + } + + @Test + public void testCreateWithCustomMachineNamer() { + final String machineNamerClass = CustomMachineNamer.class.getName(); + BailOutJcloudsLocation jcloudsLocation = BailOutJcloudsLocation.newBailOutJcloudsLocation( + managementContext, ImmutableMap.<ConfigKey<?>, Object>of( + LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS, machineNamerClass)); + jcloudsLocation.tryObtainAndCheck(ImmutableMap.of(CustomMachineNamer.MACHINE_NAME_TEMPLATE, "ignored"), new Predicate<ConfigBag>() { + public boolean apply(ConfigBag input) { + Assert.assertEquals(input.get(LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS), machineNamerClass); + return true; + } + }); + } + + @Test + public void testCreateWithCustomMachineNamerOnObtain() { + final String machineNamerClass = CustomMachineNamer.class.getName(); + BailOutJcloudsLocation jcloudsLocation = BailOutJcloudsLocation.newBailOutJcloudsLocation(managementContext); + ImmutableMap<ConfigKey<String>, String> flags = ImmutableMap.of( + CustomMachineNamer.MACHINE_NAME_TEMPLATE, "ignored", + LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS, machineNamerClass); + jcloudsLocation.tryObtainAndCheck(flags, new Predicate<ConfigBag>() { + public boolean apply(ConfigBag input) { + Assert.assertEquals(input.get(LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS), machineNamerClass); + return true; + } + }); + } + + public static class ConcurrencyTracker implements Function<ConfigBag,Void> { + final AtomicInteger concurrentCallsCounter = new AtomicInteger(); + final CountDownLatch continuationLatch = new CountDownLatch(1); + + @Override public Void apply(ConfigBag input) { + concurrentCallsCounter.incrementAndGet(); + try { + continuationLatch.await(); + } catch (InterruptedException e) { + throw Exceptions.propagate(e); + } + return null; + } + + public void unblock() { + continuationLatch.countDown(); + } + + public void assertCallCountEventually(final int expected) { + Asserts.succeedsEventually(new Runnable() { + @Override public void run() { + Assert.assertEquals(concurrentCallsCounter.get(), expected); + } + }); + } + + public void assertCallCountContinually(final int expected) { + Asserts.succeedsContinually(new Runnable() { + @Override public void run() { + Assert.assertEquals(concurrentCallsCounter.get(), expected); + } + }); + } + } + + + @SuppressWarnings("serial") + public static class FakeLocalhostWithParentJcloudsLocation extends JcloudsLocation { + public static final ConfigKey<Function<ConfigBag,Void>> BUILD_TEMPLATE_INTERCEPTOR = ConfigKeys.newConfigKey(new TypeToken<Function<ConfigBag,Void>>() {}, "buildtemplateinterceptor"); + + ConfigBag lastConfigBag; + + public FakeLocalhostWithParentJcloudsLocation() { + super(); + } + + public FakeLocalhostWithParentJcloudsLocation(Map<?, ?> conf) { + super(conf); + } + + @Override + public JcloudsSshMachineLocation obtain(Map<?, ?> flags) throws NoMachinesAvailableException { + JcloudsSshMachineLocation result = getManagementContext().getLocationManager().createLocation(LocationSpec.create(JcloudsSshMachineLocation.class) + .configure("address", "127.0.0.1") + .configure("port", 22) + .configure("user", "bob") + .configure("jcloudsParent", this)); + registerJcloudsMachineLocation("bogus", result); + + // explicitly invoke this customizer, to comply with tests + for (JcloudsLocationCustomizer customizer : getCustomizers(config().getBag())) { + customizer.customize(this, null, (JcloudsMachineLocation)result); + } + for (MachineLocationCustomizer customizer : getMachineCustomizers(config().getBag())) { + customizer.customize((JcloudsMachineLocation)result); + } + + return result; + } + + @Override + protected void releaseNode(String instanceId) { + // no-op + } + } + + @Test + public void testInheritsGeo() throws Exception { + ConfigBag allConfig = ConfigBag.newInstance() + .configure(IMAGE_ID, "bogus") + .configure(CLOUD_PROVIDER, "aws-ec2") + .configure(CLOUD_REGION_ID, "bogus") + .configure(ACCESS_IDENTITY, "bogus") + .configure(ACCESS_CREDENTIAL, "bogus") + .configure(LocationConfigKeys.LATITUDE, 42d) + .configure(LocationConfigKeys.LONGITUDE, -20d) + .configure(MACHINE_CREATE_ATTEMPTS, 1); + FakeLocalhostWithParentJcloudsLocation ll = managementContext.getLocationManager().createLocation(LocationSpec.create(FakeLocalhostWithParentJcloudsLocation.class).configure(allConfig.getAllConfig())); + MachineLocation l = ll.obtain(); + log.info("loc:" +l); + HostGeoInfo geo = HostGeoInfo.fromLocation(l); + log.info("geo: "+geo); + Assert.assertEquals(geo.latitude, 42d, 0.00001); + Assert.assertEquals(geo.longitude, -20d, 0.00001); + } + + @SuppressWarnings("unchecked") + @Test + public void testInheritsGeoFromLocationMetadataProperties() throws Exception { + // in location-metadata.properties: +// [email protected]=38.909202 +// [email protected]=-77.47314 + ConfigBag allConfig = ConfigBag.newInstance() + .configure(IMAGE_ID, "bogus") + .configure(CLOUD_PROVIDER, "softlayer") + .configure(CLOUD_REGION_ID, "wdc01") + .configure(ACCESS_IDENTITY, "bogus") + .configure(ACCESS_CREDENTIAL, "bogus") + .configure(MACHINE_CREATE_ATTEMPTS, 1); + FakeLocalhostWithParentJcloudsLocation ll = managementContext.getLocationManager().createLocation(LocationSpec.create(FakeLocalhostWithParentJcloudsLocation.class) + .configure(new JcloudsPropertiesFromBrooklynProperties().getJcloudsProperties("softlayer", "wdc01", null, managementContext.getBrooklynProperties())) + .configure(allConfig.getAllConfig())); + MachineLocation l = ll.obtain(); + log.info("loc:" +l); + HostGeoInfo geo = HostGeoInfo.fromLocation(l); + log.info("geo: "+geo); + Assert.assertEquals(geo.latitude, 38.909202d, 0.00001); + Assert.assertEquals(geo.longitude, -77.47314d, 0.00001); + } + + @Test + public void testInvokesCustomizerCallbacks() throws Exception { + JcloudsLocationCustomizer customizer = Mockito.mock(JcloudsLocationCustomizer.class); + MachineLocationCustomizer machineCustomizer = Mockito.mock(MachineLocationCustomizer.class); +// Mockito.when(customizer.customize(Mockito.any(JcloudsLocation.class), Mockito.any(ComputeService.class), Mockito.any(JcloudsSshMachineLocation.class))); + ConfigBag allConfig = ConfigBag.newInstance() + .configure(CLOUD_PROVIDER, "aws-ec2") + .configure(ACCESS_IDENTITY, "bogus") + .configure(ACCESS_CREDENTIAL, "bogus") + .configure(JcloudsLocationConfig.JCLOUDS_LOCATION_CUSTOMIZERS, ImmutableList.of(customizer)) + .configure(JcloudsLocation.MACHINE_LOCATION_CUSTOMIZERS, ImmutableList.of(machineCustomizer)) + .configure(MACHINE_CREATE_ATTEMPTS, 1); + FakeLocalhostWithParentJcloudsLocation ll = managementContext.getLocationManager().createLocation(LocationSpec.create(FakeLocalhostWithParentJcloudsLocation.class).configure(allConfig.getAllConfig())); + JcloudsMachineLocation l = (JcloudsMachineLocation)ll.obtain(); + Mockito.verify(customizer, Mockito.times(1)).customize(ll, null, l); + Mockito.verify(customizer, Mockito.never()).preRelease(l); + Mockito.verify(customizer, Mockito.never()).postRelease(l); + Mockito.verify(machineCustomizer, Mockito.times(1)).customize(l); + Mockito.verify(machineCustomizer, Mockito.never()).preRelease(l); + + ll.release(l); + Mockito.verify(customizer, Mockito.times(1)).preRelease(l); + Mockito.verify(customizer, Mockito.times(1)).postRelease(l); + Mockito.verify(machineCustomizer, Mockito.times(1)).preRelease(l); + } + + // now test creating users + + protected String getCreateUserStatementsFor(Map<ConfigKey<?>,?> config) { + BailOutJcloudsLocation jl = BailOutJcloudsLocation.newBailOutJcloudsLocation( + managementContext, MutableMap.<ConfigKey<?>, Object>builder() + .put(JcloudsLocationConfig.LOGIN_USER, "root").put(JcloudsLocationConfig.LOGIN_USER_PASSWORD, "m0ck") + .put(JcloudsLocationConfig.USER, "bob").put(JcloudsLocationConfig.LOGIN_USER_PASSWORD, "b0b") + .putAll(config).build()); + + UserCreation creation = jl.createUserStatements(null, jl.config().getBag()); + return new StatementList(creation.statements).render(OsFamily.UNIX); + } + + @Test + public void testDisablesRoot() { + String statements = getCreateUserStatementsFor(ImmutableMap.<ConfigKey<?>, Object>of()); + Assert.assertTrue(statements.contains("PermitRootLogin"), "Error:\n"+statements); + Assert.assertTrue(statements.matches("(?s).*sudoers.*useradd.*bob.*wheel.*"), "Error:\n"+statements); + } + + @Test + public void testDisableRootFalse() { + String statements = getCreateUserStatementsFor(ImmutableMap.<ConfigKey<?>, Object>of( + JcloudsLocationConfig.DISABLE_ROOT_AND_PASSWORD_SSH, false)); + Assert.assertFalse(statements.contains("PermitRootLogin"), "Error:\n"+statements); + Assert.assertTrue(statements.matches("(?s).*sudoers.*useradd.*bob.*wheel.*"), "Error:\n"+statements); + } + + @Test + public void testDisableRootAndSudoFalse() { + String statements = getCreateUserStatementsFor(ImmutableMap.<ConfigKey<?>, Object>of( + JcloudsLocationConfig.DISABLE_ROOT_AND_PASSWORD_SSH, false, + JcloudsLocationConfig.GRANT_USER_SUDO, false)); + Assert.assertFalse(statements.contains("PermitRootLogin"), "Error:\n"+statements); + Assert.assertFalse(statements.matches("(?s).*sudoers.*useradd.*bob.*wheel.*"), "Error:\n"+statements); + } + + // TODO more tests, where flags come in from resolver, named locations, etc + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLoginLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLoginLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLoginLiveTest.java new file mode 100644 index 0000000..5a17a5a --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLoginLiveTest.java @@ -0,0 +1,407 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.brooklyn.location.jclouds; + +import static org.testng.Assert.assertEquals; + +import java.io.File; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +import org.apache.brooklyn.location.LocationSpec; +import org.apache.brooklyn.location.basic.SshMachineLocation; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.os.Os; +import brooklyn.util.stream.Streams; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +/** + * Tests different login options for ssh keys, passwords, etc. + */ +public class JcloudsLoginLiveTest extends AbstractJcloudsLiveTest { + + private static final Logger LOG = LoggerFactory.getLogger(JcloudsLoginLiveTest.class); + + public static final String AWS_EC2_REGION_NAME = AWS_EC2_USEAST_REGION_NAME; + public static final String AWS_EC2_LOCATION_SPEC = "jclouds:" + AWS_EC2_PROVIDER + (AWS_EC2_REGION_NAME == null ? "" : ":" + AWS_EC2_REGION_NAME); + + // Image: {id=us-east-1/ami-7d7bfc14, providerId=ami-7d7bfc14, name=RightImage_CentOS_6.3_x64_v5.8.8.5, location={scope=REGION, id=us-east-1, description=us-east-1, parent=aws-ec2, iso3166Codes=[US-VA]}, os={family=centos, arch=paravirtual, version=6.0, description=rightscale-us-east/RightImage_CentOS_6.3_x64_v5.8.8.5.manifest.xml, is64Bit=true}, description=rightscale-us-east/RightImage_CentOS_6.3_x64_v5.8.8.5.manifest.xml, version=5.8.8.5, status=AVAILABLE[available], loginUser=root, userMetadata={owner=411009282317, rootDeviceType=instance-store, virtualizationType=paravirtual, hypervisor=xen}} + public static final String AWS_EC2_CENTOS_IMAGE_ID = "us-east-1/ami-7d7bfc14"; + + // Image: {id=us-east-1/ami-d0f89fb9, providerId=ami-d0f89fb9, name=ubuntu/images/ebs/ubuntu-precise-12.04-amd64-server-20130411.1, location={scope=REGION, id=us-east-1, description=us-east-1, parent=aws-ec2, iso3166Codes=[US-VA]}, os={family=ubuntu, arch=paravirtual, version=12.04, description=099720109477/ubuntu/images/ebs/ubuntu-precise-12.04-amd64-server-20130411.1, is64Bit=true}, description=099720109477/ubuntu/images/ebs/ubuntu-precise-12.04-amd64-server-20130411.1, version=20130411.1, status=AVAILABLE[available], loginUser=ubuntu, userMetadata={owner=099720109477, rootDeviceType=ebs, virtualizationType=paravirtual, hypervisor=xen}} + public static final String AWS_EC2_UBUNTU_IMAGE_ID = "us-east-1/ami-d0f89fb9"; + + // Image: {id=us-east-1/ami-5e008437, providerId=ami-5e008437, name=RightImage_Ubuntu_10.04_x64_v5.8.8.3, location={scope=REGION, id=us-east-1, description=us-east-1, parent=aws-ec2, iso3166Codes=[US-VA]}, os={family=ubuntu, arch=paravirtual, version=10.04, description=rightscale-us-east/RightImage_Ubuntu_10.04_x64_v5.8.8.3.manifest.xml, is64Bit=true}, description=rightscale-us-east/RightImage_Ubuntu_10.04_x64_v5.8.8.3.manifest.xml, version=5.8.8.3, status=AVAILABLE[available], loginUser=root, userMetadata={owner=411009282317, rootDeviceType=instance-store, virtualizationType=paravirtual, hypervisor=xen}} + // Uses "root" as loginUser + public static final String AWS_EC2_UBUNTU_10_IMAGE_ID = "us-east-1/ami-5e008437"; + + public static final String RACKSPACE_LOCATION_SPEC = "jclouds:" + RACKSPACE_PROVIDER; + + // Image: {id=LON/c52a0ca6-c1f2-4cd1-b7d6-afbcd1ebda22, providerId=c52a0ca6-c1f2-4cd1-b7d6-afbcd1ebda22, name=CentOS 6.0, location={scope=ZONE, id=LON, description=LON, parent=rackspace-cloudservers-uk, iso3166Codes=[GB-SLG]}, os={family=centos, name=CentOS 6.0, version=6.0, description=CentOS 6.0, is64Bit=true}, description=CentOS 6.0, status=AVAILABLE, loginUser=root, userMetadata={os_distro=centos, com.rackspace__1__visible_core=1, com.rackspace__1__build_rackconnect=1, com.rackspace__1__options=0, image_type=base, cache_in_nova=True, com.rackspace__1__source=kickstart, org.openstack__1__os_distro=org.centos, com.rackspace__1__release_build_date=2013-07-25_18-56-29, auto_disk_config=True, com.rackspace__1__release_version=5, os_type=linux, com.rackspace__1__visible_rackconnect=1, com.rackspace__1__release_id=210, com.rackspace__1__visible_managed=0, com.rackspace__1__build_core=1, org.openstack__1__os_version=6.0, org.openstack__1__architecture=x64, com.rackspace__1__build_ma naged=0}} + public static final String RACKSPACE_CENTOS_IMAGE_NAME_REGEX = "CentOS 6.0"; + + // Image: {id=LON/29fe3e2b-f119-4715-927b-763e99ebe23e, providerId=29fe3e2b-f119-4715-927b-763e99ebe23e, name=Debian 6.06 (Squeeze), location={scope=ZONE, id=LON, description=LON, parent=rackspace-cloudservers-uk, iso3166Codes=[GB-SLG]}, os={family=debian, name=Debian 6.06 (Squeeze), version=6.0, description=Debian 6.06 (Squeeze), is64Bit=true}, description=Debian 6.06 (Squeeze), status=AVAILABLE, loginUser=root, userMetadata={os_distro=debian, com.rackspace__1__visible_core=1, com.rackspace__1__build_rackconnect=1, com.rackspace__1__options=0, image_type=base, cache_in_nova=True, com.rackspace__1__source=kickstart, org.openstack__1__os_distro=org.debian, com.rackspace__1__release_build_date=2013-08-06_13-05-36, auto_disk_config=True, com.rackspace__1__release_version=4, os_type=linux, com.rackspace__1__visible_rackconnect=1, com.rackspace__1__release_id=300, com.rackspace__1__visible_managed=0, com.rackspace__1__build_core=1, org.openstack__1__os_version=6.06, org.openstack__1_ _architecture=x64, com.rackspace__1__build_managed=0}} + public static final String RACKSPACE_DEBIAN_IMAGE_NAME_REGEX = "Debian 6"; + + protected JcloudsSshMachineLocation machine; + + private File privateRsaFile = new File(Os.tidyPath("~/.ssh/id_rsa")); + private File privateDsaFile = new File(Os.tidyPath("~/.ssh/id_dsa")); + private File privateRsaFileTmp = new File(privateRsaFile.getAbsoluteFile()+".tmp"); + private File privateDsaFileTmp = new File(privateDsaFile.getAbsoluteFile()+".tmp"); + private File publicRsaFile = new File(Os.tidyPath("~/.ssh/id_rsa.pub")); + private File publicDsaFile = new File(Os.tidyPath("~/.ssh/id_dsa.pub")); + private File publicRsaFileTmp = new File(publicRsaFile.getAbsoluteFile()+".tmp"); + private File publicDsaFileTmp = new File(publicDsaFile.getAbsoluteFile()+".tmp"); + private boolean privateRsaFileMoved; + private boolean privateDsaFileMoved; + private boolean publicRsaFileMoved; + private boolean publicDsaFileMoved; + + @Test(groups = {"Live"}) + protected void testAwsEc2SpecifyingJustPrivateSshKeyInDeprecatedForm() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.LEGACY_PRIVATE_KEY_FILE.getName(), "~/.ssh/id_rsa"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC); + + machine = createEc2Machine(ImmutableMap.<String,Object>of()); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + } + + @Test(groups = {"Live"}) + protected void testAwsEc2SpecifyingPrivateAndPublicSshKeyInDeprecatedForm() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.LEGACY_PRIVATE_KEY_FILE.getName(), "~/.ssh/id_rsa"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.LEGACY_PUBLIC_KEY_FILE.getName(), "~/.ssh/id_rsa.pub"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC); + + machine = createEc2Machine(ImmutableMap.<String,Object>of()); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + } + + // Uses default key files + @Test(groups = {"Live"}) + protected void testAwsEc2SpecifyingNoKeyFiles() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC); + + machine = createEc2Machine(ImmutableMap.<String,Object>of()); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + } + + @Test(groups = {"Live"}) + public void testSpecifyingPasswordAndNoDefaultKeyFilesExist() throws Exception { + try { + moveSshKeyFiles(); + + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PASSWORD.getName(), "mypassword"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(RACKSPACE_LOCATION_SPEC); + + machine = createRackspaceMachine(ImmutableMap.of("imageNameRegex", RACKSPACE_DEBIAN_IMAGE_NAME_REGEX)); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PASSWORD, "mypassword") + .build()); + } finally { + restoreSshKeyFiles(); + } + } + + // Generates and uses a random password + @Test(groups = {"Live"}) + protected void testSpecifyingNothingAndNoDefaultKeyFilesExist() throws Exception { + try { + moveSshKeyFiles(); + + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(RACKSPACE_LOCATION_SPEC); + + machine = createRackspaceMachine(ImmutableMap.of("imageNameRegex", RACKSPACE_DEBIAN_IMAGE_NAME_REGEX)); + assertSshable(machine); + assertEquals(machine.getUser(), "myname"); + } finally { + restoreSshKeyFiles(); + } + } + + @Test(groups = {"Live"}) + protected void testSpecifyingPasswordAndSshKeysPrefersKeys() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PRIVATE_KEY_FILE.getName(), "~/.ssh/id_rsa"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PUBLIC_KEY_FILE.getName(), "~/.ssh/id_rsa.pub"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PASSWORD.getName(), "mypassword"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(RACKSPACE_LOCATION_SPEC); + + machine = createRackspaceMachine(ImmutableMap.of("imageNameRegex", RACKSPACE_DEBIAN_IMAGE_NAME_REGEX)); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PASSWORD, "mypassword") + .build()); + } + + @Test(groups = {"Live"}) + protected void testSpecifyingPasswordIgnoresDefaultSshKeys() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PASSWORD.getName(), "mypassword"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(RACKSPACE_LOCATION_SPEC); + + machine = createRackspaceMachine(ImmutableMap.of("imageNameRegex", RACKSPACE_DEBIAN_IMAGE_NAME_REGEX)); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PASSWORD, "mypassword") + .build()); + + assertNotSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + } + + @Test(groups = {"Live"}) + protected void testSpecifyingPasswordWithPublicKeyAllowsKeyAccess() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PASSWORD.getName(), "mypassword"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PUBLIC_KEY_FILE.getName(), "~/.ssh/id_rsa.pub"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(RACKSPACE_LOCATION_SPEC); + + machine = createRackspaceMachine(ImmutableMap.of("imageNameRegex", RACKSPACE_DEBIAN_IMAGE_NAME_REGEX)); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "myname") + .put(SshMachineLocation.PASSWORD, "mypassword") + .build()); + } + + // user "root" matches the loginUser=root + @Test(groups = {"Live"}) + protected void testSpecifyingPasswordWhenNoDefaultKeyFilesExistWithRootUser() throws Exception { + try { + moveSshKeyFiles(); + + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "root"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PASSWORD.getName(), "mypassword"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(RACKSPACE_LOCATION_SPEC); + + machine = createRackspaceMachine(ImmutableMap.of("imageNameRegex", RACKSPACE_DEBIAN_IMAGE_NAME_REGEX)); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "root") + .put(SshMachineLocation.PASSWORD, "mypassword") + .build()); + } finally { + restoreSshKeyFiles(); + } + } + + @Test(groups = {"Live"}) + protected void testAwsEc2SpecifyingRootUser() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "root"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PRIVATE_KEY_FILE.getName(), "~/.ssh/id_rsa"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PUBLIC_KEY_FILE.getName(), "~/.ssh/id_rsa.pub"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC); + + machine = createEc2Machine(ImmutableMap.<String,Object>of("imageId", AWS_EC2_UBUNTU_10_IMAGE_ID)); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "root") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + } + + @Test(groups = {"Live"}) + protected void testAwsEc2WhenBlankUserSoUsesRootLoginUser() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), ""); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PRIVATE_KEY_FILE.getName(), "~/.ssh/id_rsa"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PUBLIC_KEY_FILE.getName(), "~/.ssh/id_rsa.pub"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC); + + machine = createEc2Machine(ImmutableMap.<String,Object>of("imageId", AWS_EC2_UBUNTU_10_IMAGE_ID)); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "root") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + } + + // In JcloudsLocation.NON_ADDABLE_USERS, "ec2-user" was treated special and was not added! + // That was very bad for if someone is running brooklyn on a new AWS VM, and just installs brooklyn+runs as the default ec2-user. + @Test(groups = {"Live"}) + protected void testAwsEc2SpecifyingSpecialUser() throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "ec2-user"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PRIVATE_KEY_FILE.getName(), "~/.ssh/id_rsa"); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.PUBLIC_KEY_FILE.getName(), "~/.ssh/id_rsa.pub"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC); + + machine = createEc2Machine(ImmutableMap.<String,Object>of("imageId", AWS_EC2_UBUNTU_10_IMAGE_ID)); + assertSshable(machine); + + assertSshable(ImmutableMap.builder() + .put("address", machine.getAddress()) + .put("user", "ec2-user") + .put(SshMachineLocation.PRIVATE_KEY_FILE, Os.tidyPath("~/.ssh/id_rsa")) + .build()); + } + + @Override + protected void releaseMachine(JcloudsSshMachineLocation machine) { + jcloudsLocation.release(machine); + } + + private JcloudsSshMachineLocation createEc2Machine(Map<String,? extends Object> conf) throws Exception { + return obtainMachine(MutableMap.<String,Object>builder() + .putAll(conf) + .putIfAbsent("imageId", AWS_EC2_CENTOS_IMAGE_ID) + .putIfAbsent("hardwareId", AWS_EC2_SMALL_HARDWARE_ID) + .putIfAbsent("inboundPorts", ImmutableList.of(22)) + .build()); + } + + private JcloudsSshMachineLocation createRackspaceMachine(Map<String,? extends Object> conf) throws Exception { + return obtainMachine(MutableMap.<String,Object>builder() + .putAll(conf) + .putIfAbsent("inboundPorts", ImmutableList.of(22)) + .build()); + } + + protected void assertSshable(Map<?,?> machineConfig) { + SshMachineLocation machineWithThatConfig = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class) + .configure(machineConfig)); + try { + assertSshable(machineWithThatConfig); + } finally { + Streams.closeQuietly(machineWithThatConfig); + } + } + + protected void assertNotSshable(Map<?,?> machineConfig) { + try { + assertSshable(machineConfig); + Assert.fail("ssh should not have succeeded "+machineConfig); + } catch (Exception e) { + // expected + LOG.debug("Exception as expected when testing sshable "+machineConfig); + } + } + + private void moveSshKeyFiles() throws Exception { + privateRsaFileMoved = false; + privateDsaFileMoved = false; + publicRsaFileMoved = false; + publicDsaFileMoved = false; + + if (privateRsaFile.exists()) { + LOG.info("Moving {} to {}", privateRsaFile, privateRsaFileTmp); + Runtime.getRuntime().exec("mv "+privateRsaFile.getAbsolutePath()+" "+privateRsaFileTmp.getAbsolutePath()); + privateRsaFileMoved = true; + } + if (privateDsaFile.exists()) { + LOG.info("Moving {} to {}", privateDsaFile, privateDsaFileTmp); + Runtime.getRuntime().exec("mv "+privateDsaFile.getAbsolutePath()+" "+privateDsaFileTmp.getAbsolutePath()); + privateDsaFileMoved = true; + } + if (publicRsaFile.exists()) { + LOG.info("Moving {} to {}", publicRsaFile, publicRsaFileTmp); + Runtime.getRuntime().exec("mv "+publicRsaFile.getAbsolutePath()+" "+publicRsaFileTmp.getAbsolutePath()); + publicRsaFileMoved = true; + } + if (publicDsaFile.exists()) { + LOG.info("Moving {} to {}", publicDsaFile, publicDsaFileTmp); + Runtime.getRuntime().exec("mv "+publicDsaFile.getAbsolutePath()+" "+publicDsaFileTmp.getAbsolutePath()); + publicDsaFileMoved = true; + } + } + + private void restoreSshKeyFiles() throws Exception { + if (privateRsaFileMoved) { + LOG.info("Restoring {} form {}", privateRsaFile, privateRsaFileTmp); + Runtime.getRuntime().exec("mv "+privateRsaFileTmp.getAbsolutePath()+" "+privateRsaFile.getAbsolutePath()); + privateRsaFileMoved = false; + } + if (privateDsaFileMoved) { + LOG.info("Restoring {} form {}", privateDsaFile, privateDsaFileTmp); + Runtime.getRuntime().exec("mv "+privateDsaFileTmp.getAbsolutePath()+" "+privateDsaFile.getAbsolutePath()); + privateDsaFileMoved = false; + } + if (publicRsaFileMoved) { + LOG.info("Restoring {} form {}", publicRsaFile, publicRsaFileTmp); + Runtime.getRuntime().exec("mv "+publicRsaFileTmp.getAbsolutePath()+" "+publicRsaFile.getAbsolutePath()); + publicRsaFileMoved = false; + } + if (publicDsaFileMoved) { + LOG.info("Restoring {} form {}", publicDsaFile, publicDsaFileTmp); + Runtime.getRuntime().exec("mv "+publicDsaFileTmp.getAbsolutePath()+" "+publicDsaFile.getAbsolutePath()); + publicDsaFileMoved = false; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java new file mode 100644 index 0000000..5e25a8c --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.brooklyn.location.jclouds; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +import brooklyn.util.config.ConfigBag; +import brooklyn.util.text.Strings; + +public class JcloudsMachineNamerTest { + + private static final Logger log = LoggerFactory.getLogger(JcloudsMachineNamerTest.class); + + @Test + public void testGenerateGroupIdInVcloud() { + ConfigBag cfg = new ConfigBag() + .configure(JcloudsLocationConfig.CLOUD_PROVIDER, "vcloud") + .configure(JcloudsLocationConfig.CALLER_CONTEXT, "!mycontext!"); + + String result = new JcloudsMachineNamer().generateNewGroupId(cfg); + + log.info("test mycontext vcloud group id gives: "+result); + // brooklyn-user-!mycontext!-1234 + // br-<code>-<user>-myco-1234 + Assert.assertTrue(result.length() <= 24-4-1, "result: "+result); + + String user = Strings.maxlen(System.getProperty("user.name"), 2).toLowerCase(); + // (length 2 will happen if user is brooklyn, to avoid brooklyn-brooklyn at start!) + Assert.assertTrue(result.indexOf(user) >= 0); + Assert.assertTrue(result.indexOf("-myc") >= 0); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynPropertiesTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynPropertiesTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynPropertiesTest.java new file mode 100644 index 0000000..69eda84 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynPropertiesTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.brooklyn.location.jclouds; + +import java.util.Map; + +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.Maps; + +public class JcloudsPropertiesFromBrooklynPropertiesTest { + + protected static Map<String, Object> sampleProviderOrApiProps() { + Map<String, Object> map = Maps.newHashMap(); + map.put("brooklyn.location.jclouds.FooServers.identity", "bob"); + map.put("brooklyn.location.jclouds.FooServers.credential", "s3cr3t"); + map.put("brooklyn.location.jclouds.FooServers.jclouds.ssh.max-retries", "100"); + return map; + } + + protected static Map<String, Object> sampleNamedProps() { + Map<String, Object> map = Maps.newHashMap(); + map.put("brooklyn.location.named.cloudfirst", "jclouds:openstack-nova"); + map.put("brooklyn.location.named.cloudfirst.identity", "myId"); + map.put("brooklyn.location.named.cloudfirst.credential", "password"); + map.put("brooklyn.location.named.cloudfirst.imageId", "RegionOne/1"); + map.put("brooklyn.location.named.cloudfirst.securityGroups", "universal"); + return map; + } + + protected static Map<String, Object> unsupportedSampleProviderOrApiProps() { + Map<String, Object> map = Maps.newHashMap(); + map.put("brooklyn.location.jclouds.FooServers.image-id", "invalid-image-id"); + return map; + } + + protected static Map<String, Object> unsupportedNamedProps() { + Map<String, Object> map = Maps.newHashMap(); + map.put("brooklyn.location.named.cloudfirst", "jclouds:openstack-nova"); + map.put("brooklyn.location.named.cloudfirst.hardware-id", "invalid-hardware-id"); + return map; + } + + private JcloudsPropertiesFromBrooklynProperties parser; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + parser = new JcloudsPropertiesFromBrooklynProperties(); + } + + @Test + public void testProviderOrApiProperties() { + Map<String, Object> map = parser.getJcloudsProperties("FooServers", null, null, sampleProviderOrApiProps()); + Assert.assertEquals(map.get("identity"), "bob"); + Assert.assertEquals(map.get("credential"), "s3cr3t"); + Assert.assertEquals(map.get("provider"), "FooServers"); + } + + @Test + public void testNamedProperties() { + Map<String, Object> map = parser.getJcloudsProperties("openstack-nova", null, "cloudfirst", sampleNamedProps()); + Assert.assertEquals(map.get("provider"), "openstack-nova"); + Assert.assertEquals(map.get("identity"), "myId"); + Assert.assertEquals(map.get("credential"), "password"); + Assert.assertEquals(map.get("imageId"), "RegionOne/1"); + Assert.assertEquals(map.get("securityGroups"), "universal"); + } + + @Test + public void testOrderOfPreference() { + Map<String, Object> allProperties = Maps.newHashMap(); + allProperties.putAll(sampleProviderOrApiProps()); + allProperties.putAll(sampleNamedProps()); + Map<String, Object> map = parser.getJcloudsProperties("openstack-nova", null, "cloudfirst", allProperties); + Assert.assertEquals(map.get("provider"), "openstack-nova"); + Assert.assertEquals(map.get("identity"), "myId"); + Assert.assertEquals(map.get("credential"), "password"); + Assert.assertEquals(map.get("imageId"), "RegionOne/1"); + Assert.assertEquals(map.get("securityGroups"), "universal"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsSshingLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsSshingLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsSshingLiveTest.java new file mode 100644 index 0000000..48ce9cf --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsSshingLiveTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.brooklyn.location.jclouds; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import brooklyn.util.collections.MutableMap; + +import com.google.common.collect.ImmutableList; + +/** + * Tests the initial ssh command execution (e.g. user creation), using jclouds TemplateOptions + * and using just brooklyn. + */ +public class JcloudsSshingLiveTest extends AbstractJcloudsLiveTest { + + public static final String SOFTLAYER_REGION_NAME = SOFTLAYER_AMS01_REGION_NAME; + public static final String SOTLAYER_LOCATION_SPEC = "jclouds:" + SOFTLAYER_PROVIDER + (SOFTLAYER_REGION_NAME == null ? "" : ":" + SOFTLAYER_REGION_NAME); + + protected JcloudsSshMachineLocation machine; + + @Test(groups = {"Live"}) + public void testCreatesUserUsingJcloudsTemplateOptions() throws Exception { + runCreatesUser(true); + } + + @Test(groups = {"Live"}) + public void testCreatesUserWithoutUsingJcloudsTemplateOptions() throws Exception { + runCreatesUser(false); + } + + protected void runCreatesUser(boolean useJcloudsSshInit) throws Exception { + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USE_JCLOUDS_SSH_INIT.getName(), Boolean.toString(useJcloudsSshInit)); + brooklynProperties.put(BROOKLYN_PROPERTIES_PREFIX+JcloudsLocationConfig.USER.getName(), "myname"); + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(SOTLAYER_LOCATION_SPEC); + + JcloudsSshMachineLocation machine = obtainMachine(MutableMap.<String,Object>builder() + .putIfAbsent("inboundPorts", ImmutableList.of(22)) + .build()); + assertSshable(machine); + assertEquals(machine.getUser(), "myname"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/LiveTestEntity.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/LiveTestEntity.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/LiveTestEntity.java new file mode 100644 index 0000000..0ff4474 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/LiveTestEntity.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.brooklyn.location.jclouds; + +import java.util.Collection; + +import org.apache.brooklyn.api.entity.proxying.ImplementedBy; +import org.apache.brooklyn.location.Location; +import org.apache.brooklyn.location.NoMachinesAvailableException; +import org.apache.brooklyn.test.entity.TestEntity; +import org.apache.brooklyn.test.entity.TestEntityImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.basic.Lifecycle; +import org.apache.brooklyn.location.MachineProvisioningLocation; +import org.apache.brooklyn.location.basic.LocationInternal; + +import com.google.common.base.Predicates; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; + +@ImplementedBy(LiveTestEntity.LiveTestEntityImpl.class) +public interface LiveTestEntity extends TestEntity { + + MachineProvisioningLocation<?> getProvisioningLocation(); + JcloudsSshMachineLocation getObtainedLocation(); + + public static class LiveTestEntityImpl extends TestEntityImpl implements LiveTestEntity { + + private static final Logger LOG = LoggerFactory.getLogger(LiveTestEntityImpl.class); + private JcloudsLocation provisioningLocation; + private JcloudsSshMachineLocation obtainedLocation; + + @Override + public void start(final Collection<? extends Location> locs) { + LOG.trace("Starting {}", this); + callHistory.add("start"); + setAttribute(SERVICE_STATE, Lifecycle.STARTING); + counter.incrementAndGet(); + addLocations(locs); + provisioningLocation = (JcloudsLocation) Iterables.find(locs, Predicates.instanceOf(JcloudsLocation.class)); + try { + obtainedLocation = (JcloudsSshMachineLocation)provisioningLocation.obtain(((LocationInternal)provisioningLocation).config().getBag().getAllConfig()); + } catch (NoMachinesAvailableException e) { + throw Throwables.propagate(e); + } + addLocations(ImmutableList.of(obtainedLocation)); + setAttribute(SERVICE_STATE, Lifecycle.RUNNING); + } + + @Override + public void stop() { + LOG.trace("Stopping {}", this); + callHistory.add("stop"); + setAttribute(SERVICE_STATE, Lifecycle.STOPPING); + counter.decrementAndGet(); + if (provisioningLocation != null && obtainedLocation != null) { + provisioningLocation.release(obtainedLocation); + } + setAttribute(SERVICE_STATE, Lifecycle.STOPPED); + } + + public MachineProvisioningLocation<?> getProvisioningLocation() { + return provisioningLocation; + } + + public JcloudsSshMachineLocation getObtainedLocation() { + return obtainedLocation; + } + } + +}
