http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java new file mode 100644 index 0000000..535b6a8 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java @@ -0,0 +1,150 @@ +/* + * 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.assertNull; +import static org.testng.Assert.assertTrue; + +import java.io.File; + +import org.apache.brooklyn.api.entity.proxying.EntitySpec; +import org.apache.brooklyn.test.entity.TestApplication; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.rebind.RebindTestUtils; +import org.apache.brooklyn.location.OsDetails; +import brooklyn.management.internal.LocalManagementContext; +import brooklyn.util.config.ConfigBag; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.io.Files; + +public class RebindJcloudsLocationLiveTest extends AbstractJcloudsLiveTest { + + 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; + + private ClassLoader classLoader = getClass().getClassLoader(); + private TestApplication origApp; + private LiveTestEntity origEntity; + private File mementoDir; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + origApp = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class), managementContext); + origEntity = origApp.createAndManageChild(EntitySpec.create(LiveTestEntity.class)); + + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC); + jcloudsLocation.setConfig(JcloudsLocation.HARDWARE_ID, AWS_EC2_SMALL_HARDWARE_ID); + } + + @AfterMethod(alwaysRun = true) + @Override + public void tearDown() throws Exception { + super.tearDown(); + if (origApp != null) Entities.destroyAll(origApp.getManagementContext()); + if (mementoDir != null) RebindTestUtils.deleteMementoDir(mementoDir); + } + + @Override + protected LocalManagementContext newManagementContext() { + mementoDir = Files.createTempDir(); + return RebindTestUtils.newPersistingManagementContext(mementoDir, classLoader, 1); + } + + @Test(groups="Live") + public void testRebindsToJcloudsMachine() throws Exception { + origApp.start(ImmutableList.of(jcloudsLocation)); + JcloudsLocation origJcloudsLocation = jcloudsLocation; + System.out.println("orig locations: " + origEntity.getLocations()); + JcloudsSshMachineLocation origMachine = (JcloudsSshMachineLocation) Iterables.find(origEntity.getLocations(), Predicates.instanceOf(JcloudsSshMachineLocation.class)); + + TestApplication newApp = rebind(); + LiveTestEntity newEntity = (LiveTestEntity) Iterables.find(newApp.getChildren(), Predicates.instanceOf(LiveTestEntity.class)); + JcloudsSshMachineLocation newMachine = (JcloudsSshMachineLocation) Iterables.find(newEntity.getLocations(), Predicates.instanceOf(JcloudsSshMachineLocation.class)); + + assertMachineEquals(newMachine, origMachine); + assertTrue(newMachine.isSshable()); + + JcloudsLocation newJcloudsLoction = newMachine.getParent(); + assertJcloudsLocationEquals(newJcloudsLoction, origJcloudsLocation); + } + + private void assertMachineEquals(JcloudsSshMachineLocation actual, JcloudsSshMachineLocation expected) { + String errmsg = "actual="+actual.toVerboseString()+"; expected="+expected.toVerboseString(); + assertEquals(actual.getId(), expected.getId(), errmsg); + assertEquals(actual.getJcloudsId(), expected.getJcloudsId(), errmsg); + assertOsDetailEquals(actual.getOsDetails(), expected.getOsDetails()); + assertEquals(actual.getSshHostAndPort(), expected.getSshHostAndPort()); + assertEquals(actual.getPrivateAddress(), expected.getPrivateAddress()); + assertConfigBagEquals(actual.config().getBag(), expected.config().getBag(), errmsg); + } + + private void assertOsDetailEquals(OsDetails actual, OsDetails expected) { + String errmsg = "actual="+actual+"; expected="+expected; + if (actual == null) assertNull(expected, errmsg); + assertEquals(actual.isWindows(), expected.isWindows()); + assertEquals(actual.isLinux(), expected.isLinux()); + assertEquals(actual.isMac(), expected.isMac()); + assertEquals(actual.getName(), expected.getName()); + assertEquals(actual.getArch(), expected.getArch()); + assertEquals(actual.getVersion(), expected.getVersion()); + assertEquals(actual.is64bit(), expected.is64bit()); + } + + private void assertJcloudsLocationEquals(JcloudsLocation actual, JcloudsLocation expected) { + String errmsg = "actual="+actual.toVerboseString()+"; expected="+expected.toVerboseString(); + assertEquals(actual.getId(), expected.getId(), errmsg); + assertEquals(actual.getProvider(), expected.getProvider(), errmsg); + assertEquals(actual.getRegion(), expected.getRegion(), errmsg); + assertEquals(actual.getIdentity(), expected.getIdentity(), errmsg); + assertEquals(actual.getCredential(), expected.getCredential(), errmsg); + assertEquals(actual.getHostGeoInfo(), expected.getHostGeoInfo(), errmsg); + assertConfigBagEquals(actual.config().getBag(), expected.config().getBag(), errmsg); + } + + private void assertConfigBagEquals(ConfigBag actual, ConfigBag expected, String errmsg) { + // TODO revisit the strong assertion that configBags are equal + +// // TODO Can we include all of these things (e.g. when locations are entities, so flagged fields not treated special)? +// List<String> configToIgnore = ImmutableList.of("id", "template", "usedPorts", "machineCreationSemaphore", "config"); +// MutableMap<Object, Object> actualMap = MutableMap.builder().putAll(actual.getAllConfig()) +// .removeAll(configToIgnore) +// .build(); +// MutableMap<Object, Object> expectedMap = MutableMap.builder().putAll(expected.getAllConfig()) +// .removeAll(configToIgnore) +// .build(); +// +// assertEquals(actualMap, expectedMap, errmsg+"; actualBag="+actualMap+"; expectedBag="+expectedMap); + } + + private TestApplication rebind() throws Exception { + RebindTestUtils.waitForPersisted(origApp); + return (TestApplication) RebindTestUtils.rebind(mementoDir, getClass().getClassLoader()); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java new file mode 100644 index 0000000..cc52ef2 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java @@ -0,0 +1,66 @@ +/* + * 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.assertNotEquals; +import static org.testng.Assert.assertNull; + +import org.jclouds.domain.LoginCredentials; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.rebind.RebindTestFixtureWithApp; +import org.apache.brooklyn.location.basic.SshMachineLocation; +import brooklyn.util.config.ConfigBag; + +import com.google.common.net.HostAndPort; + +public class RebindJcloudsLocationTest extends RebindTestFixtureWithApp { + + public static final String LOC_SPEC = "jclouds:aws-ec2:us-east-1"; + + private JcloudsLocation origLoc; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + origLoc = (JcloudsLocation) origManagementContext.getLocationRegistry().resolve(LOC_SPEC); + } + + // Previously, the rebound config contained "id" which was then passed to createTemporarySshMachineLocation, causing + // that to fail (because the LocationSpec should not have had "id" in its config) + @Test + public void testReboundConfigDoesNotContainId() throws Exception { + rebind(); + + JcloudsLocation newLoc = (JcloudsLocation) newManagementContext.getLocationManager().getLocation(origLoc.getId()); + + ConfigBag newLocConfig = newLoc.config().getBag(); + ConfigBag config = ConfigBag.newInstanceCopying(newLocConfig); + + assertNull(newLocConfig.getStringKey(("id"))); + + SshMachineLocation tempMachine = newLoc.createTemporarySshMachineLocation( + HostAndPort.fromParts("localhost", 1234), + LoginCredentials.builder().identity("myuser").password("mypass").noPrivateKey().build(), + config); + assertNotEquals(tempMachine.getId(), newLoc.getId()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/SimpleJcloudsLocationUserLoginAndConfigLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/SimpleJcloudsLocationUserLoginAndConfigLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/SimpleJcloudsLocationUserLoginAndConfigLiveTest.java new file mode 100644 index 0000000..3e33ce8 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/SimpleJcloudsLocationUserLoginAndConfigLiveTest.java @@ -0,0 +1,249 @@ +/* + * 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.io.ByteArrayOutputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.apache.brooklyn.location.NoMachinesAvailableException; +import org.apache.brooklyn.location.basic.SshMachineLocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.util.collections.MutableMap; +import brooklyn.util.ssh.BashCommands; +import brooklyn.util.text.Identifiers; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +public class SimpleJcloudsLocationUserLoginAndConfigLiveTest extends AbstractJcloudsLiveTest { + + // FIXME And tidy up this one + + private static final String LOCATION_SPEC = AWS_EC2_PROVIDER + ":" + AWS_EC2_USEAST_REGION_NAME; + + private static final Logger log = LoggerFactory.getLogger(SimpleJcloudsLocationUserLoginAndConfigLiveTest.class); + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + jcloudsLocation = resolve(LOCATION_SPEC); + } + + @SuppressWarnings("rawtypes") + @Test(groups="Live") + public void testJcloudsCreateBogStandard() throws Exception { + log.info("TEST testJcloudsCreateBogStandard"); + JcloudsSshMachineLocation m1 = obtainMachine(ImmutableMap.of()); + + Map details = MutableMap.of("id", m1.getJcloudsId(), "hostname", m1.getAddress().getHostAddress(), "user", m1.getUser()); + log.info("got machine "+m1+" at "+jcloudsLocation+": "+details+"; now trying to rebind"); + String result; + // echo conflates spaces of arguments + result = execWithOutput(m1, Arrays.asList("echo trying m1", "hostname", "date")); + Assert.assertTrue(result.contains("trying m1")); + + log.info("now trying rebind "+m1); + JcloudsSshMachineLocation m2 = jcloudsLocation.rebindMachine(details); + result = execWithOutput(m2, Arrays.asList("echo trying m2", "hostname", "date")); + Assert.assertTrue(result.contains("trying m2")); + } + + @SuppressWarnings("rawtypes") + @Test(groups="Live") + public void testJcloudsCreateBogStandardWithUserBrooklyn() throws Exception { + log.info("TEST testJcloudsCreateBogStandardWithUserBrooklyn"); + JcloudsSshMachineLocation m1 = obtainMachine(MutableMap.of("user", "brooklyn")); + + Map details = MutableMap.of("id", m1.getJcloudsId(), "hostname", m1.getAddress().getHostAddress(), "user", m1.getUser()); + log.info("got machine "+m1+" at "+jcloudsLocation+": "+details+"; now trying to rebind"); + String result; + // echo conflates spaces of arguments + result = execWithOutput(m1, Arrays.asList("echo trying m1", "hostname", "date")); + Assert.assertTrue(result.contains("trying m1")); + + log.info("now trying rebind "+m1); + JcloudsSshMachineLocation m2 = jcloudsLocation.rebindMachine(details); + result = execWithOutput(m2, Arrays.asList("echo trying m2", "hostname", "date")); + Assert.assertTrue(result.contains("trying m2")); + + Assert.assertEquals(m2.getUser(), "brooklyn"); + } + + @SuppressWarnings("rawtypes") + @Test(groups="Live") + public void testJcloudsCreateUserMetadata() throws Exception { + log.info("TEST testJcloudsCreateBogStandard"); + String key = "brooklyn-test-user-data"; + String value = "test-"+Identifiers.makeRandomId(4); + JcloudsSshMachineLocation m1 = obtainMachine(MutableMap.of("userMetadata", key+"="+value)); + + Map details = MutableMap.of("id", m1.getJcloudsId(), "hostname", m1.getAddress().getHostAddress(), "user", m1.getUser(), + "userMetadata", key+"="+value); + Assert.assertEquals(m1.node.getUserMetadata().get(key), value); + + log.info("got machine "+m1+" at "+jcloudsLocation+": "+details+"; now trying to rebind"); + String result; + // echo conflates spaces of arguments + result = execWithOutput(m1, Arrays.asList("echo trying m1", "hostname", "date")); + Assert.assertTrue(result.contains("trying m1")); + + log.info("now trying rebind "+m1); + JcloudsSshMachineLocation m2 = jcloudsLocation.rebindMachine(details); + result = execWithOutput(m2, Arrays.asList("echo trying m2", "hostname", "date")); + Assert.assertTrue(result.contains("trying m2")); + Assert.assertEquals(m2.node.getUserMetadata().get(key), value); + } + + // a curious image, centos, but user is ec2-user, and handily not correctly auto-detected + // test we can specify a loginUser different from user, and that user is created etc... + // imageId=us-east-1/ami-f95cf390 + public static final String EC2_CENTOS_IMAGE = "us-east-1/ami-f95cf390"; + + @Test(groups="Live") + public void testJcloudsMissingUser() throws Exception { + log.info("TEST testJcloudsMissingUser"); + try { + // wait up to 30s for login (override default of 5m so test runs faster) + obtainMachine(MutableMap.of("imageId", EC2_CENTOS_IMAGE, "waitForSshable", 30*1000)); + log.info("whoops we logged in"); + } catch (NoMachinesAvailableException e) { + log.info("got error as expected, for missing user: "+e); // success + } + } + + @SuppressWarnings("rawtypes") + @Test(groups="Live") + public void testJcloudsWithSpecificLoginUserAndSameUser() throws Exception { + log.info("TEST testJcloudsWithSpecificLoginUserAndSameUser"); + JcloudsSshMachineLocation m1 = obtainMachine(MutableMap.of( + "imageId", EC2_CENTOS_IMAGE, + "loginUser", "ec2-user", + "user", "ec2-user", + "waitForSshable", 30*1000)); + + Map details = MutableMap.of("id", m1.getJcloudsId(), "hostname", m1.getAddress().getHostAddress(), "user", m1.getUser()); + log.info("got machine "+m1+" at "+jcloudsLocation+": "+details+"; now trying to rebind"); + String result; + // echo conflates spaces of arguments + result = execWithOutput(m1, Arrays.asList("echo trying m1", "hostname", "date")); + Assert.assertTrue(result.contains("trying m1")); + + log.info("now trying rebind "+m1); + JcloudsSshMachineLocation m2 = jcloudsLocation.rebindMachine(details); + result = execWithOutput(m2, Arrays.asList("echo trying m2", "hostname", "date")); + Assert.assertTrue(result.contains("trying m2")); + + Assert.assertEquals(m2.getUser(), "ec2-user"); + } + + @SuppressWarnings("rawtypes") + @Test(groups="Live") + public void testJcloudsWithSpecificLoginUserAndNewUser() throws Exception { + log.info("TEST testJcloudsWithSpecificLoginUserAndNewUser"); + JcloudsSshMachineLocation m1 = obtainMachine(MutableMap.of( + "imageId", EC2_CENTOS_IMAGE, + "loginUser", "ec2-user", + "user", "newbob", + "waitForSshable", 30*1000)); + + Map details = MutableMap.of("id", m1.getJcloudsId(), "hostname", m1.getAddress().getHostAddress(), "user", m1.getUser()); + log.info("got machine "+m1+" at "+jcloudsLocation+": "+details+"; now trying to rebind"); + String result; + // echo conflates spaces of arguments + result = execWithOutput(m1, Arrays.asList("echo trying m1", "hostname", "date")); + Assert.assertTrue(result.contains("trying m1")); + + log.info("now trying rebind "+m1); + JcloudsSshMachineLocation m2 = jcloudsLocation.rebindMachine(details); + result = execWithOutput(m2, Arrays.asList("echo trying m2", "hostname", "date")); + Assert.assertTrue(result.contains("trying m2")); + + Assert.assertEquals(m2.getUser(), "newbob"); + } + + @SuppressWarnings("rawtypes") + @Test(groups="Live") + public void testJcloudsWithSpecificLoginUserAndDefaultUser() throws Exception { + log.info("TEST testJcloudsWithSpecificLoginUserAndDefaultUser"); + JcloudsSshMachineLocation m1 = obtainMachine(MutableMap.of( + "imageId", EC2_CENTOS_IMAGE, + "loginUser", "ec2-user", + "waitForSshable", 30*1000)); + + Map details = MutableMap.of("id", m1.getJcloudsId(), "hostname", m1.getAddress().getHostAddress(), "user", m1.getUser()); + log.info("got machine "+m1+" at "+jcloudsLocation+": "+details+"; now trying to rebind"); + String result; + // echo conflates spaces of arguments + result = execWithOutput(m1, Arrays.asList("echo trying m1", "hostname", "date")); + Assert.assertTrue(result.contains("trying m1")); + + log.info("now trying rebind "+m1); + JcloudsSshMachineLocation m2 = jcloudsLocation.rebindMachine(details); + result = execWithOutput(m2, Arrays.asList("echo trying m2", "hostname", "date")); + Assert.assertTrue(result.contains("trying m2")); + } + + @Test(groups="Live") + public void testJcloudsCreateWithNoSudoGranted() throws Exception { + log.info("TEST testJcloudsCreateWithNoSudoGranted"); + JcloudsSshMachineLocation m = obtainMachine(MutableMap.of( + "grantUserSudo", false, + "waitForSshable", 30*1000)); + + int exitCode = execWithExitCode(m, ImmutableList.of(BashCommands.sudo("echo yes"))); + Assert.assertFalse(exitCode == 0, "exit code for sudo command should not have been 0"); + } + + private String execWithOutput(SshMachineLocation m, List<String> commands) { + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + exec(m, commands, stdout, stderr); + return new String(stdout.toByteArray()); + } + + private int execWithExitCode(SshMachineLocation m, List<String> commands) { + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + return exec(m, commands, stdout, stderr); + } + + private int exec(SshMachineLocation m, List<String> commands, ByteArrayOutputStream stdout, ByteArrayOutputStream stderr) { + Map<String, Object> flags = Maps.newLinkedHashMap(); + flags.put("out", stdout); + flags.put("err", stderr); + int exitCode = m.execCommands(flags, "test", commands); + log.info("stdout from "+commands+":\n"+new String(stdout.toByteArray())); + log.info("stderr from "+commands+":\n"+new String(stderr.toByteArray())); + log.info("exit code: " + exitCode); + return exitCode; + } + + 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/SingleMachineProvisioningLocationJcloudsLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/SingleMachineProvisioningLocationJcloudsLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/SingleMachineProvisioningLocationJcloudsLiveTest.java new file mode 100644 index 0000000..e9b9dd8 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/SingleMachineProvisioningLocationJcloudsLiveTest.java @@ -0,0 +1,124 @@ +/* + * 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.assertFalse; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import brooklyn.entity.basic.ConfigKeys; +import org.apache.brooklyn.location.MachineLocation; +import org.apache.brooklyn.location.basic.SingleMachineProvisioningLocation; + +public class SingleMachineProvisioningLocationJcloudsLiveTest extends AbstractJcloudsLiveTest { +private static final Logger log = LoggerFactory.getLogger(SingleMachineProvisioningLocation.class); + + private SingleMachineProvisioningLocation<JcloudsSshMachineLocation> location; + + private static final String JCLOUDS_LOCATION_SPEC = "jclouds:" + AWS_EC2_PROVIDER + ":" + AWS_EC2_USEAST_REGION_NAME; + + @Test(groups="Live") + public void testJcloudsSingle() throws Exception { + location = resolve("single:(target='"+JCLOUDS_LOCATION_SPEC+"')"); + + MachineLocation m1 = obtainMachine(); + assertNotNull(m1); + + log.info("GOT "+m1); + } + + @Test(groups="Live") + public void testJcloudsSingleRelease() throws Exception { + location = resolve("single:(target='"+JCLOUDS_LOCATION_SPEC+"')"); + + JcloudsSshMachineLocation m1 = obtainMachine(); + log.info("GOT " + m1); + JcloudsSshMachineLocation m2 = obtainMachine(); + log.info("GOT " + m2); + assertSame(m1, m2); + + location.release(m1); + assertTrue(m2.isSshable()); + + location.release(m2); + assertFalse(m2.isSshable()); + } + + @Test(groups="Live") + public void testJcloudsSingleObtainReleaseObtain() throws Exception { + location = resolve("single:(target='"+JCLOUDS_LOCATION_SPEC+"')"); + + JcloudsSshMachineLocation m1 = obtainMachine(); + log.info("GOT " + m1); + + location.release(m1); + assertFalse(m1.isSshable()); + + JcloudsSshMachineLocation m2 = obtainMachine(); + assertTrue(m2.isSshable()); + assertNotEquals(m1, m2); + + location.release(m2); + assertFalse(m2.isSshable()); + } + + @Test(groups="Live") + public void testJCloudsNamedSingle() throws Exception { + brooklynProperties.put(ConfigKeys.newStringConfigKey("brooklyn.location.named.FooServers"), JCLOUDS_LOCATION_SPEC); + location = resolve("single:(target='named:FooServers')"); + + JcloudsSshMachineLocation m1 = obtainMachine(); + assertTrue(m1.isSshable()); + + location.release(m1); + assertFalse(m1.isSshable()); + } + + @Override + protected JcloudsSshMachineLocation obtainMachine(Map<?, ?> conf) throws Exception { + JcloudsSshMachineLocation result = location.obtain(conf); + machines.add(result); + return result; + } + + @Override + protected void releaseMachine(JcloudsSshMachineLocation machine) { + if (location.getChildren().contains(machine)) { + machines.remove(machine); + location.release(machine); + } + } + + @SuppressWarnings("unchecked") + private SingleMachineProvisioningLocation<JcloudsSshMachineLocation> resolve(String spec) { + SingleMachineProvisioningLocation<JcloudsSshMachineLocation> result = (SingleMachineProvisioningLocation<JcloudsSshMachineLocation>) + managementContext.getLocationRegistry().resolve(spec); + // FIXME Do we really need to setManagementContext?! + //result.setManagementContext(managementContext); + return result; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StandaloneJcloudsLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StandaloneJcloudsLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StandaloneJcloudsLiveTest.java new file mode 100644 index 0000000..b08a087 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StandaloneJcloudsLiveTest.java @@ -0,0 +1,254 @@ +/* + * 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.assertNotNull; + +import java.io.File; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Properties; +import java.util.Set; +import java.util.UUID; + +import org.jclouds.Constants; +import org.jclouds.ContextBuilder; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.RunNodesException; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.options.RunScriptOptions; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.scriptbuilder.statements.login.AdminAccess; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import brooklyn.config.BrooklynProperties; +import brooklyn.util.text.Identifiers; + +import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import com.google.common.io.Files; + +public class StandaloneJcloudsLiveTest { + + // FIXME Why do this? + // Were we seeing bugs in jclouds for which this was easier to debug and report + // Is it because testProvisioningVmWithCustomUsername is disabled and not working? + + public static final Logger LOG = LoggerFactory.getLogger(StandaloneJcloudsLiveTest.class); + + private static final String PROVIDER = AbstractJcloudsLiveTest.AWS_EC2_PROVIDER; + private static final String REGION = AbstractJcloudsLiveTest.AWS_EC2_USEAST_REGION_NAME; + private static final String PRIVATE_IMAGE_ID = "us-east-1/ami-f95cf390"; + + static BrooklynProperties globals = BrooklynProperties.Factory.newDefault(); + + String identity = globals.getFirst("brooklyn.location.jclouds.aws-ec2.identity"); + String credential = globals.getFirst("brooklyn.location.jclouds.aws-ec2.credential"); + + @Test(enabled=false, groups={"WIP","Live"}) + public void createVm() { + String groupId = "mygroup-"+System.getProperty("user.name")+"-"+UUID.randomUUID().toString(); + + Properties properties = new Properties(); + properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, Boolean.toString(true)); + properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, Boolean.toString(true)); + // handy to list all images... but very slow! +// properties.setProperty(AWSEC2Constants.PROPERTY_EC2_AMI_QUERY, "state=available;image-type=machine"); + + ComputeServiceContext computeServiceContext = ContextBuilder.newBuilder(PROVIDER). + modules(Arrays.asList(new SshjSshClientModule(), new SLF4JLoggingModule())). + credentials(identity, credential). + overrides(properties). + build(ComputeServiceContext.class); + + final ComputeService computeService = computeServiceContext.getComputeService(); + + NodeMetadata node = null; + try { + LOG.info("Creating VM for "+identity); + + TemplateBuilder templateBuilder = computeService.templateBuilder(); + templateBuilder.locationId(REGION); + + Template template = templateBuilder.build(); + Set<? extends NodeMetadata> nodes = computeService.createNodesInGroup(groupId, 1, template); + node = Iterables.getOnlyElement(nodes, null); + if (node == null) throw new IllegalStateException("No nodes returned"); + + assertNotNull(node.getOperatingSystem()); + + Credentials nodeCredentials = node.getCredentials(); + final LoginCredentials expectedCredentials = LoginCredentials.fromCredentials(nodeCredentials); + + LOG.info("Started VM, waiting for it to be sshable"); + boolean reachable = false; + for (int i=0; i<120; i++) { + try { + Statement statement = Statements.newStatementList(Statements.exec("date")); + ExecResponse response = computeService.runScriptOnNode(node.getId(), statement, + RunScriptOptions.Builder.overrideLoginCredentials(expectedCredentials)); + if (response.getExitStatus() == 0) { + LOG.info("ssh 'date' succeeded"); + reachable = true; + break; + } + LOG.info("ssh 'date' failed, exit "+response.getExitStatus()+", but still in retry loop"); + } catch (Exception e) { + if (i<120) + LOG.info("ssh 'date' failed, but still in retry loop: "+e); + else { + LOG.error("ssh 'date' failed after timeout: "+e, e); + Throwables.propagate(e); + } + } + Thread.sleep(1000); + } + + if (!reachable) { + throw new IllegalStateException("SSH failed, never reachable"); + } + + } catch (RunNodesException e) { + if (e.getNodeErrors().size() > 0) { + node = Iterables.get(e.getNodeErrors().keySet(), 0); + } + LOG.error("Failed to start VM: "+e, e); + throw Throwables.propagate(e); + } catch (Exception e) { + LOG.error("Failed to start VM: "+e, e); + throw Throwables.propagate(e); + } finally { + LOG.info("Now destroying VM: "+node); + computeService.destroyNode( node.getId() ); + + computeService.getContext().close(); + } + + } + + @Test(enabled=false, groups={"WIP","Live"}) + public void createVmWithAdminUser() { + String groupId = "mygroup-"+System.getProperty("user.name")+"-"+UUID.randomUUID().toString(); + + Properties properties = new Properties(); + properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, Boolean.toString(true)); + properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, Boolean.toString(true)); + + ComputeServiceContext computeServiceContext = ContextBuilder.newBuilder(PROVIDER). + modules(Arrays.asList(new SshjSshClientModule(), new SLF4JLoggingModule())). + credentials(identity, credential). + overrides(properties). + build(ComputeServiceContext.class); + + final ComputeService computeService = computeServiceContext.getComputeService(); + + NodeMetadata node = null; + try { + LOG.info("Creating VM for "+identity); + String myPubKey = Files.toString(new File(System.getProperty("user.home")+"/.ssh/aws-id_rsa.pub"), Charset.defaultCharset()); + String myPrivKey = Files.toString(new File(System.getProperty("user.home")+"/.ssh/aws-id_rsa"), Charset.defaultCharset()); + + TemplateBuilder templateBuilder = computeService.templateBuilder(); + templateBuilder.locationId(REGION); + TemplateOptions opts = new TemplateOptions(); + +// templateBuilder.imageId("us-east-1/ami-2342a94a"); //rightscale + // either use above, or below + templateBuilder.imageId(PRIVATE_IMAGE_ID); //private one (to test when user isn't autodetected) + opts.overrideLoginUser("ec2-user"); + + AdminAccess.Builder adminBuilder = AdminAccess.builder(). + adminUsername("bob"). + grantSudoToAdminUser(true). + authorizeAdminPublicKey(true).adminPublicKey(myPubKey). + // items below aren't wanted but values for some are required otherwise AdminAccess uses all defaults + lockSsh(true).adminPassword(Identifiers.makeRandomId(12)). + resetLoginPassword(false).loginPassword(Identifiers.makeRandomId(12)). + installAdminPrivateKey(false).adminPrivateKey("ignored"); + opts.runScript(adminBuilder.build()); + + templateBuilder.options(opts); + + Template template = templateBuilder.build(); + Set<? extends NodeMetadata> nodes = computeService.createNodesInGroup(groupId, 1, template); + node = Iterables.getOnlyElement(nodes, null); + if (node == null) throw new IllegalStateException("No nodes returned"); + + LOG.info("Started VM, waiting for it to be sshable on "+node.getPublicAddresses()); + final LoginCredentials crds = +// node.getCredentials(); + LoginCredentials.builder().user("bob").privateKey(myPrivKey).build(); + boolean reachable = false; + for (int i=0; i<120; i++) { + try { + Statement statement = Statements.newStatementList(Statements.exec("date")); + ExecResponse response = computeService.runScriptOnNode(node.getId(), statement, + RunScriptOptions.Builder.overrideLoginCredentials(crds)); + if (response.getExitStatus() == 0) { + LOG.info("ssh 'date' succeeded"); + reachable = true; + break; + } + LOG.info("ssh 'date' failed, exit "+response.getExitStatus()+", but still in retry loop"); + } catch (Exception e) { + if (i<120) + LOG.info("ssh 'date' failed, but still in retry loop: "+e); + else { + LOG.error("ssh 'date' failed after timeout: "+e, e); + Throwables.propagate(e); + } + } + Thread.sleep(1000); + } + + if (!reachable) { + throw new IllegalStateException("SSH failed, never reachable"); + } + + } catch (RunNodesException e) { + if (e.getNodeErrors().size() > 0) { + node = Iterables.get(e.getNodeErrors().keySet(), 0); + } + LOG.error("Failed to start VM: "+e, e); + throw Throwables.propagate(e); + } catch (Exception e) { + LOG.error("Failed to start VM: "+e, e); + throw Throwables.propagate(e); + } finally { + LOG.info("Now destroying VM: "+node); + computeService.destroyNode( node.getId() ); + + computeService.getContext().close(); + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizerTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizerTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizerTest.java new file mode 100644 index 0000000..c6025ba --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizerTest.java @@ -0,0 +1,311 @@ +/* + * 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.networking; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertTrue; + +import java.net.URI; +import java.util.Collections; + +import org.jclouds.aws.AWSResponseException; +import org.jclouds.aws.domain.AWSError; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Location; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.mockito.Answers; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.net.Cidr; + +public class JcloudsLocationSecurityGroupCustomizerTest { + + JcloudsLocationSecurityGroupCustomizer customizer; + ComputeService computeService; + Location location; + SecurityGroupExtension securityApi; + + /** Used to skip external checks in unit tests. */ + private static class TestCidrSupplier implements Supplier<Cidr> { + @Override public Cidr get() { + return new Cidr("192.168.10.10/32"); + } + } + + @BeforeMethod + public void setUp() { + customizer = new JcloudsLocationSecurityGroupCustomizer("testapp", new TestCidrSupplier()); + location = mock(Location.class); + securityApi = mock(SecurityGroupExtension.class); + computeService = mock(ComputeService.class, Answers.RETURNS_DEEP_STUBS.get()); + when(computeService.getSecurityGroupExtension()).thenReturn(Optional.of(securityApi)); + } + + @Test + public void testSameInstanceReturnedForSameApplication() { + assertEquals(JcloudsLocationSecurityGroupCustomizer.getInstance("a"), + JcloudsLocationSecurityGroupCustomizer.getInstance("a")); + assertNotEquals(JcloudsLocationSecurityGroupCustomizer.getInstance("a"), + JcloudsLocationSecurityGroupCustomizer.getInstance("b")); + } + + @Test + public void testSecurityGroupAddedWhenJcloudsLocationCustomised() { + Template template = mock(Template.class); + TemplateOptions templateOptions = mock(TemplateOptions.class); + when(template.getLocation()).thenReturn(location); + when(template.getOptions()).thenReturn(templateOptions); + SecurityGroup group = newGroup("id"); + when(securityApi.createSecurityGroup(anyString(), eq(location))).thenReturn(group); + + // Two Brooklyn.JcloudsLocations added to same Jclouds.Location + JcloudsLocation jcloudsLocationA = new JcloudsLocation(MutableMap.of("deferConstruction", true)); + JcloudsLocation jcloudsLocationB = new JcloudsLocation(MutableMap.of("deferConstruction", true)); + customizer.customize(jcloudsLocationA, computeService, template); + customizer.customize(jcloudsLocationB, computeService, template); + + // One group with three permissions shared by both locations. + // Expect TCP, UDP and ICMP between members of group and SSH to Brooklyn + verify(securityApi).createSecurityGroup(anyString(), eq(location)); + verify(securityApi, times(4)).addIpPermission(any(IpPermission.class), eq(group)); + // New groups set on options + verify(templateOptions, times(2)).securityGroups(anyString()); + } + + @Test + public void testSharedGroupLoadedWhenItExistsButIsNotCached() { + Template template = mock(Template.class); + TemplateOptions templateOptions = mock(TemplateOptions.class); + when(template.getLocation()).thenReturn(location); + when(template.getOptions()).thenReturn(templateOptions); + JcloudsLocation jcloudsLocation = new JcloudsLocation(MutableMap.of("deferConstruction", true)); + SecurityGroup shared = newGroup(customizer.getNameForSharedSecurityGroup()); + SecurityGroup irrelevant = newGroup("irrelevant"); + when(securityApi.listSecurityGroupsInLocation(location)).thenReturn(ImmutableSet.of(irrelevant, shared)); + + customizer.customize(jcloudsLocation, computeService, template); + + verify(securityApi).listSecurityGroupsInLocation(location); + verify(securityApi, never()).createSecurityGroup(anyString(), any(Location.class)); + } + + @Test + public void testAddPermissionsToNode() { + IpPermission ssh = newPermission(22); + IpPermission jmx = newPermission(31001); + String nodeId = "node"; + SecurityGroup sharedGroup = newGroup(customizer.getNameForSharedSecurityGroup()); + SecurityGroup group = newGroup("id"); + when(securityApi.listSecurityGroupsForNode(nodeId)).thenReturn(ImmutableSet.of(sharedGroup, group)); + when(computeService.getContext().unwrap().getId()).thenReturn("aws-ec2"); + + customizer.addPermissionsToLocation(ImmutableList.of(ssh, jmx), nodeId, computeService); + + verify(securityApi, never()).createSecurityGroup(anyString(), any(Location.class)); + verify(securityApi, times(1)).addIpPermission(ssh, group); + verify(securityApi, times(1)).addIpPermission(jmx, group); + } + + @Test + public void testAddPermissionsToNodeUsesUncachedSecurityGroup() { + JcloudsLocation jcloudsLocation = new JcloudsLocation(MutableMap.of("deferConstruction", true)); + IpPermission ssh = newPermission(22); + String nodeId = "nodeId"; + SecurityGroup sharedGroup = newGroup(customizer.getNameForSharedSecurityGroup()); + SecurityGroup uniqueGroup = newGroup("unique"); + + Template template = mock(Template.class); + TemplateOptions templateOptions = mock(TemplateOptions.class); + when(template.getLocation()).thenReturn(location); + when(template.getOptions()).thenReturn(templateOptions); + when(securityApi.createSecurityGroup(anyString(), eq(location))).thenReturn(sharedGroup); + when(computeService.getContext().unwrap().getId()).thenReturn("aws-ec2"); + + // Call customize to cache the shared group + customizer.customize(jcloudsLocation, computeService, template); + reset(securityApi); + when(securityApi.listSecurityGroupsForNode(nodeId)).thenReturn(ImmutableSet.of(uniqueGroup, sharedGroup)); + customizer.addPermissionsToLocation(ImmutableSet.of(ssh), nodeId, computeService); + + // Expect the per-machine group to have been altered, not the shared group + verify(securityApi).addIpPermission(ssh, uniqueGroup); + verify(securityApi, never()).addIpPermission(any(IpPermission.class), eq(sharedGroup)); + } + + @Test + public void testSecurityGroupsLoadedWhenAddingPermissionsToUncachedNode() { + IpPermission ssh = newPermission(22); + String nodeId = "nodeId"; + SecurityGroup sharedGroup = newGroup(customizer.getNameForSharedSecurityGroup()); + SecurityGroup uniqueGroup = newGroup("unique"); + + when(securityApi.listSecurityGroupsForNode(nodeId)).thenReturn(ImmutableSet.of(sharedGroup, uniqueGroup)); + when(computeService.getContext().unwrap().getId()).thenReturn("aws-ec2"); + + // Expect first call to list security groups on nodeId, second to use cached version + customizer.addPermissionsToLocation(ImmutableSet.of(ssh), nodeId, computeService); + customizer.addPermissionsToLocation(ImmutableSet.of(ssh), nodeId, computeService); + + verify(securityApi, times(1)).listSecurityGroupsForNode(nodeId); + verify(securityApi, times(2)).addIpPermission(ssh, uniqueGroup); + verify(securityApi, never()).addIpPermission(any(IpPermission.class), eq(sharedGroup)); + } + + @Test + public void testAddRuleNotRetriedByDefault() { + IpPermission ssh = newPermission(22); + String nodeId = "node"; + SecurityGroup sharedGroup = newGroup(customizer.getNameForSharedSecurityGroup()); + SecurityGroup uniqueGroup = newGroup("unique"); + when(securityApi.listSecurityGroupsForNode(nodeId)).thenReturn(ImmutableSet.of(sharedGroup, uniqueGroup)); + when(securityApi.addIpPermission(eq(ssh), eq(uniqueGroup))) + .thenThrow(new RuntimeException("exception creating " + ssh)); + when(computeService.getContext().unwrap().getId()).thenReturn("aws-ec2"); + + try { + customizer.addPermissionsToLocation(ImmutableList.of(ssh), nodeId, computeService); + } catch (Exception e) { + assertTrue(e.getMessage().contains("repeated errors from provider"), "message=" + e.getMessage()); + } + verify(securityApi, never()).createSecurityGroup(anyString(), any(Location.class)); + verify(securityApi, times(1)).addIpPermission(ssh, uniqueGroup); + } + + @Test + public void testCustomExceptionRetryablePredicate() { + final String message = "testCustomExceptionRetryablePredicate"; + Predicate<Exception> messageChecker = new Predicate<Exception>() { + @Override + public boolean apply(Exception input) { + Throwable t = input; + while (t != null) { + if (t.getMessage().contains(message)) { + return true; + } else { + t = t.getCause(); + } + } + return false; + } + }; + customizer.setRetryExceptionPredicate(messageChecker); + when(computeService.getContext().unwrap().getId()).thenReturn("aws-ec2"); + + IpPermission ssh = newPermission(22); + String nodeId = "node"; + SecurityGroup sharedGroup = newGroup(customizer.getNameForSharedSecurityGroup()); + SecurityGroup uniqueGroup = newGroup("unique"); + when(securityApi.listSecurityGroupsForNode(nodeId)).thenReturn(ImmutableSet.of(sharedGroup, uniqueGroup)); + when(securityApi.addIpPermission(eq(ssh), eq(uniqueGroup))) + .thenThrow(new RuntimeException(new Exception(message))) + .thenThrow(new RuntimeException(new Exception(message))) + .thenReturn(sharedGroup); + + customizer.addPermissionsToLocation(ImmutableList.of(ssh), nodeId, computeService); + + verify(securityApi, never()).createSecurityGroup(anyString(), any(Location.class)); + verify(securityApi, times(3)).addIpPermission(ssh, uniqueGroup); + } + + @Test + public void testAddRuleRetriedOnAwsFailure() { + IpPermission ssh = newPermission(22); + String nodeId = "nodeId"; + SecurityGroup sharedGroup = newGroup(customizer.getNameForSharedSecurityGroup()); + SecurityGroup uniqueGroup = newGroup("unique"); + customizer.setRetryExceptionPredicate(JcloudsLocationSecurityGroupCustomizer.newAwsExceptionRetryPredicate()); + when(securityApi.listSecurityGroupsForNode(nodeId)).thenReturn(ImmutableSet.of(sharedGroup, uniqueGroup)); + when(securityApi.addIpPermission(any(IpPermission.class), eq(uniqueGroup))) + .thenThrow(newAwsResponseExceptionWithCode("InvalidGroup.InUse")) + .thenThrow(newAwsResponseExceptionWithCode("DependencyViolation")) + .thenThrow(newAwsResponseExceptionWithCode("RequestLimitExceeded")) + .thenThrow(newAwsResponseExceptionWithCode("Blocked")) + .thenReturn(sharedGroup); + when(computeService.getContext().unwrap().getId()).thenReturn("aws-ec2"); + + try { + customizer.addPermissionsToLocation(ImmutableList.of(ssh), nodeId, computeService); + } catch (Exception e) { + String expected = "repeated errors from provider"; + assertTrue(e.getMessage().contains(expected), "expected exception message to contain " + expected + ", was: " + e.getMessage()); + } + + verify(securityApi, never()).createSecurityGroup(anyString(), any(Location.class)); + verify(securityApi, times(4)).addIpPermission(ssh, uniqueGroup); + } + + private SecurityGroup newGroup(String id) { + URI uri = null; + String ownerId = null; + return new SecurityGroup( + "providerId", + id, + id, + location, + uri, + Collections.<String, String>emptyMap(), + ImmutableSet.<String>of(), + ImmutableSet.<IpPermission>of(), + ownerId); + } + + private IpPermission newPermission(int port) { + return IpPermission.builder() + .ipProtocol(IpProtocol.TCP) + .fromPort(port) + .toPort(port) + .cidrBlock("0.0.0.0/0") + .build(); + } + + private AWSError newAwsErrorWithCode(String code) { + AWSError e = new AWSError(); + e.setCode(code); + return e; + } + + private Exception newAwsResponseExceptionWithCode(String code) { + AWSResponseException e = new AWSResponseException("irrelevant message", null, null, newAwsErrorWithCode(code)); + return new RuntimeException(e); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsPortForwardingStubbedLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsPortForwardingStubbedLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsPortForwardingStubbedLiveTest.java new file mode 100644 index 0000000..860a8e3 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsPortForwardingStubbedLiveTest.java @@ -0,0 +1,196 @@ +/* + * 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.networking; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import java.util.List; + +import org.apache.brooklyn.location.jclouds.AbstractJcloudsStubbedLiveTest; +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadata.Status; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.Template; +import org.jclouds.domain.LoginCredentials; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import brooklyn.config.ConfigKey; +import org.apache.brooklyn.location.access.PortForwardManager; +import org.apache.brooklyn.location.access.PortForwardManagerImpl; +import org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation; +import brooklyn.util.net.Cidr; +import brooklyn.util.net.Protocol; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.net.HostAndPort; + +/** + * The VM creation is stubbed out, but it still requires live access (i.e. real account credentials) + * to generate the template etc. + * + * We supply a ComputeServiceRegistry that delegates to the real instance for everything except + * VM creation and deletion. For those operations, it delegates to a NodeCreator that + * returns a dummy NodeMetadata, recording all calls made to it. + */ +public class JcloudsPortForwardingStubbedLiveTest extends AbstractJcloudsStubbedLiveTest { + + @SuppressWarnings("unused") + private static final Logger LOG = LoggerFactory.getLogger(JcloudsPortForwardingStubbedLiveTest.class); + + static class RecordingJcloudsPortForwarderExtension implements JcloudsPortForwarderExtension { + final PortForwardManager pfm; + final List<List<Object>> opens = Lists.newCopyOnWriteArrayList(); + final List<List<Object>> closes = Lists.newCopyOnWriteArrayList(); + int nextPublicPort = 12345; + + RecordingJcloudsPortForwarderExtension(PortForwardManager pfm) { + this.pfm = pfm; + } + @Override public HostAndPort openPortForwarding(NodeMetadata node, int targetPort, Optional<Integer> optionalPublicPort, Protocol protocol, Cidr accessingCidr) { + opens.add(ImmutableList.of(node, targetPort, optionalPublicPort, protocol, accessingCidr)); + HostAndPort result = HostAndPort.fromParts("1.2.3.4", nextPublicPort++); + pfm.associate(node.getId(), result, targetPort); + return result; + } + @Override public void closePortForwarding(NodeMetadata node, int targetPort, HostAndPort publicHostAndPort, Protocol protocol) { + closes.add(ImmutableList.of(node, targetPort, publicHostAndPort, protocol)); + pfm.forgetPortMapping(node.getId(), publicHostAndPort.getPort()); + } + } + + @Override + protected NodeCreator newNodeCreator() { + return new NodeCreator() { + int nextIpSuffix = 2; + @Override + protected NodeMetadata newNode(String group, Template template) { + int ipSuffix = nextIpSuffix++; + NodeMetadata result = new NodeMetadataBuilder() + .id("myid-"+ipSuffix) + .credentials(LoginCredentials.builder().identity("myuser").credential("mypassword").build()) + .loginPort(22) + .status(Status.RUNNING) + .publicAddresses(ImmutableList.of("173.194.32."+ipSuffix)) + .privateAddresses(ImmutableList.of("172.168.10."+ipSuffix)) + .build(); + return result; + } + }; + } + + @Test(groups = {"Live", "Live-sanity"}) + protected void testPortForwardingCallsForwarder() throws Exception { + PortForwardManager pfm = new PortForwardManagerImpl(); + RecordingJcloudsPortForwarderExtension portForwarder = new RecordingJcloudsPortForwarderExtension(pfm); + + JcloudsSshMachineLocation machine = obtainMachine(ImmutableMap.<ConfigKey<?>,Object>of( + JcloudsLocation.USE_PORT_FORWARDING, true, + JcloudsLocation.PORT_FORWARDER, portForwarder)); + + NodeMetadata created = nodeCreator.created.get(0); + assertEquals(nodeCreator.created.size(), 1, "created="+nodeCreator.created+"; machine="+machine); + assertEquals(machine.getNode(), created); + assertEquals(portForwarder.opens.size(), 1, "opens="+portForwarder.opens+"; machine="+machine); + assertEquals(portForwarder.opens.get(0).get(0), created); + assertEquals(portForwarder.opens.get(0).get(1), 22); + assertEquals(portForwarder.opens.get(0).get(3), Protocol.TCP); + assertEquals(portForwarder.opens.get(0).get(4), Cidr.UNIVERSAL); + assertEquals(machine.getSshHostAndPort(), HostAndPort.fromParts("1.2.3.4", 12345)); + + releaseMachine(machine); + String destroyed = nodeCreator.destroyed.get(0); + assertEquals(nodeCreator.destroyed.size(), 1, "destroyed="+nodeCreator.destroyed+"; machine="+machine); + assertEquals(destroyed, created.getId()); + assertEquals(portForwarder.closes.size(), 1, "closes="+portForwarder.closes+"; machine="+machine); + assertEquals(portForwarder.closes.get(0).get(0), created); + assertEquals(portForwarder.closes.get(0).get(1), 22); + assertEquals(portForwarder.closes.get(0).get(2), HostAndPort.fromParts("1.2.3.4", 12345)); + assertEquals(portForwarder.closes.get(0).get(3), Protocol.TCP); + } + + @Test(groups = {"Live", "Live-sanity"}) + protected void testDeregistersWithPortForwardManagerOnRelease() throws Exception { + PortForwardManager pfm = new PortForwardManagerImpl(); + RecordingJcloudsPortForwarderExtension portForwarder = new RecordingJcloudsPortForwarderExtension(pfm); + + JcloudsSshMachineLocation machine = obtainMachine(ImmutableMap.<ConfigKey<?>,Object>of( + JcloudsLocation.PORT_FORWARDER, portForwarder, + JcloudsLocation.PORT_FORWARDING_MANAGER, pfm)); + + // Add an association for this machine - expect that to be deleted when the machine is released. + HostAndPort publicHostAndPort = HostAndPort.fromParts("1.2.3.4", 1234); + pfm.associate("mypublicip", publicHostAndPort, machine, 80); + assertEquals(pfm.lookup(machine, 80), publicHostAndPort); + assertEquals(pfm.lookup("mypublicip", 80), publicHostAndPort); + + // Release + releaseMachine(machine); + + // Expect to have been cleared from PortForwardManager's records + assertNull(pfm.lookup(machine, 80)); + assertNull(pfm.lookup("mypublicip", 80)); + + // And for port-forwarding to have been closed + assertEquals(portForwarder.closes.size(), 1, "closes="+portForwarder.closes+"; machine="+machine); + assertEquals(portForwarder.closes.get(0).get(1), 80); + assertEquals(portForwarder.closes.get(0).get(2), HostAndPort.fromParts("1.2.3.4", 1234)); + assertEquals(portForwarder.closes.get(0).get(3), Protocol.TCP); + } + + @Test(groups = {"Live", "Live-sanity"}) + protected void testReleaseVmDoesNotImpactOtherVms() throws Exception { + PortForwardManager pfm = new PortForwardManagerImpl(); + RecordingJcloudsPortForwarderExtension portForwarder = new RecordingJcloudsPortForwarderExtension(pfm); + + JcloudsSshMachineLocation machine1 = obtainMachine(ImmutableMap.<ConfigKey<?>,Object>of( + JcloudsLocation.USE_PORT_FORWARDING, true, + JcloudsLocation.PORT_FORWARDER, portForwarder, + JcloudsLocation.PORT_FORWARDING_MANAGER, pfm)); + + JcloudsSshMachineLocation machine2 = obtainMachine(ImmutableMap.<ConfigKey<?>,Object>of( + JcloudsLocation.USE_PORT_FORWARDING, true, + JcloudsLocation.PORT_FORWARDER, portForwarder, + JcloudsLocation.PORT_FORWARDING_MANAGER, pfm)); + + NodeMetadata node1 = nodeCreator.created.get(0); + + // Add an association for machine2 - expect that not to be touched when machine1 is released. + HostAndPort publicHostAndPort = HostAndPort.fromParts("1.2.3.4", 1234); + pfm.associate("mypublicip", publicHostAndPort, machine2, 80); + + // Release machine1 + releaseMachine(machine1); + + // Expect machine2 to still be registered + assertEquals(pfm.lookup(machine2, 80), publicHostAndPort); + assertEquals(pfm.lookup("mypublicip", 80), publicHostAndPort); + + // And no calls to "close" for machine2; just for machine1's port 22 + assertEquals(portForwarder.closes.size(), 1, "closes="+portForwarder.closes+"; machine1="+machine1); + assertEquals(portForwarder.closes.get(0).get(0), node1); + assertEquals(portForwarder.closes.get(0).get(1), 22); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/SecurityGroupLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/SecurityGroupLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/SecurityGroupLiveTest.java new file mode 100644 index 0000000..0eed616 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/SecurityGroupLiveTest.java @@ -0,0 +1,32 @@ +/* + * 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.networking; + +import org.testng.annotations.Test; + +@Test(groups = {"Live", "WIP"}) +public class SecurityGroupLiveTest { + + public void testCreateEc2WithSecurityGroup() { + SecurityGroupDefinition sgDef = new SecurityGroupDefinition() + .allowingInternalPorts(8097, 8098).allowingInternalPortRange(6000, 7999) + .allowingPublicPort(8099); + // TODO create machine and test + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/pool/JcloudsMachinePoolLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/pool/JcloudsMachinePoolLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/pool/JcloudsMachinePoolLiveTest.java new file mode 100644 index 0000000..4d30c80 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/pool/JcloudsMachinePoolLiveTest.java @@ -0,0 +1,120 @@ +/* + * 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.pool; + +import java.util.Arrays; + +import org.apache.brooklyn.location.jclouds.AbstractJcloudsLiveTest; +import org.jclouds.ContextBuilder; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.sshj.config.SshjSshClientModule; +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 org.apache.brooklyn.location.jclouds.JcloudsLocation; + +public class JcloudsMachinePoolLiveTest extends AbstractJcloudsLiveTest { + + public static final Logger log = LoggerFactory.getLogger(JcloudsMachinePoolLiveTest.class); + + private static final String PROVIDER = AWS_EC2_PROVIDER; + private static final String LOCATION_SPEC = PROVIDER + ":" + AWS_EC2_EUWEST_REGION_NAME; + + public static class SamplePool extends MachinePool { + public SamplePool(ComputeService svc) { + super(svc); + } + + public final static ReusableMachineTemplate + USUAL_VM = + new ReusableMachineTemplate("usual").templateOwnedByMe(). + tagOptional("tagForUsualVm"). + metadataOptional("metadataForUsualVm", "12345"). + minRam(1024).minCores(2); + + public final static ReusableMachineTemplate + ANYONE_NOT_TINY_VM = + new ReusableMachineTemplate("anyone"). + minRam(512).minCores(1).strict(false); + + public static final ReusableMachineTemplate + VM_LARGE1 = + new ReusableMachineTemplate("vm.large1").templateOwnedByMe(). + minRam(16384).minCores(4), + VM_SMALL1 = + new ReusableMachineTemplate("vm.small1").templateOwnedByMe().smallest(); + + { registerTemplates(USUAL_VM, ANYONE_NOT_TINY_VM, VM_LARGE1, VM_SMALL1); } + } + + private ComputeServiceContext context; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + + jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(LOCATION_SPEC); + + context = ContextBuilder.newBuilder(PROVIDER) + .modules(Arrays.asList(new SshjSshClientModule(), new SLF4JLoggingModule())) + .credentials(jcloudsLocation.getIdentity(), jcloudsLocation.getCredential()) + .build(ComputeServiceContext.class); + } + + @AfterMethod(alwaysRun=true) + @Override + public void tearDown() throws Exception { + try { + super.tearDown(); + } finally { + if (context != null) context.close(); + } + } + + @Test(groups={"Live","WIP"}) + public void buildClaimAndDestroy() { + ComputeService svc = context.getComputeService(); + SamplePool p = new SamplePool(svc); + log.info("buildClaimAndDestroy: created pool"); + p.refresh(); + log.info("buildClaimAndDestroy: refreshed pool"); + p.ensureExists(2, SamplePool.USUAL_VM); + log.info("buildClaimAndDestroy: ensure have 2"); + MachineSet l = p.claim(1, SamplePool.USUAL_VM); + Assert.assertEquals(l.size(), 1); + log.info("buildClaimAndDestroy: claimed 1"); + MachineSet unclaimedUsual = p.unclaimed(MachinePoolPredicates.matching(SamplePool.USUAL_VM)); + log.info("buildClaimAndDestroy: unclaimed now "+unclaimedUsual); + Assert.assertTrue(!unclaimedUsual.isEmpty()); + p.destroy(unclaimedUsual); + unclaimedUsual = p.unclaimed(MachinePoolPredicates.matching(SamplePool.USUAL_VM)); + log.info("buildClaimAndDestroy: destroyed, unclaimed now "+unclaimedUsual); + log.info("end"); + } + + + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/AbstractJcloudsLocationTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/AbstractJcloudsLocationTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/AbstractJcloudsLocationTest.java new file mode 100644 index 0000000..fd93a7a --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/AbstractJcloudsLocationTest.java @@ -0,0 +1,170 @@ +/* + * 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.provider; + +import static org.testng.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.apache.brooklyn.api.management.ManagementContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.testng.collections.Lists; + +import brooklyn.config.BrooklynProperties; +import brooklyn.entity.basic.Entities; +import org.apache.brooklyn.location.NoMachinesAvailableException; +import org.apache.brooklyn.location.basic.SshMachineLocation; +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import brooklyn.util.collections.MutableList; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.exceptions.Exceptions; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +public abstract class AbstractJcloudsLocationTest { + private static final Logger LOG = LoggerFactory.getLogger(AbstractJcloudsLocationTest.class); + + private final String provider; + + protected JcloudsLocation loc; + protected List<SshMachineLocation> machines = MutableList.of(); + protected ManagementContext ctx; + + protected AbstractJcloudsLocationTest(String provider) { + this.provider = provider; + } + + /** + * The location and image id tuplets to test. + */ + @DataProvider(name = "fromImageId") + public abstract Object[][] cloudAndImageIds(); + + /** + * A single location and image id tuplet to test. + */ + @DataProvider(name = "fromFirstImageId") + public Object[][] cloudAndImageFirstId() { + Object[][] all = cloudAndImageIds(); + return (all != null) ? new Object[][] { all[0] } : new Object[][] { }; + } + + /** + * The location and image name pattern tuplets to test. + */ + @DataProvider(name = "fromImageNamePattern") + public abstract Object[][] cloudAndImageNamePatterns(); + + /** + * The location, image pattern and image owner tuplets to test. + */ + @DataProvider(name = "fromImageDescriptionPattern") + public abstract Object[][] cloudAndImageDescriptionPatterns(); + + @BeforeMethod(alwaysRun=true) + public void setUp() { + BrooklynProperties props = BrooklynProperties.Factory.newDefault().addFromMap(ImmutableMap.of("provider", provider)); + ctx = Entities.newManagementContext(props.asMapWithStringKeys()); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() { + List<Exception> exceptions = Lists.newArrayList(); + for (SshMachineLocation machine : machines) { + try { + loc.release(machine); + } catch (Exception e) { + LOG.warn("Error releasing {}: {}; continuing...", machine, e.getMessage()); + exceptions.add(e); + } + } + if (!exceptions.isEmpty()) { + LOG.info("Exception during tearDown: {}", Exceptions.collapseText(exceptions.get(0))); + } + machines.clear(); + + if (ctx != null) Entities.destroyAllCatching(ctx); + } + + @Test(dataProvider="fromImageId") + public void testTagMapping(String regionName, String imageId, String imageOwner) { + Map<String, Object> dummy = ImmutableMap.<String, Object>of("identity", "DUMMY", "credential", "DUMMY"); + loc = (JcloudsLocation) ctx.getLocationRegistry().resolve(provider + (regionName == null ? "" : ":" + regionName), dummy); + ImmutableMap.Builder<String, Object> builder = ImmutableMap.<String, Object>builder().put("imageId", imageId); + if (imageOwner != null) builder.put("imageOwner", imageOwner); + Map<String, Object> tagMapping = builder.build(); + loc.setTagMapping(ImmutableMap.<String, Map<String, ? extends Object>>of("MyEntityType", tagMapping)); + + Map<String, Object> flags = loc.getProvisioningFlags(ImmutableList.of("MyEntityType")); + assertTrue(Maps.<String, Object>difference(flags, tagMapping).entriesOnlyOnRight().isEmpty(), "flags="+flags); + } + + @Test(groups = "Live", dataProvider="fromImageId") + public void testProvisionVmUsingImageId(String regionName, String imageId, String imageOwner) { + loc = (JcloudsLocation) ctx.getLocationRegistry().resolve(provider + (regionName == null ? "" : ":" + regionName)); + SshMachineLocation machine = obtainMachine(MutableMap.of("imageId", imageId, "imageOwner", imageOwner, JcloudsLocation.MACHINE_CREATE_ATTEMPTS, 2)); + + LOG.info("Provisioned {} vm {}; checking if ssh'able", provider, machine); + assertTrue(machine.isSshable()); + } + + @Test(groups = "Live", dataProvider="fromImageNamePattern") + public void testProvisionVmUsingImageNamePattern(String regionName, String imageNamePattern, String imageOwner) { + loc = (JcloudsLocation) ctx.getLocationRegistry().resolve(provider + (regionName == null ? "" : ":" + regionName)); + SshMachineLocation machine = obtainMachine(MutableMap.of("imageNameRegex", imageNamePattern, "imageOwner", imageOwner, JcloudsLocation.MACHINE_CREATE_ATTEMPTS, 2)); + + LOG.info("Provisioned {} vm {}; checking if ssh'able", provider, machine); + assertTrue(machine.isSshable()); + } + + @Test(groups = "Live", dataProvider="fromImageDescriptionPattern") + public void testProvisionVmUsingImageDescriptionPattern(String regionName, String imageDescriptionPattern, String imageOwner) { + loc = (JcloudsLocation) ctx.getLocationRegistry().resolve(provider + (regionName == null ? "" : ":" + regionName)); + SshMachineLocation machine = obtainMachine(MutableMap.of("imageDescriptionRegex", imageDescriptionPattern, "imageOwner", imageOwner, JcloudsLocation.MACHINE_CREATE_ATTEMPTS, 2)); + + LOG.info("Provisioned {} vm {}; checking if ssh'able", provider, machine); + assertTrue(machine.isSshable()); + } + + // Use this utility method to ensure machines are released on tearDown + protected SshMachineLocation obtainMachine(Map flags) { + try { + SshMachineLocation result = (SshMachineLocation)loc.obtain(flags); + machines.add(result); + return result; + } catch (NoMachinesAvailableException nmae) { + LOG.warn("No machines available", nmae); + throw Exceptions.propagate(nmae); + } + } + + protected SshMachineLocation release(SshMachineLocation machine) { + machines.remove(machine); + loc.release(machine); + return machine; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/AwsEc2LocationLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/AwsEc2LocationLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/AwsEc2LocationLiveTest.java new file mode 100644 index 0000000..6644645 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/AwsEc2LocationLiveTest.java @@ -0,0 +1,66 @@ +/* + * 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.provider; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class AwsEc2LocationLiveTest extends AbstractJcloudsLocationTest { + + private static final String PROVIDER = "aws-ec2"; + private static final String EUWEST_REGION_NAME = "eu-west-1"; + private static final String USEAST_REGION_NAME = "us-east-1"; + private static final String EUWEST_IMAGE_ID = EUWEST_REGION_NAME+"/"+"ami-89def4fd"; // RightImage_CentOS_5.4_i386_v5.5.9_EBS + private static final String USEAST_IMAGE_ID = USEAST_REGION_NAME+"/"+"ami-2342a94a"; // RightImage_CentOS_5.4_i386_v5.5.9_EBS + private static final String IMAGE_OWNER = "411009282317"; + private static final String IMAGE_PATTERN = "RightImage_CentOS_5.4_i386_v5.5.9_EBS"; + + public AwsEc2LocationLiveTest() { + super(PROVIDER); + } + + @Override + @DataProvider(name = "fromImageId") + public Object[][] cloudAndImageIds() { + return new Object[][] { + new Object[] { EUWEST_REGION_NAME, EUWEST_IMAGE_ID, IMAGE_OWNER }, + new Object[] { USEAST_REGION_NAME, USEAST_IMAGE_ID, IMAGE_OWNER } + }; + } + + @Override + @DataProvider(name = "fromImageDescriptionPattern") + public Object[][] cloudAndImageDescriptionPatterns() { + return new Object[][] { + new Object[] { EUWEST_REGION_NAME, IMAGE_PATTERN, IMAGE_OWNER }, + new Object[] { USEAST_REGION_NAME, IMAGE_PATTERN, IMAGE_OWNER } + }; + } + + @Override + @DataProvider(name = "fromImageNamePattern") + public Object[][] cloudAndImageNamePatterns() { + return new Object[][] { + new Object[] { USEAST_REGION_NAME, IMAGE_PATTERN, IMAGE_OWNER } + }; + } + + @Test(enabled = false) + public void noop() { } /* just exists to let testNG IDE run the test */ +}
