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:09:27 UTC
[09/72] [abbrv] incubator-brooklyn git commit: BROOKLYN-162 - apply
org.apache package prefix to software-base, tidying package names,
and moving a few sensory things to core
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppTest.java
new file mode 100644
index 0000000..e4d30c3
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppTest.java
@@ -0,0 +1,352 @@
+/*
+ * 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.entity.java;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.net.MalformedURLException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.internal.EntityLocal;
+import org.apache.brooklyn.api.sensor.SensorEvent;
+import org.apache.brooklyn.api.sensor.SensorEventListener;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.java.JavaAppUtils;
+import org.apache.brooklyn.entity.java.UsesJava;
+import org.apache.brooklyn.entity.java.UsesJmx;
+import org.apache.brooklyn.entity.java.VanillaJavaApp;
+import org.apache.brooklyn.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.sensor.feed.jmx.JmxHelper;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.core.crypto.FluentKeySigner;
+import org.apache.brooklyn.util.core.crypto.SecureKeys;
+import org.apache.brooklyn.util.crypto.SslTrustUtils;
+import org.apache.brooklyn.util.jmx.jmxmp.JmxmpAgent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.PortRanges;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+
+public class VanillaJavaAppTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(VanillaJavaAppTest.class);
+
+ private static final long TIMEOUT_MS = 10*1000;
+
+ // Static attributes such as number of processors and start time are only polled every 60 seconds
+ // so if they are not immediately available, it will be 60 seconds before they are polled again
+ private static final Object LONG_TIMEOUT_MS = 61*1000;
+
+ private static String BROOKLYN_THIS_CLASSPATH = null;
+ private static Class<?> MAIN_CLASS = ExampleVanillaMain.class;
+ private static Class<?> MAIN_CPU_HUNGRY_CLASS = ExampleVanillaMainCpuHungry.class;
+
+ private TestApplication app;
+ private LocalhostMachineProvisioningLocation loc;
+
+ @BeforeMethod(alwaysRun = true)
+ public void setUp() throws Exception {
+ if (BROOKLYN_THIS_CLASSPATH==null) {
+ BROOKLYN_THIS_CLASSPATH = ResourceUtils.create(MAIN_CLASS).getClassLoaderDir();
+ }
+ app = TestApplication.Factory.newManagedInstanceForTests();
+ loc = app.newLocalhostProvisioningLocation(MutableMap.of("address", "localhost"));
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void tearDown() throws Exception {
+ if (app != null) Entities.destroyAll(app.getManagementContext());
+ }
+
+ @Test
+ public void testReadsConfigFromFlags() throws Exception {
+ final VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+ .configure("main", "my.Main").configure("classpath", ImmutableList.of("c1", "c2"))
+ .configure("args", ImmutableList.of("a1", "a2")));
+
+ assertEquals(javaProcess.getMainClass(), "my.Main");
+ assertEquals(javaProcess.getClasspath(), ImmutableList.of("c1","c2"));
+ assertEquals(javaProcess.getConfig(VanillaJavaApp.ARGS), ImmutableList.of("a1", "a2"));
+ }
+
+ @Test(groups={"WIP", "Integration"})
+ public void testJavaSystemProperties() throws Exception {
+ final VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+ .configure("main", "my.Main").configure("classpath", ImmutableList.of("c1", "c2"))
+ .configure("args", ImmutableList.of("a1", "a2")));
+ ((EntityLocal)javaProcess).setConfig(UsesJava.JAVA_SYSPROPS, ImmutableMap.of("fooKey", "fooValue", "barKey", "barValue"));
+ // TODO: how to test: launch standalone app that outputs system properties to stdout? Probe via JMX?
+ }
+
+ @Test(groups={"Integration"})
+ public void testStartsAndStops() throws Exception {
+ String main = MAIN_CLASS.getCanonicalName();
+ final VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+ .configure("main", main).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH))
+ .configure("args", ImmutableList.of()));
+ app.start(ImmutableList.of(loc));
+ assertEquals(javaProcess.getAttribute(VanillaJavaApp.SERVICE_STATE_ACTUAL), Lifecycle.RUNNING);
+
+ javaProcess.stop();
+ assertEquals(javaProcess.getAttribute(VanillaJavaApp.SERVICE_STATE_ACTUAL), Lifecycle.STOPPED);
+ }
+
+ @Test(groups={"Integration"})
+ public void testHasJvmMXBeanSensorVals() throws Exception {
+ String main = MAIN_CLASS.getCanonicalName();
+ final VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+ .configure("main", main).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH))
+ .configure("args", ImmutableList.of()));
+ app.start(ImmutableList.of(loc));
+
+ // Memory MXBean
+ Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+ public void run() {
+ assertNotNull(javaProcess.getAttribute(VanillaJavaApp.NON_HEAP_MEMORY_USAGE));
+ long init = javaProcess.getAttribute(VanillaJavaApp.INIT_HEAP_MEMORY);
+ long used = javaProcess.getAttribute(VanillaJavaApp.USED_HEAP_MEMORY);
+ long committed = javaProcess.getAttribute(VanillaJavaApp.COMMITTED_HEAP_MEMORY);
+ long max = javaProcess.getAttribute(VanillaJavaApp.MAX_HEAP_MEMORY);
+
+ assertNotNull(used);
+ assertNotNull(init);
+ assertNotNull(committed);
+ assertNotNull(max);
+ assertTrue(init <= max, String.format("init %d > max %d heap memory", init, max));
+ assertTrue(used <= committed, String.format("used %d > committed %d heap memory", used, committed));
+ assertTrue(committed <= max, String.format("committed %d > max %d heap memory", committed, max));
+ }});
+
+ // Threads MX Bean
+ Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+ public void run() {
+ long current = javaProcess.getAttribute(VanillaJavaApp.CURRENT_THREAD_COUNT);
+ long peak = javaProcess.getAttribute(VanillaJavaApp.PEAK_THREAD_COUNT);
+
+ assertNotNull(current);
+ assertNotNull(peak);
+ assertTrue(current <= peak, String.format("current %d > peak %d thread count", current, peak));
+ }});
+
+ // Runtime MX Bean
+ Asserts.succeedsEventually(MutableMap.of("timeout", LONG_TIMEOUT_MS), new Runnable() {
+ public void run() {
+ assertNotNull(javaProcess.getAttribute(VanillaJavaApp.START_TIME));
+ assertNotNull(javaProcess.getAttribute(VanillaJavaApp.UP_TIME));
+ }});
+
+ // Operating System MX Bean
+ Asserts.succeedsEventually(MutableMap.of("timeout", LONG_TIMEOUT_MS), new Runnable() {
+ public void run() {
+ assertNotNull(javaProcess.getAttribute(VanillaJavaApp.PROCESS_CPU_TIME));
+ assertNotNull(javaProcess.getAttribute(VanillaJavaApp.SYSTEM_LOAD_AVERAGE));
+ assertNotNull(javaProcess.getAttribute(VanillaJavaApp.AVAILABLE_PROCESSORS));
+ assertNotNull(javaProcess.getAttribute(VanillaJavaApp.TOTAL_PHYSICAL_MEMORY_SIZE));
+ assertNotNull(javaProcess.getAttribute(VanillaJavaApp.FREE_PHYSICAL_MEMORY_SIZE));
+ }});
+ // TODO work on providing useful metrics from garbage collector MX Bean
+ // assertNotNull(javaProcess.getAttribute(VanillaJavaApp.GARBAGE_COLLECTION_TIME)) TODO: work on providing this
+ }
+
+ @Test(groups={"Integration"})
+ public void testJvmMXBeanProcessCpuTimeGivesNonZeroPercentage() throws Exception {
+ String main = MAIN_CPU_HUNGRY_CLASS.getCanonicalName();
+ final VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+ .configure("main", main).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH))
+ .configure("args", ImmutableList.of()));
+ app.start(ImmutableList.of(loc));
+
+ JavaAppUtils.connectJavaAppServerPolicies((EntityLocal)javaProcess);
+
+ final List<Double> fractions = new CopyOnWriteArrayList<Double>();
+ app.getManagementContext().getSubscriptionManager().subscribe(javaProcess, VanillaJavaApp.PROCESS_CPU_TIME_FRACTION_LAST, new SensorEventListener<Double>() {
+ public void onEvent(SensorEvent<Double> event) {
+ fractions.add(event.getValue());
+ }});
+
+ // Expect non-trivial load to be generated by the process.
+ // Expect load to be in the right order of magnitude (to ensure we haven't got a decimal point in the wrong place etc);
+ // But with multi-core could get big number; and on jenkins@releng3 we once saw [11.9, 0.6, 0.5]!
+ Asserts.succeedsEventually(new Runnable() {
+ public void run() {
+ Iterable<Double> nonTrivialFractions = Iterables.filter(fractions, new Predicate<Double>() {
+ public boolean apply(Double input) {
+ return input > 0.01;
+ }});
+ assertTrue(Iterables.size(nonTrivialFractions) > 3, "fractions="+fractions);
+ }});
+
+ Iterable<Double> tooBigFractions = Iterables.filter(fractions, new Predicate<Double>() {
+ public boolean apply(Double input) {
+ return input > 50;
+ }});
+ assertTrue(Iterables.isEmpty(tooBigFractions), "fractions="+fractions);
+
+ Iterable<Double> ballparkRightFractions = Iterables.filter(fractions, new Predicate<Double>() {
+ public boolean apply(Double input) {
+ return input > 0.01 && input < 4;
+ }});
+ assertTrue(Iterables.size(ballparkRightFractions) >= (fractions.size() / 2), "fractions="+fractions);
+
+ LOG.info("VanillaJavaApp->ExampleVanillaMainCpuHuntry: ProcessCpuTime fractions="+fractions);
+ }
+
+ @Test(groups={"Integration"})
+ public void testStartsWithJmxPortSpecifiedInConfig() throws Exception {
+ int port = 53405;
+ String main = MAIN_CLASS.getCanonicalName();
+ VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+ .configure("main", main).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH))
+ .configure("args", ImmutableList.of()));
+ ((EntityLocal)javaProcess).setConfig(UsesJmx.JMX_PORT, PortRanges.fromInteger(port));
+ app.start(ImmutableList.of(loc));
+
+ assertEquals(javaProcess.getAttribute(UsesJmx.JMX_PORT), (Integer)port);
+ }
+
+ // FIXME Way test was written requires JmxSensorAdapter; need to rewrite...
+ @Test(groups={"Integration", "WIP"})
+ public void testStartsWithSecureJmxPortSpecifiedInConfig() throws Exception {
+ int port = 53406;
+ String main = MAIN_CLASS.getCanonicalName();
+ final VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+ .configure("main", main).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH))
+ .configure("args", ImmutableList.of()));
+ ((EntityLocal)javaProcess).setConfig(UsesJmx.JMX_PORT, PortRanges.fromInteger(port));
+ ((EntityLocal)javaProcess).setConfig(UsesJmx.JMX_SSL_ENABLED, true);
+
+ app.start(ImmutableList.of(loc));
+ // will fail above if JMX can't connect, but also do some add'l checks
+
+ assertEquals(javaProcess.getAttribute(UsesJmx.JMX_PORT), (Integer)port);
+
+ // good key+cert succeeds
+ new AsserterForJmxConnection(javaProcess)
+ .customizeSocketFactory(null, null)
+ .connect();
+
+ // bad cert fails
+ Asserts.assertFails(new Callable<Void>() {
+ public Void call() throws Exception {
+ new AsserterForJmxConnection(javaProcess)
+ .customizeSocketFactory(null, new FluentKeySigner("cheater").newCertificateFor("jmx-access-key", SecureKeys.newKeyPair()))
+ .connect();
+ return null;
+ }});
+
+ // bad key fails
+ Asserts.assertFails(new Callable<Void>() {
+ public Void call() throws Exception {
+ new AsserterForJmxConnection(javaProcess)
+ .customizeSocketFactory(SecureKeys.newKeyPair().getPrivate(), null)
+ .connect();
+ return null;
+ }});
+
+ // bad profile fails
+ Asserts.assertFails(new Callable<Void>() {
+ public Void call() throws Exception {
+ AsserterForJmxConnection asserter = new AsserterForJmxConnection(javaProcess);
+ asserter.putEnv("jmx.remote.profiles", JmxmpAgent.TLS_JMX_REMOTE_PROFILES);
+ asserter.customizeSocketFactory(SecureKeys.newKeyPair().getPrivate(), null)
+ .connect();
+ return null;
+ }});
+ }
+
+ private static class AsserterForJmxConnection {
+ final VanillaJavaApp entity;
+ final JMXServiceURL url;
+ final Map<String,Object> env;
+
+ @SuppressWarnings("unchecked")
+ public AsserterForJmxConnection(VanillaJavaApp e) throws MalformedURLException {
+ this.entity = e;
+
+ JmxHelper jmxHelper = new JmxHelper((EntityLocal)entity);
+ this.url = new JMXServiceURL(jmxHelper.getUrl());
+ this.env = Maps.newLinkedHashMap(jmxHelper.getConnectionEnvVars());
+ }
+
+ public JMXServiceURL getJmxUrl() throws MalformedURLException {
+ return url;
+ }
+
+ public void putEnv(String key, Object val) {
+ env.put(key, val);
+ }
+
+ public AsserterForJmxConnection customizeSocketFactory(PrivateKey customKey, Certificate customCert) throws Exception {
+ PrivateKey key = (customKey == null) ? entity.getConfig(UsesJmx.JMX_SSL_ACCESS_KEY) : customKey;
+ Certificate cert = (customCert == null) ? entity.getConfig(UsesJmx.JMX_SSL_ACCESS_CERT) : customCert;
+
+ KeyStore ks = SecureKeys.newKeyStore();
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ if (key!=null) {
+ ks.setKeyEntry("brooklyn-jmx-access", key, "".toCharArray(), new Certificate[] {cert});
+ }
+ kmf.init(ks, "".toCharArray());
+
+ TrustManager tms =
+ // TODO use root cert for trusting server
+ //trustStore!=null ? SecureKeys.getTrustManager(trustStore) :
+ SslTrustUtils.TRUST_ALL;
+
+ SSLContext ctx = SSLContext.getInstance("TLSv1");
+ ctx.init(kmf.getKeyManagers(), new TrustManager[] {tms}, null);
+ SSLSocketFactory ssf = ctx.getSocketFactory();
+ env.put(JmxmpAgent.TLS_SOCKET_FACTORY_PROPERTY, ssf);
+
+ return this;
+ }
+
+ public JMXConnector connect() throws Exception {
+ return JMXConnectorFactory.connect(getJmxUrl(), env);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/machine/MachineEntityEc2LiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/MachineEntityEc2LiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/MachineEntityEc2LiveTest.java
new file mode 100644
index 0000000..9c4e571
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/MachineEntityEc2LiveTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.entity.machine;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.entity.AbstractEc2LiveTest;
+import org.apache.brooklyn.entity.machine.MachineEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class MachineEntityEc2LiveTest extends AbstractEc2LiveTest {
+
+ @Override
+ protected void doTest(Location loc) throws Exception {
+ final MachineEntity server = app.createAndManageChild(EntitySpec.create(MachineEntity.class));
+
+ app.start(ImmutableList.of(loc));
+
+ Asserts.succeedsEventually(new Runnable() {
+ @Override public void run() {
+ assertNotNull(server.getAttribute(MachineEntity.UPTIME));
+ assertNotNull(server.getAttribute(MachineEntity.LOAD_AVERAGE));
+ assertNotNull(server.getAttribute(MachineEntity.CPU_USAGE));
+ assertNotNull(server.getAttribute(MachineEntity.FREE_MEMORY));
+ assertNotNull(server.getAttribute(MachineEntity.TOTAL_MEMORY));
+ assertNotNull(server.getAttribute(MachineEntity.USED_MEMORY));
+ }});
+
+ String result = server.execCommand("MY_ENV=myval && echo start $MY_ENV");
+ assertTrue(result.contains("start myval"), "result="+result);
+ }
+
+ @Test(enabled=false)
+ public void testDummy() {} // Convince testng IDE integration that this really does have test methods
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/machine/MachineEntityRebindTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/MachineEntityRebindTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/MachineEntityRebindTest.java
new file mode 100644
index 0000000..cda1309
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/MachineEntityRebindTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.entity.machine;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixtureWithApp;
+import org.apache.brooklyn.entity.core.Attributes;
+import org.apache.brooklyn.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class MachineEntityRebindTest extends RebindTestFixtureWithApp {
+
+ @Test(groups = "Integration")
+ public void testRebindToMachineEntity() throws Exception {
+ EmptySoftwareProcess machine = origApp.createAndManageChild(EntitySpec.create(EmptySoftwareProcess.class));
+ origApp.start(ImmutableList.of(origManagementContext.getLocationRegistry().resolve("localhost")));
+ EntityTestUtils.assertAttributeEqualsEventually(machine, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+ rebind(false);
+ Entity machine2 = newManagementContext.getEntityManager().getEntity(machine.getId());
+ EntityTestUtils.assertAttributeEqualsEventually(machine2, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java
new file mode 100644
index 0000000..c31e458
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.entity.machine.pool;
+
+import static org.testng.Assert.fail;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+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.Attributes;
+import org.apache.brooklyn.entity.core.BrooklynConfigKeys;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.entity.machine.pool.ServerPool;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public abstract class AbstractServerPoolTest {
+
+ // Note not extending BrooklynAppUnitTestSupport because sub-classes of this are for live and for unit tests.
+ // Instead, we have to repeat that logic for setting SKIP_ON_BOX_BASE_DIR_RESOLUTION
+
+ private static final int DEFAULT_POOL_SIZE = 3;
+
+ protected Location location;
+ protected ManagementContext mgmt;
+ protected TestApplication poolApp;
+ protected ServerPool pool;
+ private List<TestApplication> createdApps = Lists.newLinkedList();
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ createdApps.clear();
+ mgmt = createManagementContext();
+ location = createLocation();
+ EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class)
+ .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution());
+ poolApp = ApplicationBuilder.newManagedApp(appSpec, mgmt);
+
+ pool = poolApp.createAndManageChild(EntitySpec.create(ServerPool.class)
+ .configure(ServerPool.INITIAL_SIZE, getInitialPoolSize())
+ .configure(ServerPool.MEMBER_SPEC, EntitySpec.create(EmptySoftwareProcess.class)));
+ poolApp.start(ImmutableList.of(location));
+ EntityTestUtils.assertAttributeEqualsEventually(pool, Attributes.SERVICE_UP, true);
+ assertAvailableCountEventuallyEquals(getInitialPoolSize());
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ // Kills the apps before terminating the pool
+ for (TestApplication app : createdApps) {
+ Entities.destroy(app);
+ }
+ if (mgmt != null) {
+ Entities.destroyAll(mgmt);
+ mgmt = null;
+ }
+ }
+
+ protected int getInitialPoolSize() {
+ return DEFAULT_POOL_SIZE;
+ }
+
+ protected ManagementContext createManagementContext() {
+ return new LocalManagementContextForTests();
+ }
+
+ protected boolean shouldSkipOnBoxBaseDirResolution() {
+ return true;
+ }
+
+ /** @return Creates a LocalhostMachineProvisioningLocation */
+ protected Location createLocation() {
+ return mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+ }
+
+ protected void assertNoMachinesAvailableForApp(TestApplication app) {
+ try {
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ fail("Expected exception when starting app with too many entities for pool");
+ } catch (Exception e) {
+ Throwable t = Exceptions.getFirstThrowableOfType(e, NoMachinesAvailableException.class);
+ if (t == null) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ protected void assertAvailableCountEventuallyEquals(int count) {
+ assertAvailableCountEventuallyEquals(pool, count);
+ }
+
+ protected void assertAvailableCountEventuallyEquals(ServerPool pool, int count) {
+ EntityTestUtils.assertAttributeEqualsEventually(pool, ServerPool.AVAILABLE_COUNT, count);
+ }
+
+ protected void assertClaimedCountEventuallyEquals(int count) {
+ assertClaimedCountEventuallyEquals(pool, count);
+ }
+
+ protected void assertClaimedCountEventuallyEquals(ServerPool pool, Integer count) {
+ EntityTestUtils.assertAttributeEqualsEventually(pool, ServerPool.CLAIMED_COUNT, count);
+ }
+
+ protected TestApplication createAppWithChildren(int numChildren) {
+ if (numChildren < 0) fail("Invalid number of children for app: " + numChildren);
+ EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class)
+ .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution());
+ TestApplication app = ApplicationBuilder.newManagedApp(appSpec, mgmt);
+ while (numChildren-- > 0) {
+ app.createAndManageChild(EntitySpec.create(EmptySoftwareProcess.class));
+ }
+ createdApps.add(app);
+ return app;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLiveTest.java
new file mode 100644
index 0000000..c1e1f18
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLiveTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.entity.machine.pool;
+
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Attributes;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.testng.annotations.Test;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableList;
+
+public class ServerPoolLiveTest extends AbstractServerPoolTest {
+
+ public static final String PROVIDER = "softlayer";
+
+ protected BrooklynProperties brooklynProperties;
+
+ @Override
+ protected Location createLocation() {
+ // Image: {id=CENTOS_6_64, providerId=CENTOS_6_64, os={family=centos, version=6.5, description=CentOS / CentOS / 6.5-64 LAMP for Bare Metal, is64Bit=true}, description=CENTOS_6_64, status=AVAILABLE, loginUser=root}
+ Map<String, ?> allFlags = MutableMap.<String, Object>builder()
+ .put("provider", PROVIDER)
+ .put("tags", ImmutableList.of(getClass().getName()))
+ .put("vmNameMaxLength", 30)
+ .put("imageId", "CENTOS_6_64")
+ .build();
+ return mgmt.getLocationRegistry().resolve(PROVIDER, allFlags);
+ }
+
+ @Override
+ protected ManagementContext createManagementContext() {
+ String[] propsToRemove = new String[]{"imageId", "imageDescriptionRegex", "imageNameRegex", "inboundPorts", "hardwareId", "minRam"};
+
+ // Don't let any defaults from brooklyn.properties (except credentials) interfere with test
+ brooklynProperties = BrooklynProperties.Factory.newDefault();
+ for (String propToRemove : propsToRemove) {
+ for (String propVariant : ImmutableList.of(propToRemove, CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propToRemove))) {
+ brooklynProperties.remove("brooklyn.locations.jclouds." + PROVIDER + "." + propVariant);
+ brooklynProperties.remove("brooklyn.locations." + propVariant);
+ brooklynProperties.remove("brooklyn.jclouds." + PROVIDER + "." + propVariant);
+ brooklynProperties.remove("brooklyn.jclouds." + propVariant);
+ }
+ }
+
+ // Also removes scriptHeader (e.g. if doing `. ~/.bashrc` and `. ~/.profile`, then that can cause "stdin: is not a tty")
+ brooklynProperties.remove("brooklyn.ssh.config.scriptHeader");
+ return new LocalManagementContextForTests(brooklynProperties);
+ }
+
+ protected boolean shouldSkipOnBoxBaseDirResolution() {
+ return false;
+ }
+
+ @Override
+ protected int getInitialPoolSize() {
+ return 1;
+ }
+
+ @Test(groups = "Live")
+ public void testAppCanBeDeployedToPool() {
+ TestApplication app = createAppWithChildren(1);
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertTrue(app.getAttribute(Attributes.SERVICE_UP));
+ for (Entity child : app.getChildren()) {
+ assertTrue(child.getAttribute(Attributes.SERVICE_UP));
+ }
+ TestApplication app2 = createAppWithChildren(1);
+ assertNoMachinesAvailableForApp(app2);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.java
new file mode 100644
index 0000000..fb3974b
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.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.entity.machine.pool;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+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.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.entity.machine.pool.ServerPool;
+import org.apache.brooklyn.entity.machine.pool.ServerPoolLocation;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.util.collections.MutableMap;
+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;
+
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.dynamic.DynamicLocation;
+
+public class ServerPoolLocationResolverTest {
+
+ private LocalManagementContext managementContext;
+ private Entity locationOwner;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ managementContext = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty());
+ TestApplication t = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
+ locationOwner = t.createAndManageChild(EntitySpec.create(ServerPool.class)
+ .configure(ServerPool.INITIAL_SIZE, 0)
+ .configure(ServerPool.MEMBER_SPEC, EntitySpec.create(EmptySoftwareProcess.class)));
+ Location poolLocation = managementContext.getLocationManager()
+ .createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+ t.start(ImmutableList.of(poolLocation));
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (managementContext != null) Entities.destroyAll(managementContext);
+ }
+
+ @Test
+ public void testResolve() {
+ ServerPoolLocation location = resolve("pool:" + locationOwner.getId());
+ assertEquals(location.getOwner().getId(), locationOwner.getId());
+ }
+
+ @Test
+ public void testSetsDisplayName() {
+ ServerPoolLocation location = resolve("pool:" + locationOwner.getId() + ":(displayName=xyz)");
+ assertEquals(location.getDisplayName(), "xyz");
+ }
+
+ private ServerPoolLocation resolve(String val) {
+ Map<String, Object> flags = MutableMap.<String, Object>of(DynamicLocation.OWNER.getName(), locationOwner);
+ Location l = managementContext.getLocationRegistry().resolve(val, flags);
+ Assert.assertNotNull(l);
+ return (ServerPoolLocation) l;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolRebindTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolRebindTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolRebindTest.java
new file mode 100644
index 0000000..2de22bb
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolRebindTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.entity.machine.pool;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.mgmt.rebind.RebindOptions;
+import org.apache.brooklyn.core.mgmt.rebind.RebindTestUtils;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Attributes;
+import org.apache.brooklyn.entity.machine.pool.ServerPool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Optional;
+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 ServerPoolRebindTest extends AbstractServerPoolTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ServerPoolRebindTest.class);
+ private ClassLoader classLoader = getClass().getClassLoader();
+ private File mementoDir;
+
+ @Override
+ protected ManagementContext createManagementContext() {
+ mementoDir = Files.createTempDir();
+ return RebindTestUtils.newPersistingManagementContext(mementoDir, classLoader);
+ }
+
+ @Override
+ @AfterMethod(alwaysRun = true)
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mementoDir != null) RebindTestUtils.deleteMementoDir(mementoDir);
+ }
+
+ private Collection<Application> rebind(TestApplication app) throws Exception {
+ LOG.info("Rebind start");
+ RebindTestUtils.waitForPersisted(app);
+ ((LocalManagementContext) app.getManagementContext()).terminate();
+ Collection<Application> r = RebindTestUtils.rebindAll(RebindOptions.create().mementoDir(mementoDir).classLoader(classLoader));
+ LOG.info("Rebind complete");
+ return r;
+ }
+
+ @Test(enabled = false)
+ public void testRebindingToPool() throws Exception {
+ TestApplication app = createAppWithChildren(1);
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertTrue(app.getAttribute(Attributes.SERVICE_UP));
+ assertAvailableCountEventuallyEquals(pool, getInitialPoolSize() - 1);
+ assertClaimedCountEventuallyEquals(pool, 1);
+
+ Collection<Application> reboundApps = rebind(poolApp);
+ ServerPool reboundPool = null;
+ for (Application reboundApp : reboundApps) {
+ Optional<Entity> np = Iterables.tryFind(reboundApp.getChildren(), Predicates.instanceOf(ServerPool.class));
+ if (np.isPresent()) {
+ mgmt = reboundApp.getManagementContext();
+ reboundPool = (ServerPool) np.get();
+ break;
+ }
+ }
+
+ assertNotNull(reboundPool, "No app in rebound context has " + ServerPool.class.getName() +
+ " child. Apps: " + reboundApps);
+ assertNotNull(reboundPool.getDynamicLocation());
+ assertTrue(reboundPool.getAttribute(Attributes.SERVICE_UP));
+ assertAvailableCountEventuallyEquals(reboundPool, getInitialPoolSize() - 1);
+ assertClaimedCountEventuallyEquals(reboundPool, 1);
+
+ TestApplication app2 = createAppWithChildren(1);
+ app2.start(ImmutableList.of(reboundPool.getDynamicLocation()));
+ assertTrue(app2.getAttribute(Attributes.SERVICE_UP));
+ assertAvailableCountEventuallyEquals(reboundPool, getInitialPoolSize() - 2);
+ assertClaimedCountEventuallyEquals(reboundPool, 2);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java
new file mode 100644
index 0000000..9d9b3a2
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.entity.machine.pool;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Attributes;
+import org.apache.brooklyn.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.entity.machine.pool.ServerPoolImpl;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation.LocalhostMachine;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class ServerPoolTest extends AbstractServerPoolTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ServerPoolTest.class);
+
+ @Test
+ public void testAppCanBeDeployedToServerPool() {
+ TestApplication app = createAppWithChildren(1);
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertTrue(app.getAttribute(Attributes.SERVICE_UP));
+ for (Entity child : app.getChildren()) {
+ assertTrue(child.getAttribute(Attributes.SERVICE_UP));
+ }
+ }
+
+ @Test
+ public void testFailureWhenNotEnoughServersAvailable() {
+ TestApplication app = createAppWithChildren(getInitialPoolSize() + 1);
+ assertNoMachinesAvailableForApp(app);
+ EntityTestUtils.assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE);
+ }
+
+ @Test
+ public void testDeployReleaseDeploy() {
+ TestApplication app = createAppWithChildren(getInitialPoolSize());
+ TestApplication app2 = createAppWithChildren(1);
+
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ EntityTestUtils.assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, true);
+ assertAvailableCountEventuallyEquals(0);
+ assertNoMachinesAvailableForApp(app2);
+
+ app.stop();
+ assertFalse(app.getAttribute(Attributes.SERVICE_UP));
+ assertAvailableCountEventuallyEquals(getInitialPoolSize());
+
+ app2.start(ImmutableList.of(pool.getDynamicLocation()));
+ EntityTestUtils.assertAttributeEqualsEventually(app2, Attributes.SERVICE_UP, true);
+
+ assertAvailableCountEventuallyEquals(getInitialPoolSize() - 1);
+ assertClaimedCountEventuallyEquals(1);
+ }
+
+ @Test
+ public void testResizingPoolUp() {
+ TestApplication app = createAppWithChildren(getInitialPoolSize());
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertTrue(app.getAttribute(Attributes.SERVICE_UP));
+
+ TestApplication app2 = createAppWithChildren(1);
+ assertNoMachinesAvailableForApp(app2);
+
+ pool.resizeByDelta(1);
+
+ assertAvailableCountEventuallyEquals(1);
+
+ assertEquals((int) pool.getCurrentSize(), getInitialPoolSize() + 1);
+ app2.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertTrue(app2.getAttribute(Attributes.SERVICE_UP));
+ }
+
+ @Test
+ public void testResizePoolDownSucceedsWhenEnoughMachinesAreFree() {
+ TestApplication app = createAppWithChildren(1);
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertAvailableCountEventuallyEquals(getInitialPoolSize() - 1);
+
+ pool.resize(1);
+
+ assertAvailableCountEventuallyEquals(0);
+ }
+
+ @Test
+ public void testResizeDownDoesNotReleaseClaimedMachines() {
+ TestApplication app = createAppWithChildren(getInitialPoolSize() - 1);
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertAvailableCountEventuallyEquals(1);
+ assertClaimedCountEventuallyEquals(getInitialPoolSize() - 1);
+
+ LOG.info("Test attempting to resize to 0 members. Should only drop the one available machine.");
+ pool.resize(0);
+
+ assertAvailableCountEventuallyEquals(0);
+ assertEquals(Iterables.size(pool.getMembers()), getInitialPoolSize() - 1);
+ assertAvailableCountEventuallyEquals(0);
+ assertClaimedCountEventuallyEquals(getInitialPoolSize() - 1);
+ }
+
+ @Test
+ public void testCanAddExistingMachinesToPool() {
+ TestApplication app = createAppWithChildren(getInitialPoolSize());
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertAvailableCountEventuallyEquals(0);
+
+ LocalhostMachine loc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachine.class));
+ Entity added = pool.addExistingMachine(loc);
+ assertFalse(added.getConfig(ServerPoolImpl.REMOVABLE));
+ assertAvailableCountEventuallyEquals(1);
+
+ TestApplication app2 = createAppWithChildren(1);
+ app2.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertAvailableCountEventuallyEquals(0);
+ }
+
+ @Test
+ public void testExistingMachinesAreNotRemovedFromThePoolOnShrinkButAreOnStop() {
+ LocalhostMachine loc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachine.class));
+ pool.addExistingMachine(loc);
+ assertAvailableCountEventuallyEquals(getInitialPoolSize() + 1);
+ pool.resize(0);
+ assertAvailableCountEventuallyEquals(1);
+ pool.stop();
+ assertAvailableCountEventuallyEquals(0);
+ }
+
+ @Test
+ public void testAddExistingMachineFromSpec() {
+ TestApplication app = createAppWithChildren(getInitialPoolSize());
+ app.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertAvailableCountEventuallyEquals(0);
+
+ Collection<Entity> added = pool.addExistingMachinesFromSpec("byon:(hosts=\"localhost,localhost\")");
+ assertEquals(added.size(), 2, "Added: " + Joiner.on(", ").join(added));
+ Iterator<Entity> it = added.iterator();
+ assertFalse(it.next().getConfig(ServerPoolImpl.REMOVABLE));
+ assertFalse(it.next().getConfig(ServerPoolImpl.REMOVABLE));
+ assertAvailableCountEventuallyEquals(2);
+
+ TestApplication app2 = createAppWithChildren(2);
+ app2.start(ImmutableList.of(pool.getDynamicLocation()));
+ assertAvailableCountEventuallyEquals(0);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/software/base/AbstractDockerLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/AbstractDockerLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/AbstractDockerLiveTest.java
new file mode 100644
index 0000000..7a9fd6d
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/AbstractDockerLiveTest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.software.base;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Runs a test with many different distros and versions.
+ */
+public abstract class AbstractDockerLiveTest {
+
+ public static final String PROVIDER = "docker";
+
+ protected BrooklynProperties brooklynProperties;
+ protected ManagementContext ctx;
+
+ protected TestApplication app;
+ protected Location jcloudsLocation;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ List<String> propsToRemove = ImmutableList.of("imageDescriptionRegex", "imageNameRegex", "inboundPorts",
+ "hardwareId", "minRam");
+
+ // Don't let any defaults from brooklyn.properties (except credentials) interfere with test
+ brooklynProperties = BrooklynProperties.Factory.newDefault();
+ for (String propToRemove : propsToRemove) {
+ for (String propVariant : ImmutableList.of(propToRemove, CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propToRemove))) {
+ brooklynProperties.remove("brooklyn.locations.jclouds."+PROVIDER+"."+propVariant);
+ brooklynProperties.remove("brooklyn.locations."+propVariant);
+ brooklynProperties.remove("brooklyn.jclouds."+PROVIDER+"."+propVariant);
+ brooklynProperties.remove("brooklyn.jclouds."+propVariant);
+ }
+ }
+
+ // Also removes scriptHeader (e.g. if doing `. ~/.bashrc` and `. ~/.profile`, then that can cause "stdin: is not a tty")
+ brooklynProperties.remove("brooklyn.ssh.config.scriptHeader");
+
+ ctx = new LocalManagementContext(brooklynProperties);
+ app = ApplicationBuilder.newManagedApp(TestApplication.class, ctx);
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (app != null) Entities.destroyAllCatching(app.getManagementContext());
+ }
+
+ @Test(groups={"Live", "WIP"})
+ public void test_Ubuntu_13_10() throws Exception {
+ runTest(ImmutableMap.of("imageId", "7fe2ec2ff748c411cf0d6833120741778c00e1b07a83c4104296b6258b5331c4",
+ "loginUser", "root",
+ "loginUser.password", "password"));
+ }
+
+ protected void runTest(Map<String,?> flags) throws Exception {
+ String tag = getClass().getSimpleName().toLowerCase();
+ Map<String,?> allFlags = MutableMap.<String,Object>builder()
+ .put("tags", ImmutableList.of(tag))
+ .putAll(flags)
+ .build();
+ jcloudsLocation = ctx.getLocationRegistry().resolve(PROVIDER, allFlags);
+ doTest(jcloudsLocation);
+ }
+
+ protected abstract void doTest(Location loc) throws Exception;
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessRestartIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessRestartIntegrationTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessRestartIntegrationTest.java
new file mode 100644
index 0000000..67f5fc4
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessRestartIntegrationTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.entity.software.base;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess.RestartSoftwareParameters;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.collections.CollectionFunctionals;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Tests restart of the software *process* (as opposed to the VM).
+ */
+public abstract class AbstractSoftwareProcessRestartIntegrationTest extends BrooklynAppLiveTestSupport {
+
+ // TODO Remove duplication from TomcatServerRestartIntegrationTest and MySqlRestartIntegrationTest
+
+ @SuppressWarnings("unused")
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractSoftwareProcessRestartIntegrationTest.class);
+
+ protected abstract EntitySpec<? extends SoftwareProcess> newEntitySpec();
+
+ @Test(groups="Integration")
+ public void testStopProcessAndRestart() throws Exception {
+ runStopProcessAndRestart(
+ SoftwareProcess.RESTART,
+ ImmutableMap.of(RestartSoftwareParameters.RESTART_MACHINE.getName(), RestartSoftwareParameters.RestartMachineMode.FALSE));
+ }
+
+ @Test(groups="Integration")
+ public void testStopProcessAndStart() throws Exception {
+ runStopProcessAndRestart(
+ SoftwareProcess.START,
+ ImmutableMap.of("locations", ImmutableList.of()));
+ }
+
+ protected void runStopProcessAndRestart(Effector<?> restartEffector, Map<String, ?> args) throws Exception {
+ LocalhostMachineProvisioningLocation loc = app.newLocalhostProvisioningLocation();
+ SoftwareProcess entity = app.createAndManageChild(newEntitySpec());
+
+ // Start the app
+ app.start(ImmutableList.of(loc));
+ EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_UP, true);
+ EntityTestUtils.assertAttributeEqualsEventually(app, SoftwareProcess.SERVICE_UP, true);
+
+ // Stop the process
+ Entities.invokeEffector(app, entity, SoftwareProcess.STOP, ImmutableMap.of(
+ StopSoftwareParameters.STOP_MACHINE_MODE.getName(), StopSoftwareParameters.StopMode.NEVER))
+ .get();
+ EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_UP, false);
+ EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED);
+ EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_PROCESS_IS_RUNNING, false);
+ EntityTestUtils.assertAttributeEventually(entity, ServiceStateLogic.SERVICE_NOT_UP_INDICATORS, CollectionFunctionals.<String>mapSizeEquals(1));
+
+ // Restart the process
+ Entities.invokeEffector(app, entity, restartEffector, args).get();
+ EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_UP, true);
+ EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+ EntityTestUtils.assertAttributeEqualsEventually(entity, SoftwareProcess.SERVICE_PROCESS_IS_RUNNING, true);
+ EntityTestUtils.assertAttributeEqualsEventually(entity, ServiceStateLogic.SERVICE_NOT_UP_INDICATORS, ImmutableMap.<String, Object>of());
+
+ EntityTestUtils.assertAttributeEqualsEventually(app, SoftwareProcess.SERVICE_UP, true);
+ EntityTestUtils.assertAttributeEqualsEventually(app, SoftwareProcess.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcess.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcess.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcess.java
new file mode 100644
index 0000000..4459db4
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcess.java
@@ -0,0 +1,33 @@
+/*
+ * 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.entity.software.base;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.entity.core.BrooklynConfigKeys;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+
+@ImplementedBy(DoNothingSoftwareProcessImpl.class)
+public interface DoNothingSoftwareProcess extends SoftwareProcess {
+
+ public static final ConfigKey<Boolean> SKIP_ON_BOX_BASE_DIR_RESOLUTION = ConfigKeys.newConfigKeyWithDefault(
+ BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION,
+ true);
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcessDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcessDriver.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcessDriver.java
new file mode 100644
index 0000000..2e0ea00
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcessDriver.java
@@ -0,0 +1,69 @@
+/*
+ * 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.entity.software.base;
+
+import org.apache.brooklyn.api.internal.EntityLocal;
+import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+/**
+ * Implements methods in {@link org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver}
+ * such that no actions are performed.
+ * <p>
+ * {@link #isRunning()} returns true.
+ */
+public class DoNothingSoftwareProcessDriver extends AbstractSoftwareProcessSshDriver {
+
+ public DoNothingSoftwareProcessDriver(EntityLocal entity, SshMachineLocation machine) {
+ super(entity, machine);
+ }
+
+ @Override
+ public boolean isRunning() {
+ return true;
+ }
+
+ @Override
+ public void copyPreInstallResources() {
+ }
+
+ @Override
+ public void copyInstallResources() {
+ }
+
+ @Override
+ public void copyRuntimeResources() {
+ }
+
+ @Override
+ public void install() {
+ }
+
+ @Override
+ public void customize() {
+ }
+
+ @Override
+ public void launch() {
+ }
+
+ @Override
+ public void stop() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcessImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcessImpl.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcessImpl.java
new file mode 100644
index 0000000..a04c660
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/DoNothingSoftwareProcessImpl.java
@@ -0,0 +1,38 @@
+/*
+ * 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.entity.software.base;
+
+import org.apache.brooklyn.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
+
+public class DoNothingSoftwareProcessImpl extends SoftwareProcessImpl implements DoNothingSoftwareProcess {
+
+ @Override
+ public Class getDriverInterface() {
+ return DoNothingSoftwareProcessDriver.class;
+ }
+
+ @Override
+ protected void connectSensors() {
+ super.connectSensors();
+ if (getAttribute(SERVICE_STATE_ACTUAL) == Lifecycle.STARTING) {
+ setAttribute(SERVICE_UP, true);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/software/base/MachineLifecycleEffectorTasksTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/MachineLifecycleEffectorTasksTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/MachineLifecycleEffectorTasksTest.java
new file mode 100644
index 0000000..3d87dbb
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/MachineLifecycleEffectorTasksTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.entity.software.base;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.internal.EntityLocal;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.BrooklynConfigKeys;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode;
+import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
+import org.apache.brooklyn.entity.stock.BasicEntity;
+import org.apache.brooklyn.entity.stock.BasicEntityImpl;
+import org.apache.brooklyn.entity.trait.Startable;
+import org.apache.brooklyn.sensor.core.DependentConfiguration;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.task.TaskInternal;
+import org.apache.brooklyn.util.time.Duration;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import org.apache.brooklyn.location.jclouds.BailOutJcloudsLocation;
+
+public class MachineLifecycleEffectorTasksTest {
+ public static boolean canStop(StopMode stopMode, boolean isEntityStopped) {
+ BasicEntityImpl entity = new BasicEntityImpl();
+ Lifecycle state = isEntityStopped ? Lifecycle.STOPPED : Lifecycle.RUNNING;
+ entity.setAttribute(SoftwareProcess.SERVICE_STATE_ACTUAL, state);
+ return MachineLifecycleEffectorTasks.canStop(stopMode, entity);
+ }
+
+ @DataProvider(name = "canStopStates")
+ public Object[][] canStopStates() {
+ return new Object[][] {
+ { StopMode.ALWAYS, true, true },
+ { StopMode.ALWAYS, false, true },
+ { StopMode.IF_NOT_STOPPED, true, false },
+ { StopMode.IF_NOT_STOPPED, false, true },
+ { StopMode.NEVER, true, false },
+ { StopMode.NEVER, false, false },
+ };
+ }
+
+ @Test(dataProvider = "canStopStates")
+ public void testBasicSonftwareProcessCanStop(StopMode mode, boolean isEntityStopped, boolean expected) {
+ boolean canStop = canStop(mode, isEntityStopped);
+ assertEquals(canStop, expected);
+ }
+
+ @Test
+ public void testProvisionLatchObeyed() throws Exception {
+
+ AttributeSensor<Boolean> ready = Sensors.newBooleanSensor("readiness");
+
+ TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
+ BasicEntity triggerEntity = app.createAndManageChild(EntitySpec.create(BasicEntity.class));
+
+ EmptySoftwareProcess entity = app.createAndManageChild(EntitySpec.create(EmptySoftwareProcess.class)
+ .configure(BrooklynConfigKeys.PROVISION_LATCH, DependentConfiguration.attributeWhenReady(triggerEntity, ready)));
+
+ final Task<Void> task = Entities.invokeEffector(app, app, Startable.START, ImmutableMap.of(
+ "locations", ImmutableList.of(BailOutJcloudsLocation.newBailOutJcloudsLocation(app.getManagementContext()))));
+
+ assertEffectorBlockingDetailsEventually(entity, "Waiting for config " + BrooklynConfigKeys.PROVISION_LATCH.getName());
+
+ Asserts.succeedsContinually(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(task.isDone());
+ }
+ });
+ try {
+ ((EntityLocal) triggerEntity).setAttribute(ready, true);
+ task.get(Duration.THIRTY_SECONDS);
+ } catch (Throwable t) {
+ // BailOut location throws but we don't care.
+ } finally {
+ Entities.destroyAll(app.getManagementContext());
+ }
+ }
+
+ private void assertEffectorBlockingDetailsEventually(final Entity entity, final String blockingDetailsSnippet) {
+ Asserts.succeedsEventually(new Runnable() {
+ @Override public void run() {
+ Task<?> entityTask = Iterables.getOnlyElement(entity.getApplication().getManagementContext().getExecutionManager().getTasksWithAllTags(
+ ImmutableList.of(BrooklynTaskTags.EFFECTOR_TAG, BrooklynTaskTags.tagForContextEntity(entity))));
+ String blockingDetails = getBlockingDetails(entityTask);
+ assertTrue(blockingDetails.contains(blockingDetailsSnippet));
+ }});
+ }
+
+ private String getBlockingDetails(Task<?> task) {
+ List<TaskInternal<?>> taskChain = Lists.newArrayList();
+ TaskInternal<?> taskI = (TaskInternal<?>) task;
+ while (taskI != null) {
+ taskChain.add(taskI);
+ if (taskI.getBlockingDetails() != null) {
+ return taskI.getBlockingDetails();
+ }
+ taskI = (TaskInternal<?>) taskI.getBlockingTask();
+ }
+ throw new IllegalStateException("No blocking details for "+task+" (walked task chain "+taskChain+")");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SameServerEntityTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SameServerEntityTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SameServerEntityTest.java
new file mode 100644
index 0000000..937c87d
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SameServerEntityTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.entity.software.base;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.software.base.SameServerEntity;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation.LocalhostMachine;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class SameServerEntityTest {
+
+ private LocalhostMachineProvisioningLocation loc;
+ private ManagementContext mgmt;
+ private TestApplication app;
+ private SameServerEntity entity;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() {
+ loc = new LocalhostMachineProvisioningLocation();
+ app = TestApplication.Factory.newManagedInstanceForTests();
+ mgmt = app.getManagementContext();
+ entity = app.createAndManageChild(EntitySpec.create(SameServerEntity.class));
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() {
+ if (app != null) Entities.destroyAll(mgmt);
+ }
+
+ @Test
+ public void testUsesSameMachineLocationForEachChild() throws Exception {
+ Entity child1 = entity.addChild(EntitySpec.create(TestEntity.class));
+ Entity child2 = entity.addChild(EntitySpec.create(TestEntity.class));
+ Entities.manage(child1);
+ Entities.manage(child2);
+
+ app.start(ImmutableList.of(loc));
+
+ Location child1Loc = Iterables.getOnlyElement(child1.getLocations());
+ Location child2Loc = Iterables.getOnlyElement(child2.getLocations());
+
+ assertSame(child1Loc, child2Loc);
+ assertTrue(child1Loc instanceof LocalhostMachine, "loc="+child1Loc);
+
+ assertEquals(ImmutableSet.of(child1Loc), ImmutableSet.copyOf(loc.getInUse()));
+
+ app.stop();
+
+ assertEquals(ImmutableSet.of(), ImmutableSet.copyOf(loc.getInUse()));
+ }
+}