You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/08/19 13:10:04 UTC
[46/72] [abbrv] incubator-brooklyn git commit: BROOKLYN-162 - jclouds
last few package prefixes needed,
and tidy in core and elsewhere related (or observed in the process)
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/MultiLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/MultiLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/core/MultiLocationTest.java
new file mode 100644
index 0000000..ab527f2
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/MultiLocationTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.core;
+
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.net.Networking;
+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.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
+import org.apache.brooklyn.location.cloud.AvailabilityZoneExtension;
+import org.apache.brooklyn.location.core.MultiLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class MultiLocationTest {
+
+ private static final Logger log = LoggerFactory.getLogger(MultiLocationTest.class);
+
+ private LocalManagementContext managementContext;
+ private SshMachineLocation mac1a;
+ private SshMachineLocation mac1b;
+ private SshMachineLocation mac2a;
+ private SshMachineLocation mac2b;
+ private FixedListMachineProvisioningLocation<SshMachineLocation> loc1;
+ private FixedListMachineProvisioningLocation<SshMachineLocation> loc2;
+ private MultiLocation<SshMachineLocation> multiLoc;
+
+ @SuppressWarnings("unchecked")
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ managementContext = LocalManagementContextForTests.newInstance();
+ mac1a = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .displayName("mac1a")
+ .configure("address", Networking.getInetAddressWithFixedName("1.1.1.1")));
+ mac1b = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .displayName("mac1b")
+ .configure("address", Networking.getInetAddressWithFixedName("1.1.1.2")));
+ mac2a = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .displayName("mac2a")
+ .configure("address", Networking.getInetAddressWithFixedName("1.1.1.3")));
+ mac2b = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .displayName("mac2b")
+ .configure("address", Networking.getInetAddressWithFixedName("1.1.1.4")));
+ loc1 = managementContext.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+ .displayName("loc1")
+ .configure("machines", MutableSet.of(mac1a, mac1b)));
+ loc2 = managementContext.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+ .displayName("loc2")
+ .configure("machines", MutableSet.of(mac2a, mac2b)));
+ multiLoc = managementContext.getLocationManager().createLocation(LocationSpec.create(MultiLocation.class)
+ .displayName("multiLoc")
+ .configure("subLocations", ImmutableList.of(loc1, loc2)));
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (managementContext != null) Entities.destroyAll(managementContext);
+ }
+
+ @Test
+ public void testHasAvailabilityZonesAsSubLocations() throws Exception {
+ multiLoc.hasExtension(AvailabilityZoneExtension.class);
+ AvailabilityZoneExtension extension = multiLoc.getExtension(AvailabilityZoneExtension.class);
+ Asserts.assertEqualsIgnoringOrder(extension.getAllSubLocations(), ImmutableList.of(loc1, loc2));
+ Asserts.assertEqualsIgnoringOrder(extension.getSubLocations(2), ImmutableList.of(loc1, loc2));
+ assertTrue(ImmutableList.of(loc1, loc2).containsAll(extension.getSubLocations(1)));
+ }
+
+ @Test
+ public void testObtainAndReleaseDelegateToSubLocation() throws Exception {
+ SshMachineLocation obtained = multiLoc.obtain(ImmutableMap.of());
+ assertTrue(ImmutableList.of(mac1a, mac1b, mac2a, mac2b).contains(obtained));
+ multiLoc.release(obtained);
+ }
+
+ @Test
+ public void testObtainsMovesThroughSubLocations() throws Exception {
+ Assert.assertEquals(multiLoc.obtain().getAddress().getHostAddress(), "1.1.1.1");
+ Assert.assertEquals(multiLoc.obtain().getAddress().getHostAddress(), "1.1.1.2");
+ Assert.assertEquals(multiLoc.obtain().getAddress().getHostAddress(), "1.1.1.3");
+ Assert.assertEquals(multiLoc.obtain().getAddress().getHostAddress(), "1.1.1.4");
+ try {
+ multiLoc.obtain();
+ Assert.fail();
+ } catch (NoMachinesAvailableException e) {
+ log.info("Error when no machines available across locations is: "+e);
+ Assert.assertTrue(e.toString().contains("loc1"), "Message should have referred to sub-location message: "+e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/PaasLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/PaasLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/core/PaasLocationTest.java
new file mode 100644
index 0000000..a58c0a1
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/PaasLocationTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.core;
+
+import org.apache.brooklyn.core.test.location.TestPaasLocation;
+import org.apache.brooklyn.location.paas.PaasLocation;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class PaasLocationTest {
+
+ private PaasLocation location;
+
+ @Test
+ public void testProviderName(){
+ location = new TestPaasLocation();
+ Assert.assertEquals(location.getPaasProviderName(), "TestPaas");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/PortRangesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/PortRangesTest.java b/core/src/test/java/org/apache/brooklyn/location/core/PortRangesTest.java
new file mode 100644
index 0000000..e4d7036
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/PortRangesTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.core;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Iterator;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.location.core.PortRanges;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class PortRangesTest {
+
+ @Test
+ public void testSingleRange() {
+ PortRange r = PortRanges.fromInteger(1234);
+ assertContents(r, 1234);
+ }
+
+ @Test
+ public void testFromCollection() {
+ PortRange r = PortRanges.fromCollection(ImmutableList.of(1234, 2345));
+ assertContents(r, 1234, 2345);
+ }
+
+ @Test
+ public void testFromString() {
+ PortRange r = PortRanges.fromString("80,8080,8000,8080-8099");
+ assertContents(r, 80, 8080, 8000,
+ 8080, 8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089,
+ 8090, 8091, 8092, 8093, 8094, 8095, 8096, 8097, 8098, 8099);
+ }
+
+ @Test
+ public void testFromStringWithSpaces() {
+ PortRange r = PortRanges.fromString(" 80 , 8080 , 8000 , 8080 - 8099 ");
+ assertContents(r, 80, 8080, 8000,
+ 8080, 8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089,
+ 8090, 8091, 8092, 8093, 8094, 8095, 8096, 8097, 8098, 8099);
+ }
+
+ @Test
+ public void testFromStringWithSpacesToString() {
+ PortRange r = PortRanges.fromString(" 80 , 8080 , 8000 , 8080 - 8099 ");
+ Assert.assertEquals(r.toString(), "80,8080,8000,8080-8099");
+ }
+
+ @Test
+ public void testFromStringThrowsIllegalArgumentException() {
+ assertFromStringThrowsIllegalArgumentException("80-100000");
+ assertFromStringThrowsIllegalArgumentException("0-80");
+ }
+
+ @Test
+ public void testCoercion() {
+ PortRanges.init();
+ PortRange r = TypeCoercions.coerce("80", PortRange.class);
+ assertContents(r, 80);
+ }
+
+ @Test
+ public void testCoercionInt() {
+ PortRanges.init();
+ PortRange r = TypeCoercions.coerce(80, PortRange.class);
+ assertContents(r, 80);
+ }
+
+ @Test
+ public void testLinearRangeOfSizeOne() throws Exception {
+ PortRanges.LinearPortRange range = new PortRanges.LinearPortRange(80, 80);
+ assertEquals(Lists.newArrayList(range), ImmutableList.of(80));
+ }
+
+ @Test
+ public void testLinearRangeCountingUpwards() throws Exception {
+ PortRanges.LinearPortRange range = new PortRanges.LinearPortRange(80, 81);
+ assertEquals(Lists.newArrayList(range), ImmutableList.of(80, 81));
+ }
+
+ @Test
+ public void testLinearRangeCountingDownwards() throws Exception {
+ PortRanges.LinearPortRange range = new PortRanges.LinearPortRange(80, 79);
+ assertEquals(Lists.newArrayList(range), ImmutableList.of(80, 79));
+ }
+
+ protected void assertFromStringThrowsIllegalArgumentException(String range) {
+ try {
+ PortRanges.fromString(range);
+ Assert.fail();
+ } catch (IllegalArgumentException e) {
+ // success
+ }
+ }
+
+ private static <T> void assertContents(Iterable<T> actual, T ...expected) {
+ Iterator<T> i = actual.iterator();
+ int c = 0;
+ while (i.hasNext()) {
+ if (expected.length<=c) {
+ Assert.fail("Iterable contained more than the "+c+" expected elements");
+ }
+ Assert.assertEquals(i.next(), expected[c++]);
+ }
+ if (expected.length>c) {
+ Assert.fail("Iterable contained only "+c+" elements, "+expected.length+" expected");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/RecordingMachineLocationCustomizer.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/RecordingMachineLocationCustomizer.java b/core/src/test/java/org/apache/brooklyn/location/core/RecordingMachineLocationCustomizer.java
new file mode 100644
index 0000000..efaa94f
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/RecordingMachineLocationCustomizer.java
@@ -0,0 +1,71 @@
+/*
+ * 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.core;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.MachineLocationCustomizer;
+
+public class RecordingMachineLocationCustomizer implements MachineLocationCustomizer {
+ public static class Call {
+ public final String methodName;
+ public final List<?> args;
+
+ public Call(String methodName, List<?> args) {
+ this.methodName = checkNotNull(methodName);
+ this.args = checkNotNull(args);
+ }
+
+ @Override
+ public String toString() {
+ return methodName+args;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(methodName, args);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof RecordingMachineLocationCustomizer.Call) &&
+ methodName.equals(((RecordingMachineLocationCustomizer.Call)other).methodName) &&
+ args.equals(((RecordingMachineLocationCustomizer.Call)other).args);
+ }
+ }
+
+ public final List<RecordingMachineLocationCustomizer.Call> calls = Lists.newCopyOnWriteArrayList();
+
+ @Override
+ public void customize(MachineLocation machine) {
+ calls.add(new Call("customize", ImmutableList.of(machine)));
+ }
+
+ @Override
+ public void preRelease(MachineLocation machine) {
+ calls.add(new Call("preRelease", ImmutableList.of(machine)));
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/SimulatedLocation.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/SimulatedLocation.java b/core/src/test/java/org/apache/brooklyn/location/core/SimulatedLocation.java
new file mode 100644
index 0000000..e9f572c
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/SimulatedLocation.java
@@ -0,0 +1,141 @@
+/*
+ * 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.core;
+
+import java.net.InetAddress;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.location.HardwareDetails;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.MachineDetails;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.api.location.PortSupplier;
+import org.apache.brooklyn.location.core.AbstractLocation;
+import org.apache.brooklyn.location.core.BasicHardwareDetails;
+import org.apache.brooklyn.location.core.BasicMachineDetails;
+import org.apache.brooklyn.location.core.BasicOsDetails;
+import org.apache.brooklyn.location.core.PortRanges;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.net.Networking;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+
+/** Location for use in dev/test, defining custom start/stop support, and/or tweaking the ports which are permitted to be available
+ * (using setPermittedPorts(Iterable))
+ */
+public class SimulatedLocation extends AbstractLocation implements MachineProvisioningLocation<MachineLocation>, MachineLocation, PortSupplier {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final InetAddress address;
+ static {
+ address = Networking.getLocalHost();
+ }
+
+ Iterable<Integer> permittedPorts = PortRanges.fromString("1+");
+ Set<Integer> usedPorts = Sets.newLinkedHashSet();
+
+ public SimulatedLocation() {
+ this(MutableMap.<String,Object>of());
+ }
+ public SimulatedLocation(Map<String,? extends Object> flags) {
+ super(flags);
+ }
+
+ @Override
+ public SimulatedLocation newSubLocation(Map<?,?> newFlags) {
+ // TODO shouldn't have to copy config bag as it should be inherited (but currently it is not used inherited everywhere; just most places)
+ return getManagementContext().getLocationManager().createLocation(LocationSpec.create(getClass())
+ .parent(this)
+ .configure(config().getLocalBag().getAllConfig()) // FIXME Should this just be inherited?
+ .configure(newFlags));
+ }
+
+ public MachineLocation obtain(Map<?,?> flags) {
+ return this;
+ }
+
+ public void release(MachineLocation machine) {
+ }
+
+ public Map<String,Object> getProvisioningFlags(Collection<String> tags) {
+ return MutableMap.<String,Object>of();
+ }
+
+ public InetAddress getAddress() {
+ return address;
+ }
+
+ @Override
+ public String getHostname() {
+ String hostname = address.getHostName();
+ return (hostname == null || hostname.equals(address.getHostAddress())) ? null : hostname;
+ }
+
+ @Override
+ public Set<String> getPublicAddresses() {
+ return ImmutableSet.of(address.getHostAddress());
+ }
+
+ @Override
+ public Set<String> getPrivateAddresses() {
+ return ImmutableSet.of();
+ }
+
+ public synchronized boolean obtainSpecificPort(int portNumber) {
+ if (!Iterables.contains(permittedPorts, portNumber)) return false;
+ if (usedPorts.contains(portNumber)) return false;
+ usedPorts.add(portNumber);
+ return true;
+ }
+
+ public synchronized int obtainPort(PortRange range) {
+ for (int p: range)
+ if (obtainSpecificPort(p)) return p;
+ return -1;
+ }
+
+ public synchronized void releasePort(int portNumber) {
+ usedPorts.remove(portNumber);
+ }
+
+ public synchronized void setPermittedPorts(Iterable<Integer> ports) {
+ permittedPorts = ports;
+ }
+
+ @Override
+ public OsDetails getOsDetails() {
+ return getMachineDetails().getOsDetails();
+ }
+
+ @Override
+ public MachineDetails getMachineDetails() {
+ HardwareDetails hardwareDetails = new BasicHardwareDetails(null, null);
+ OsDetails osDetails = BasicOsDetails.Factory.ANONYMOUS_LINUX;
+ return new BasicMachineDetails(hardwareDetails, osDetails);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/TestPortSupplierLocation.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/TestPortSupplierLocation.java b/core/src/test/java/org/apache/brooklyn/location/core/TestPortSupplierLocation.java
new file mode 100644
index 0000000..df37585
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/TestPortSupplierLocation.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.core;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.location.core.PortRanges;
+import org.apache.brooklyn.sensor.core.PortAttributeSensorAndConfigKey;
+import org.apache.brooklyn.sensor.feed.ConfigToAttributes;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestPortSupplierLocation extends BrooklynAppUnitTestSupport {
+
+ SimulatedLocation loc;
+ PortAttributeSensorAndConfigKey ps;
+ TestEntity entity;
+
+ @BeforeMethod(alwaysRun=true)
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ loc = app.newSimulatedLocation();
+ entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+ app.start(ImmutableList.of(loc));
+
+ ps = new PortAttributeSensorAndConfigKey("some.port", "for testing", "1234+");
+ }
+
+ @Test
+ public void testObtainsPort() throws Exception {
+ ConfigToAttributes.apply(entity, ps);
+
+ int p = entity.getAttribute(ps);
+ assertEquals(p, 1234);
+
+ //sensor access should keep the same value
+ p = entity.getAttribute(ps);
+ assertEquals(p, 1234);
+ }
+
+ @Test
+ public void testRepeatedConvertAccessIncrements() throws Exception {
+ int p = ps.getAsSensorValue(entity);
+ assertEquals(p, 1234);
+
+ //but direct access should see it as being reserved (not required behaviour, but it is the current behaviour)
+ int p2 = ps.getAsSensorValue(entity);
+ assertEquals(p2, 1235);
+ }
+
+ @Test
+ public void testNullBeforeSetting() throws Exception {
+ // currently getting the attribute before explicitly setting return null; i.e. no "auto-set" --
+ // but this behaviour may be changed
+ Integer p = entity.getAttribute(ps);
+ assertEquals(p, null);
+ }
+
+ @Test
+ public void testSimulatedRestrictedPermitted() throws Exception {
+ loc.setPermittedPorts(PortRanges.fromString("1240+"));
+
+ ConfigToAttributes.apply(entity, ps);
+ int p = entity.getAttribute(ps);
+ assertEquals((int)p, 1240);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostLocationResolverTest.java
new file mode 100644
index 0000000..bb781a4
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostLocationResolverTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.core.localhost;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
+import org.apache.brooklyn.location.core.BasicLocationRegistry;
+import org.apache.brooklyn.location.core.internal.LocationInternal;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class LocalhostLocationResolverTest {
+
+ private BrooklynProperties brooklynProperties;
+ private LocalManagementContext managementContext;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ managementContext = LocalManagementContextForTests.newInstance();
+ brooklynProperties = managementContext.getBrooklynProperties();
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (managementContext != null) Entities.destroyAll(managementContext);
+ }
+
+ @Test
+ public void testTakesLocalhostScopedProperties() {
+ brooklynProperties.put("brooklyn.location.localhost.privateKeyFile", "myprivatekeyfile");
+ brooklynProperties.put("brooklyn.location.localhost.publicKeyFile", "mypublickeyfile");
+ brooklynProperties.put("brooklyn.location.localhost.privateKeyData", "myprivateKeyData");
+ brooklynProperties.put("brooklyn.location.localhost.publicKeyData", "myPublicKeyData");
+ brooklynProperties.put("brooklyn.location.localhost.privateKeyPassphrase", "myprivateKeyPassphrase");
+
+ Map<String, Object> conf = resolve("localhost").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 testTakesLocalhostDeprecatedScopedProperties() {
+ brooklynProperties.put("brooklyn.localhost.privateKeyFile", "myprivatekeyfile");
+ brooklynProperties.put("brooklyn.localhost.publicKeyFile", "mypublickeyfile");
+ brooklynProperties.put("brooklyn.localhost.privateKeyData", "myprivateKeyData");
+ brooklynProperties.put("brooklyn.localhost.publicKeyData", "myPublicKeyData");
+ brooklynProperties.put("brooklyn.localhost.privateKeyPassphrase", "myprivateKeyPassphrase");
+
+ Map<String, Object> conf = resolve("localhost").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 testTakesDeprecatedProperties() {
+ brooklynProperties.put("brooklyn.localhost.private-key-file", "myprivatekeyfile");
+ brooklynProperties.put("brooklyn.localhost.public-key-file", "mypublickeyfile");
+ brooklynProperties.put("brooklyn.localhost.private-key-data", "myprivateKeyData");
+ brooklynProperties.put("brooklyn.localhost.public-key-data", "myPublicKeyData");
+ brooklynProperties.put("brooklyn.localhost.private-key-passphrase", "myprivateKeyPassphrase");
+ Map<String, Object> conf = resolve("localhost").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 testPropertyScopePrescedence() {
+ brooklynProperties.put("brooklyn.location.named.mynamed", "localhost");
+
+ // prefer those in "named" over everything else
+ brooklynProperties.put("brooklyn.location.named.mynamed.privateKeyFile", "privateKeyFile-inNamed");
+ brooklynProperties.put("brooklyn.location.localhost.privateKeyFile", "privateKeyFile-inProviderSpecific");
+ brooklynProperties.put("brooklyn.localhost.privateKeyFile", "privateKeyFile-inGeneric");
+
+ // prefer those in provider-specific over generic
+ brooklynProperties.put("brooklyn.location.localhost.publicKeyFile", "publicKeyFile-inProviderSpecific");
+ brooklynProperties.put("brooklyn.location.publicKeyFile", "publicKeyFile-inGeneric");
+
+ // prefer location-generic if nothing else
+ brooklynProperties.put("brooklyn.location.privateKeyData", "privateKeyData-inGeneric");
+
+ Map<String, Object> conf = resolve("named:mynamed").config().getBag().getAllConfig();
+
+ assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed");
+ assertEquals(conf.get("publicKeyFile"), "publicKeyFile-inProviderSpecific");
+ assertEquals(conf.get("privateKeyData"), "privateKeyData-inGeneric");
+ }
+
+ @Test
+ public void testLocalhostLoads() {
+ Assert.assertTrue(resolve("localhost") instanceof LocalhostMachineProvisioningLocation);
+ }
+
+ @Test
+ public void testThrowsOnInvalid() throws Exception {
+ assertThrowsNoSuchElement("wrongprefix");
+ assertThrowsIllegalArgument("localhost(name=abc"); // no closing bracket
+ assertThrowsIllegalArgument("localhost(name)"); // no value for name
+ assertThrowsIllegalArgument("localhost(name=)"); // no value for name
+ }
+
+
+ @Test
+ public void testAcceptsList() {
+ List<Location> l = getLocationResolver().resolve(ImmutableList.of("localhost"));
+ assertEquals(l.size(), 1, "l="+l);
+ assertTrue(l.get(0) instanceof LocalhostMachineProvisioningLocation, "l="+l);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testRegistryCommaResolution() throws NoMachinesAvailableException {
+ List<Location> l;
+ l = getLocationResolver().resolve(JavaStringEscapes.unwrapJsonishListIfPossible("localhost,localhost,localhost"));
+ assertEquals(l.size(), 3, "l="+l);
+ assertTrue(l.get(0) instanceof LocalhostMachineProvisioningLocation, "l="+l);
+ assertTrue(l.get(1) instanceof LocalhostMachineProvisioningLocation, "l="+l);
+ assertTrue(l.get(2) instanceof LocalhostMachineProvisioningLocation, "l="+l);
+
+ // And check works if comma in brackets
+ l = getLocationResolver().resolve(JavaStringEscapes.unwrapJsonishListIfPossible(
+ "[ \"byon:(hosts=\\\"192.168.0.1\\\",user=bob)\", \"byon:(hosts=\\\"192.168.0.2\\\",user=bob2)\" ]"));
+ assertEquals(l.size(), 2, "l="+l);
+ assertTrue(l.get(0) instanceof FixedListMachineProvisioningLocation, "l="+l);
+ assertTrue(l.get(1) instanceof FixedListMachineProvisioningLocation, "l="+l);
+ assertEquals(((FixedListMachineProvisioningLocation<SshMachineLocation>)l.get(0)).obtain().getUser(), "bob");
+ assertEquals(((FixedListMachineProvisioningLocation<SshMachineLocation>)l.get(1)).obtain().getUser(), "bob2");
+ }
+
+ @Test(expectedExceptions={NoSuchElementException.class})
+ public void testRegistryCommaResolutionInListNotAllowed1() throws NoMachinesAvailableException {
+ // disallowed since 0.7.0
+ getLocationResolver().resolve(ImmutableList.of("localhost,localhost,localhost"));
+ }
+
+ @Test(expectedExceptions={IllegalArgumentException.class})
+ public void testRegistryCommaResolutionInListNotAllowed2() throws NoMachinesAvailableException {
+ // disallowed since 0.7.0
+ // fails because it interprets the entire string as a single spec, which does not parse
+ getLocationResolver().resolve(ImmutableList.of("localhost(),localhost()"));
+ }
+
+ @Test(expectedExceptions={IllegalArgumentException.class})
+ public void testRegistryCommaResolutionInListNotAllowed3() throws NoMachinesAvailableException {
+ // disallowed since 0.7.0
+ // fails because it interprets the entire string as a single spec, which does not parse
+ getLocationResolver().resolve(ImmutableList.of("localhost(name=a),localhost(name=b)"));
+ }
+
+ @Test(expectedExceptions={IllegalArgumentException.class})
+ public void testDoesNotAcceptsListOLists() {
+ ((BasicLocationRegistry)managementContext.getLocationRegistry()).resolve(ImmutableList.of(ImmutableList.of("localhost")));
+ }
+
+ @Test
+ public void testResolvesExplicitName() throws Exception {
+ Location location = resolve("localhost(name=myname)");
+ assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+ assertEquals(location.getDisplayName(), "myname");
+ }
+
+ @Test
+ public void testWithOldStyleColon() throws Exception {
+ Location location = resolve("localhost:(name=myname)");
+ assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+ assertEquals(location.getDisplayName(), "myname");
+ }
+
+ @Test
+ public void testResolvesPropertiesInSpec() throws Exception {
+ LocationInternal location = resolve("localhost(privateKeyFile=myprivatekeyfile,name=myname)");
+ assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+ assertEquals(location.getDisplayName(), "myname");
+ assertEquals(location.config().getBag().getStringKey("privateKeyFile"), "myprivatekeyfile");
+ }
+
+ @Test
+ public void testResolvesDefaultName() throws Exception {
+ Location location = resolve("localhost");
+ assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+ assertEquals(location.getDisplayName(), "localhost");
+
+ Location location2 = resolve("localhost()");
+ assertTrue(location2 instanceof LocalhostMachineProvisioningLocation);
+ assertEquals(location2.getDisplayName(), "localhost");
+ }
+
+ private BasicLocationRegistry getLocationResolver() {
+ return (BasicLocationRegistry) managementContext.getLocationRegistry();
+ }
+
+ private LocationInternal resolve(String val) {
+ Location l = managementContext.getLocationRegistry().resolve(val);
+ Assert.assertNotNull(l);
+ return (LocationInternal) l;
+ }
+
+ private void assertThrowsNoSuchElement(String val) {
+ try {
+ resolve(val);
+ fail();
+ } catch (NoSuchElementException e) {
+ // success
+ }
+
+ // and check the long form returns an Absent (not throwing)
+ Assert.assertTrue(managementContext.getLocationRegistry().resolve(val, false, null).isAbsent());
+ }
+
+ private void assertThrowsIllegalArgument(String val) {
+ try {
+ resolve(val);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // success
+ }
+
+ // and check the long form returns an Absent (not throwing)
+ Assert.assertTrue(managementContext.getLocationRegistry().resolve(val, false, null).isAbsent());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostMachineProvisioningLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostMachineProvisioningLocationTest.java
new file mode 100644
index 0000000..4735350
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostMachineProvisioningLocationTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.core.localhost;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.net.ServerSocket;
+
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.net.Networking;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.core.PortRanges;
+import org.apache.brooklyn.location.geo.HostGeoInfo;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+
+public class LocalhostMachineProvisioningLocationTest {
+
+ private static final Logger log = LoggerFactory.getLogger(LocalhostMachineProvisioningLocationTest.class);
+
+ private LocalManagementContext mgmt;
+
+ @BeforeMethod
+ @AfterClass
+ protected void clearStatics() {
+ LocalhostMachineProvisioningLocation.clearStaticData();
+ }
+
+ @BeforeClass
+ protected void setup() {
+ mgmt = LocalManagementContextForTests.newInstance();
+ }
+
+ @AfterClass
+ protected void teardown() {
+ Entities.destroyAll(mgmt);
+ }
+
+ protected LocalhostMachineProvisioningLocation newLocalhostProvisioner() {
+ return mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+ }
+
+ protected LocalhostMachineProvisioningLocation newLocalhostProvisionerWithAddress(String address) {
+ return mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class)
+ .configure("address", address));
+ }
+
+ @Test
+ public void defaultInvocationCanProvisionALocalhostInstance() throws Exception {
+ LocalhostMachineProvisioningLocation provisioner = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+ SshMachineLocation machine = provisioner.obtain();
+ assertNotNull(machine);
+ assertEquals(machine.getAddress(), Networking.getLocalHost());
+ }
+
+ @Test
+ public void testUsesLocationNameProvided() throws Exception {
+ LocalhostMachineProvisioningLocation provisioner = newLocalhostProvisionerWithAddress("localhost");
+ assertEquals(((SshMachineLocation)provisioner.obtain()).getAddress().getHostName(), "localhost");
+
+ LocalhostMachineProvisioningLocation provisioner2 = newLocalhostProvisionerWithAddress("1.2.3.4");
+ assertEquals(((SshMachineLocation)provisioner2.obtain()).getAddress().getHostName(), "1.2.3.4");
+
+ LocalhostMachineProvisioningLocation provisioner3 = newLocalhostProvisionerWithAddress("127.0.0.1");
+ assertEquals(((SshMachineLocation)provisioner3.obtain()).getAddress().getHostName(), "127.0.0.1");
+ }
+
+ public void provisionWithASpecificNumberOfInstances() throws NoMachinesAvailableException {
+ LocalhostMachineProvisioningLocation provisioner = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class)
+ .configure("count", 2));
+
+ // first machine
+ SshMachineLocation first = provisioner.obtain();
+ assertNotNull(first);
+ assertEquals(first.getAddress(), Networking.getLocalHost());
+
+ // second machine
+ SshMachineLocation second = provisioner.obtain();
+ assertNotNull(second);
+ assertEquals(second.getAddress(), Networking.getLocalHost());
+
+ // third machine - fails
+ try {
+ SshMachineLocation third = provisioner.obtain();
+ fail("did not throw expected exception; got "+third);
+ } catch (NoMachinesAvailableException e) {
+ /* expected */
+ }
+ }
+
+ @Test
+ public void obtainTwoAddressesInRangeThenDontObtain() throws Exception {
+ LocalhostMachineProvisioningLocation p = newLocalhostProvisioner();
+ SshMachineLocation m = p.obtain();
+
+ // Find two ports that are free, rather than risk false-negatives if a port was left open by something else.
+ int start = 48311;
+ while (true) {
+ if (Networking.isPortAvailable(m.getAddress(), start) && Networking.isPortAvailable(m.getAddress(), start+1)) {
+ break;
+ } else {
+ start++;
+ }
+ }
+ PortRange r = PortRanges.fromString(""+start+"-"+(start+1));
+
+ try {
+ int i1 = m.obtainPort(r);
+ Assert.assertEquals(i1, start);
+ int i2 = m.obtainPort(r);
+ Assert.assertEquals(i2, start+1);
+
+ //should fail
+ int i3 = m.obtainPort(r);
+ Assert.assertEquals(i3, -1);
+
+ //releasing and reapplying should succed
+ m.releasePort(i2);
+ int i4 = m.obtainPort(r);
+ Assert.assertEquals(i4, i2);
+
+ } finally {
+ m.releasePort(start);
+ m.releasePort(start+1);
+ }
+ }
+
+ @Test
+ public void obtainLowNumberedPortsAutomatically() throws Exception {
+ LocalhostMachineProvisioningLocation p = newLocalhostProvisioner();
+ SshMachineLocation m = p.obtain();
+ int start = 983; //random rarely used port, not that it matters
+ try {
+ int actual = m.obtainPort(PortRanges.fromInteger(start));
+ Assert.assertEquals(actual, start);
+ } finally {
+ m.releasePort(start);
+ }
+
+ }
+
+ @Test
+ public void obtainPortFailsIfInUse() throws Exception {
+ LocalhostMachineProvisioningLocation p = newLocalhostProvisioner();
+ SshMachineLocation m = p.obtain();
+
+ // Find two ports that are free, rather than risk false-negatives if a port was left open by something else.
+ int start = 48311;
+ while (true) {
+ if (Networking.isPortAvailable(m.getAddress(), start) && Networking.isPortAvailable(m.getAddress(), start+1)) {
+ break;
+ } else {
+ start++;
+ }
+ }
+ PortRange r = PortRanges.fromString(""+start+"-"+(start+1));
+
+ ServerSocket ss = null;
+ try {
+ ss = new ServerSocket(start, 0, m.getAddress());
+ int i1 = m.obtainPort(r);
+ Assert.assertEquals(i1, start+1);
+ } finally {
+ if (ss!=null) ss.close();
+ m.releasePort(start);
+ m.releasePort(start+1);
+ }
+ }
+
+ @Test
+ public void obtainLocationWithGeography() throws Exception {
+ mgmt.getBrooklynProperties().put("brooklyn.location.named.lhx", "localhost");
+ // bogus location so very little chance of it being what maxmind returns!
+ mgmt.getBrooklynProperties().put("brooklyn.location.named.lhx.latitude", 42d);
+ mgmt.getBrooklynProperties().put("brooklyn.location.named.lhx.longitude", -20d);
+ MachineProvisioningLocation<?> p = (MachineProvisioningLocation<?>) mgmt.getLocationRegistry().resolve("named:lhx");
+ SshMachineLocation m = (SshMachineLocation) p.obtain(MutableMap.of());
+ HostGeoInfo geo = HostGeoInfo.fromLocation(m);
+ log.info("Geo info for "+m+" is: "+geo);
+ Assert.assertEquals(geo.latitude, 42d, 0.00001);
+ Assert.assertEquals(geo.longitude, -20d, 0.00001);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostProvisioningAndAccessTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostProvisioningAndAccessTest.java b/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostProvisioningAndAccessTest.java
new file mode 100644
index 0000000..3bb594e
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/core/localhost/LocalhostProvisioningAndAccessTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.core.localhost;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class LocalhostProvisioningAndAccessTest {
+
+ private LocalManagementContext mgmt;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ mgmt = new LocalManagementContext(BrooklynProperties.Factory.newDefault());
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void tearDown(){
+ if (mgmt != null) Entities.destroyAll(mgmt);
+ }
+
+ @Test(groups="Integration")
+ public void testProvisionAndConnect() throws Exception {
+ Location location = mgmt.getLocationRegistry().resolve("localhost");
+ assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+ SshMachineLocation m = ((LocalhostMachineProvisioningLocation)location).obtain();
+ int result = m.execCommands("test", Arrays.asList("echo hello world"));
+ assertEquals(result, 0);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoInfoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoInfoTest.java b/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoInfoTest.java
index 2ca5807..56c1b41 100644
--- a/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoInfoTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoInfoTest.java
@@ -22,7 +22,7 @@ import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.location.basic.SimulatedLocation;
+import org.apache.brooklyn.location.core.SimulatedLocation;
import org.apache.brooklyn.util.collections.MutableMap;
import org.testng.annotations.Test;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoLookupIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoLookupIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoLookupIntegrationTest.java
index c80534f..1990905 100644
--- a/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoLookupIntegrationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/geo/HostGeoLookupIntegrationTest.java
@@ -24,8 +24,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;
-import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.time.Duration;
import com.google.common.base.Objects;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationIntegrationTest.java
new file mode 100644
index 0000000..cd9f724
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationIntegrationTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.ssh;
+
+import java.io.ByteArrayOutputStream;
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.crypto.SecureKeys;
+import org.apache.brooklyn.util.core.internal.ssh.SshTool;
+import org.apache.brooklyn.util.core.internal.ssh.sshj.SshjTool;
+import org.apache.brooklyn.util.core.internal.ssh.sshj.SshjTool.SshjToolBuilder;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Preconditions;
+
+import static org.testng.Assert.assertEquals;
+
+public class SshMachineLocationIntegrationTest {
+
+ protected TestApplication app;
+ protected ManagementContext mgmt;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setup() throws Exception {
+ mgmt = LocalManagementContextForTests.builder(true)
+ .useDefaultProperties()
+ .build();
+ app = TestApplication.Factory.newManagedInstanceForTests(mgmt);
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (mgmt != null) Entities.destroyAll(mgmt);
+ mgmt = null;
+ }
+
+ // Note: requires `named:localhost-passphrase` set up with a key whose passphrase is "localhost"
+ // * create the key with:
+ // ssh-keygen -t rsa -N "brooklyn" -f ~/.ssh/id_rsa_passphrase
+ // ssh-copy-id localhost
+ // * create brooklyn.properties, containing:
+ // brooklyn.location.named.localhost-passphrase=localhost
+ // brooklyn.location.named.localhost-passphrase.privateKeyFile=~/.ssh/id_rsa_passphrase
+ // brooklyn.location.named.localhost-passphrase.privateKeyPassphrase=brooklyn
+ @Test(groups = "Integration")
+ public void testExtractingConnectablePassphraselessKey() throws Exception {
+ LocalhostMachineProvisioningLocation lhp = (LocalhostMachineProvisioningLocation) mgmt.getLocationRegistry().resolve("named:localhost-passphrase", true, null).orNull();
+ Preconditions.checkNotNull(lhp, "This test requires a localhost named location called 'localhost-passphrase' (which should have a passphrase set)");
+ SshMachineLocation sm = lhp.obtain();
+
+ SshjToolBuilder builder = SshjTool.builder().host(sm.getAddress().getHostName()).user(sm.getUser());
+
+ KeyPair data = sm.findKeyPair();
+ if (data!=null) builder.privateKeyData(SecureKeys.toPem(data));
+ String password = sm.findPassword();
+ if (password!=null) builder.password(password);
+ SshjTool tool = builder.build();
+ tool.connect();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int result = tool.execCommands(MutableMap.<String,Object>of("out", out), Arrays.asList("date"));
+ Assert.assertTrue(out.toString().contains(" 20"), "out="+out);
+ assertEquals(result, 0);
+ }
+
+ @Test(groups = "Integration")
+ public void testExecScriptScriptDirFlagIsRespected() throws Exception {
+ // For explanation of (some of) the magic behind this command, see http://stackoverflow.com/a/229606/68898
+ final String command = "if [[ \"$0\" == \"/var/tmp/\"* ]]; then true; else false; fi";
+
+ LocalhostMachineProvisioningLocation lhp = (LocalhostMachineProvisioningLocation) mgmt.getLocationRegistry().resolve("localhost", true, null).orNull();
+ SshMachineLocation sm = lhp.obtain();
+
+ Map<String, Object> props = ImmutableMap.<String, Object>builder()
+ .put(SshTool.PROP_SCRIPT_DIR.getName(), "/var/tmp")
+ .build();
+ int rc = sm.execScript(props, "Test script directory execution", ImmutableList.of(command));
+ assertEquals(rc, 0);
+ }
+
+ @Test(groups = "Integration")
+ public void testLocationScriptDirConfigIsRespected() throws Exception {
+ // For explanation of (some of) the magic behind this command, see http://stackoverflow.com/a/229606/68898
+ final String command = "if [[ \"$0\" == \"/var/tmp/\"* ]]; then true; else false; fi";
+
+ Map<String, Object> locationConfig = ImmutableMap.<String, Object>builder()
+ .put(SshMachineLocation.SCRIPT_DIR.getName(), "/var/tmp")
+ .build();
+
+ LocalhostMachineProvisioningLocation lhp = (LocalhostMachineProvisioningLocation) mgmt.getLocationRegistry().resolve("localhost", locationConfig);
+ SshMachineLocation sm = lhp.obtain();
+
+ int rc = sm.execScript("Test script directory execution", ImmutableList.of(command));
+ assertEquals(rc, 0);
+ }
+
+ @Test(groups = "Integration")
+ public void testMissingLocationScriptDirIsAlsoOkay() throws Exception {
+ final String command = "echo hello";
+
+ Map<String, Object> locationConfig = ImmutableMap.<String, Object>builder()
+// .put(SshMachineLocation.SCRIPT_DIR.getName(), "/var/tmp")
+ .build();
+
+ LocalhostMachineProvisioningLocation lhp = (LocalhostMachineProvisioningLocation) mgmt.getLocationRegistry().resolve("localhost", locationConfig);
+ SshMachineLocation sm = lhp.obtain();
+
+ int rc = sm.execScript("Test script directory execution", ImmutableList.of(command));
+ assertEquals(rc, 0);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationPerformanceTest.java b/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationPerformanceTest.java
new file mode 100644
index 0000000..27ca938
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationPerformanceTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.ssh;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.test.PerformanceTestUtils;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.internal.ssh.SshTool;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.time.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+ * Test the performance of different variants of invoking the sshj tool.
+ *
+ * Intended for human-invocation and inspection, to see which parts are most expensive.
+ */
+public class SshMachineLocationPerformanceTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SshMachineLocationPerformanceTest.class);
+
+ private SshMachineLocation machine;
+ private ListeningExecutorService executor;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ machine = new SshMachineLocation(MutableMap.of("address", "localhost"));
+ executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void afterMethod() throws Exception {
+ if (executor != null) executor.shutdownNow();
+ Streams.closeQuietly(machine);
+ }
+
+ @Test(groups = {"Integration"})
+ public void testConsecutiveSmallCommands() throws Exception {
+ runExecManyCommands(ImmutableList.of("true"), "small-cmd", 10);
+ }
+
+ // Mimics SshSensorAdapter's polling
+ @Test(groups = {"Integration"})
+ public void testConsecutiveSmallCommandsWithCustomStdoutAndErr() throws Exception {
+ final ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+ final ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+
+ Runnable task = new Runnable() {
+ @Override public void run() {
+ machine.execScript(ImmutableMap.of("out", stdout, "err", stderr), "test", ImmutableList.of("true"));
+ }};
+ runMany(task, "small-cmd-custom-stdout", 1, 10);
+ }
+
+ @Test(groups = {"Integration"})
+ public void testConcurrentSmallCommands() throws Exception {
+ runExecManyCommands(ImmutableList.of("true"), "small-cmd", 10, 10);
+ }
+
+ @Test(groups = {"Integration"})
+ public void testConsecutiveBigStdoutCommands() throws Exception {
+ runExecManyCommands(ImmutableList.of("head -c 100000 /dev/urandom"), "big-stdout", 10);
+ }
+
+ @Test(groups = {"Integration"})
+ public void testConsecutiveBigStdinCommands() throws Exception {
+ String bigstr = Identifiers.makeRandomId(100000);
+ runExecManyCommands(ImmutableList.of("echo "+bigstr+" | wc -c"), "big-stdin", 10);
+ }
+
+ @Test(groups = {"Integration"})
+ public void testConsecutiveSmallCommandsWithDifferentProperties() throws Exception {
+ final Map<String, ?> emptyProperties = Collections.emptyMap();
+ final Map<String, ?> customProperties = MutableMap.of(
+ "address", Networking.getLocalHost(),
+ SshTool.PROP_SESSION_TIMEOUT.getName(), 20000,
+ SshTool.PROP_CONNECT_TIMEOUT.getName(), 50000,
+ SshTool.PROP_SCRIPT_HEADER.getName(), "#!/bin/bash");
+
+ Runnable task = new Runnable() {
+ @Override public void run() {
+ if (Math.random() < 0.5) {
+ machine.execScript(emptyProperties, "test", ImmutableList.of("true"));
+ } else {
+ machine.execScript(customProperties, "test", ImmutableList.of("true"));
+ }
+ }};
+ runMany(task, "small-cmd-custom-ssh-properties", 1, 10);
+ }
+
+ private void runExecManyCommands(final List<String> cmds, String context, int iterations) throws Exception {
+ runExecManyCommands(cmds, context, 1, iterations);
+ }
+
+ private void runExecManyCommands(final List<String> cmds, String context, int concurrentRuns, int iterations) throws Exception {
+ Runnable task = new Runnable() {
+ @Override public void run() {
+ execScript(cmds);
+ }};
+ runMany(task, context, concurrentRuns, iterations);
+ }
+
+ private void runMany(final Runnable task, final String context, int concurrentRuns, int iterations) throws Exception {
+ long preCpuTime = PerformanceTestUtils.getProcessCpuTime();
+ Stopwatch stopwatch = Stopwatch.createStarted();
+
+ for (int i = 0; i < iterations; i++) {
+ List<ListenableFuture<?>> futures = Lists.newArrayList();
+ for (int j = 0; j < concurrentRuns; j++) {
+ futures.add(executor.submit(new Runnable() {
+ public void run() {
+ try {
+ task.run();
+ } catch (Exception e) {
+ LOG.error("Error for "+context+", executing "+task, e);
+ throw Throwables.propagate(e);
+ }
+ }}));
+ }
+ Futures.allAsList(futures).get();
+
+ long postCpuTime = PerformanceTestUtils.getProcessCpuTime();
+ long elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);
+ double fractionCpu = (elapsedTime > 0) ? ((double)postCpuTime-preCpuTime) / TimeUnit.MILLISECONDS.toNanos(elapsedTime) : -1;
+ LOG.info("Executing {}; completed {}; took {}; fraction cpu {}",
+ new Object[] {context, (i+1), Time.makeTimeStringRounded(elapsedTime), fractionCpu});
+ }
+ }
+
+ private int execScript(List<String> cmds) {
+ return machine.execScript("mysummary", cmds);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationReuseIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationReuseIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationReuseIntegrationTest.java
new file mode 100644
index 0000000..e129dc1
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/ssh/SshMachineLocationReuseIntegrationTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.ssh;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.internal.ssh.SshTool;
+import org.apache.brooklyn.util.core.internal.ssh.sshj.SshjTool;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.time.Duration;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Tests the re-use of SshTools in SshMachineLocation
+ */
+public class SshMachineLocationReuseIntegrationTest {
+
+ public static class RecordingSshjTool extends SshjTool {
+ public static final AtomicBoolean forbidden = new AtomicBoolean(false);
+ public static final AtomicInteger connectionCount = new AtomicInteger(0);
+ public static final AtomicInteger disconnectionCount = new AtomicInteger();
+
+ public RecordingSshjTool(Map<String, ?> map) {
+ super(map);
+ }
+
+ @Override
+ public void connect() {
+ if (forbidden.get()) throw new IllegalStateException("forbidden at this time");
+ connectionCount.incrementAndGet();
+ super.connect();
+ }
+
+ @Override
+ public void disconnect() {
+ disconnectionCount.incrementAndGet();
+ super.disconnect();
+ }
+
+ public static void reset() {
+ forbidden.set(false);
+ connectionCount.set(0);
+ disconnectionCount.set(0);
+ }
+
+ @Override
+ public int execCommands(Map<String, ?> props, List<String> commands, Map<String, ?> env) {
+ if (forbidden.get()) throw new IllegalStateException("forbidden at this time");
+ return super.execCommands(props, commands, env);
+ }
+
+ @Override
+ public int execScript(Map<String, ?> props, List<String> commands, Map<String, ?> env) {
+ if (forbidden.get()) throw new IllegalStateException("forbidden at this time");
+ return super.execScript(props, commands, env);
+ }
+
+ @Override
+ public int execShellDirect(Map<String, ?> props, List<String> commands, Map<String, ?> env) {
+ if (forbidden.get()) throw new IllegalStateException("forbidden at this time");
+ return super.execShellDirect(props, commands, env);
+ }
+ }
+
+ private SshMachineLocation host;
+ private LocalManagementContext managementContext;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ managementContext = new LocalManagementContext();
+ host = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .configure("address", Networking.getLocalHost())
+ .configure(SshTool.PROP_TOOL_CLASS, RecordingSshjTool.class.getName()));
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (host != null) Streams.closeQuietly(host);
+ if (managementContext != null) Entities.destroyAll(managementContext);
+ RecordingSshjTool.reset();
+ }
+
+ @Test(groups = "Integration")
+ public void testBasicReuse() throws Exception {
+ host.execScript("mysummary", ImmutableList.of("exit"));
+ host.execScript("mysummary", ImmutableList.of("exit"));
+ assertEquals(RecordingSshjTool.connectionCount.get(), 1, "Expected one SSH connection to have been recorded");
+ }
+
+ @Test(groups = "Integration")
+ public void testReuseWithInterestingProps() throws Exception {
+ host.execScript(customSshConfigKeys(), "mysummary", ImmutableList.of("exit"));
+ host.execScript(customSshConfigKeys(), "mysummary", ImmutableList.of("exit"));
+ assertEquals(RecordingSshjTool.connectionCount.get(), 1, "Expected one SSH connection to have been recorded");
+ }
+
+ @Test(groups = "Integration")
+ public void testNewConnectionForDifferentProps() throws Exception {
+ host.execScript("mysummary", ImmutableList.of("exit"));
+ host.execScript(customSshConfigKeys(), "mysummary", ImmutableList.of("exit"));
+ assertEquals(RecordingSshjTool.connectionCount.get(), 2, "Expected two SSH connections to have been recorded");
+ }
+
+ @Test(groups = "Integration")
+ public void testSshToolReusedWhenConfigDiffers() throws Exception {
+ Map<String, Object> props = customSshConfigKeys();
+ host.execScript(props, "mysummary", ImmutableList.of("exit"));
+
+ // Use another output stream for second request
+ props.put(SshTool.PROP_SCRIPT_HEADER.getName(), "#!/bin/bash -e\n");
+ host.execScript(props, "mysummary", ImmutableList.of("exit"));
+ assertEquals(RecordingSshjTool.connectionCount.get(), 1, "Expected one SSH connection to have been recorded even though out script header differed.");
+ }
+
+ @Test(groups = "Integration")
+ public void testSshCacheExpiresEvenIfNotUsed() throws Exception {
+ SshMachineLocation host2 = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .configure("address", InetAddress.getLocalHost())
+ .configure(SshMachineLocation.SSH_CACHE_EXPIRY_DURATION, Duration.ONE_SECOND)
+ .configure(SshTool.PROP_TOOL_CLASS, RecordingSshjTool.class.getName()));
+
+ Map<String, Object> props = customSshConfigKeys();
+ host2.execScript(props, "mysummary", ImmutableList.of("exit"));
+
+ Asserts.succeedsEventually(new Runnable() {
+ @Override public void run() {
+ assertEquals(RecordingSshjTool.disconnectionCount.get(), 1);
+ }});
+ }
+
+ public Map<String, Object> customSshConfigKeys() throws UnknownHostException {
+ return MutableMap.<String, Object>of(
+ "address", Networking.getLocalHost(),
+ SshTool.PROP_SESSION_TIMEOUT.getName(), 20000,
+ SshTool.PROP_CONNECT_TIMEOUT.getName(), 50000,
+ SshTool.PROP_SCRIPT_HEADER.getName(), "#!/bin/bash");
+ }
+}