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:29 UTC
[11/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/AbstractSoftlayerLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/AbstractSoftlayerLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/AbstractSoftlayerLiveTest.java
new file mode 100644
index 0000000..9080e51
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/AbstractSoftlayerLiveTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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;
+
+import java.util.List;
+import java.util.Map;
+
+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.apache.brooklyn.util.text.StringShortener;
+import org.apache.brooklyn.util.text.Strings;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Runs a test with many different distros and versions.
+ */
+public abstract class AbstractSoftlayerLiveTest {
+
+ public static final String PROVIDER = "softlayer";
+ public static final int MAX_TAG_LENGTH = 20;
+ public static final int MAX_VM_NAME_LENGTH = 30;
+
+ 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("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");
+
+ ctx = new LocalManagementContext(brooklynProperties);
+ app = ApplicationBuilder.newManagedApp(TestApplication.class, ctx);
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (app != null) Entities.destroyAll(app.getManagementContext());
+ }
+
+ @Test(groups = {"Live"})
+ public void test_Default() throws Exception {
+ runTest(ImmutableMap.<String,Object>of());
+ }
+
+ @Test(groups = {"Live"})
+ public void test_Ubuntu_12_0_4() throws Exception {
+ // Image: {id=UBUNTU_12_64, providerId=UBUNTU_12_64, os={family=ubuntu, version=12.04, description=Ubuntu / Ubuntu / 12.04.0-64 Minimal, is64Bit=true}, description=UBUNTU_12_64, status=AVAILABLE, loginUser=root}
+ runTest(ImmutableMap.<String,Object>of("imageId", "UBUNTU_12_64"));
+ }
+
+ @Test(groups = {"Live"})
+ public void test_Centos_6_0() throws Exception {
+ // 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}
+ runTest(ImmutableMap.<String,Object>of("imageId", "CENTOS_6_64"));
+ }
+
+ protected void runTest(Map<String,?> flags) throws Exception {
+ StringShortener shortener = Strings.shortener().separator("-");
+ shortener.canTruncate(getClass().getSimpleName(), MAX_TAG_LENGTH);
+ Map<String,?> allFlags = MutableMap.<String,Object>builder()
+ .put("tags", ImmutableList.of(shortener.getStringOfMaxLength(MAX_TAG_LENGTH)))
+ .put("vmNameMaxLength", MAX_VM_NAME_LENGTH)
+ .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/brooklynnode/BrooklynClusterIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynClusterIntegrationTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynClusterIntegrationTest.java
new file mode 100644
index 0000000..1879e4b
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynClusterIntegrationTest.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.brooklynnode;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynCluster;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode.ExistingFileBehaviour;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.os.Os;
+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 com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class BrooklynClusterIntegrationTest extends BrooklynAppUnitTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BrooklynNodeIntegrationTest.class);
+
+ private File pseudoBrooklynPropertiesFile;
+ private File pseudoBrooklynCatalogFile;
+ private File persistenceDir;
+ private LocalhostMachineProvisioningLocation loc;
+ private List<LocalhostMachineProvisioningLocation> locs;
+
+ @BeforeMethod(alwaysRun=true)
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ pseudoBrooklynPropertiesFile = Os.newTempFile("brooklynnode-test", ".properties");
+ pseudoBrooklynPropertiesFile.delete();
+
+ pseudoBrooklynCatalogFile = Os.newTempFile("brooklynnode-test", ".catalog");
+ pseudoBrooklynCatalogFile.delete();
+
+ loc = app.newLocalhostProvisioningLocation();
+ locs = ImmutableList.of(loc);
+ }
+
+ @AfterMethod(alwaysRun=true)
+ @Override
+ public void tearDown() throws Exception {
+ try {
+ super.tearDown();
+ } finally {
+ if (pseudoBrooklynPropertiesFile != null) pseudoBrooklynPropertiesFile.delete();
+ if (pseudoBrooklynCatalogFile != null) pseudoBrooklynCatalogFile.delete();
+ if (persistenceDir != null) Os.deleteRecursively(persistenceDir);
+ }
+ }
+
+ @Test(groups="Integration")
+ public void testCanStartAndStop() throws Exception {
+ BrooklynCluster cluster = app.createAndManageChild(EntitySpec.create(BrooklynCluster.class)
+ .configure(BrooklynCluster.INITIAL_SIZE, 1)
+ .configure(BrooklynNode.WEB_CONSOLE_BIND_ADDRESS, Networking.ANY_NIC)
+ .configure(BrooklynNode.ON_EXISTING_PROPERTIES_FILE, ExistingFileBehaviour.DO_NOT_USE));
+ app.start(locs);
+ Entity brooklynNode = Iterables.find(cluster.getMembers(), Predicates.instanceOf(BrooklynNode.class));
+ LOG.info("started "+app+" containing "+cluster+" for "+JavaClassNames.niceClassAndMethod());
+
+ EntityTestUtils.assertAttributeEqualsEventually(cluster, BrooklynNode.SERVICE_UP, true);
+ EntityTestUtils.assertAttributeEqualsEventually(brooklynNode, BrooklynNode.SERVICE_UP, true);
+
+ cluster.stop();
+ EntityTestUtils.assertAttributeEquals(cluster, BrooklynNode.SERVICE_UP, false);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
new file mode 100644
index 0000000..948a42a
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
@@ -0,0 +1,632 @@
+/*
+ * 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.brooklynnode;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.brooklyn.api.effector.Effector;
+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.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.objs.proxy.EntityProxyImpl;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynEntityMirror;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNodeImpl;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNodeSshDriver;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode.DeployBlueprintEffector;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode.ExistingFileBehaviour;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode.StopNodeAndKillAppsEffector;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.entity.stock.BasicApplicationImpl;
+import org.apache.brooklyn.sensor.feed.http.JsonFunctions;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.test.HttpTestUtils;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.http.HttpTool;
+import org.apache.brooklyn.util.core.http.HttpToolResponse;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Functionals;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.net.Urls;
+import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.Locations;
+import org.apache.brooklyn.location.basic.PortRanges;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
+
+/**
+ * This test needs to able to access the binary artifact in order to run.
+ * The default behaviour is to take this from maven, which works pretty well if you're downloading from hosted maven.
+ * <p>
+ * This class has been updated so that it does not effect or depend on the contents of ~/.brooklyn/brooklyn.properties .
+ * <p>
+ * If you wish to supply your own version (useful if testing changes locally!), you'll need to force download of this file.
+ * The simplest way is to install:
+ * <ul>
+ * <li>file://$HOME/.brooklyn/repository/BrooklynNode/${VERSION}/BrooklynNode-${VERSION}.tar.gz - for snapshot versions (filename is default format due to lack of filename in sonatype inferencing;
+ * note on case-sensitive systems it might have to be all in lower case!)
+ * <li>file://$HOME/.brooklyn/repository/BrooklynNode/${VERSION}/brooklyn-${VERSION}-dist.tar.gz - for release versions, filename should match that in maven central
+ * </ul>
+ * In both cases, remember that you may also need to wipe the local apps cache ($BROOKLYN_DATA_DIR/installs/BrooklynNode).
+ * The following commands may be useful:
+ * <p>
+ * <code>
+ * cp ~/.m2/repository/org/apache/brooklyn/brooklyn-dist/0.7.0-SNAPSHOT/brooklyn-dist-0.7.0-SNAPSHOT-dist.tar.gz ~/.brooklyn/repository/BrooklynNode/0.7.0-SNAPSHOT/BrooklynNode-0.7.0-SNAPSHOT.tar.gz
+ * rm -rf /tmp/brooklyn-`whoami`/installs/BrooklynNode*
+ * </code>
+ */
+public class BrooklynNodeIntegrationTest extends BrooklynAppUnitTestSupport {
+
+ private static final Logger log = LoggerFactory.getLogger(BrooklynNodeIntegrationTest.class);
+
+ private File pseudoBrooklynPropertiesFile;
+ private File pseudoBrooklynCatalogFile;
+ private File persistenceDir;
+ private LocalhostMachineProvisioningLocation loc;
+ private List<LocalhostMachineProvisioningLocation> locs;
+
+ @BeforeMethod(alwaysRun=true)
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ pseudoBrooklynPropertiesFile = Os.newTempFile("brooklynnode-test", ".properties");
+ pseudoBrooklynPropertiesFile.delete();
+
+ pseudoBrooklynCatalogFile = Os.newTempFile("brooklynnode-test", ".catalog");
+ pseudoBrooklynCatalogFile.delete();
+
+ loc = app.newLocalhostProvisioningLocation();
+ locs = ImmutableList.of(loc);
+ }
+
+ @AfterMethod(alwaysRun=true)
+ @Override
+ public void tearDown() throws Exception {
+ try {
+ super.tearDown();
+ } finally {
+ if (pseudoBrooklynPropertiesFile != null) pseudoBrooklynPropertiesFile.delete();
+ if (pseudoBrooklynCatalogFile != null) pseudoBrooklynCatalogFile.delete();
+ if (persistenceDir != null) Os.deleteRecursively(persistenceDir);
+ }
+ }
+
+ protected EntitySpec<BrooklynNode> newBrooklynNodeSpecForTest() {
+ // poor man's way to output which test is running
+ log.info("Creating entity spec for "+JavaClassNames.callerNiceClassAndMethod(1));
+
+ return EntitySpec.create(BrooklynNode.class)
+ .configure(BrooklynNode.WEB_CONSOLE_BIND_ADDRESS, Networking.ANY_NIC)
+ .configure(BrooklynNode.ON_EXISTING_PROPERTIES_FILE, ExistingFileBehaviour.DO_NOT_USE);
+
+ /* yaml equivalent, for testing:
+
+location: localhost
+services:
+- type: brooklyn.entity.brooklynnode.BrooklynNode
+ bindAddress: 127.0.0.1
+ onExistingProperties: do_not_use
+
+# some other options
+ enabledHttpProtocols: [ https ]
+ managementPassword: s3cr3t
+ brooklynLocalPropertiesContents: |
+ brooklyn.webconsole.security.https.required=true
+ brooklyn.webconsole.security.users=admin
+ brooklyn.webconsole.security.user.admin.password=s3cr3t
+ brooklyn.location.localhost.enabled=false
+
+ */
+ }
+
+ @Test(groups="Integration")
+ public void testCanStartAndStop() throws Exception {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest());
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ EntityTestUtils.assertAttributeEqualsEventually(brooklynNode, BrooklynNode.SERVICE_UP, true);
+
+ brooklynNode.stop();
+ EntityTestUtils.assertAttributeEquals(brooklynNode, BrooklynNode.SERVICE_UP, false);
+ }
+
+ @Test(groups="Integration")
+ public void testSetsGlobalBrooklynPropertiesFromContents() throws Exception {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_REMOTE_PATH, pseudoBrooklynPropertiesFile.getAbsolutePath())
+ .configure(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_CONTENTS, "abc=def"));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ assertEquals(Files.readLines(pseudoBrooklynPropertiesFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
+ }
+
+ @Test(groups="Integration")
+ public void testSetsLocalBrooklynPropertiesFromContents() throws Exception {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_REMOTE_PATH, pseudoBrooklynPropertiesFile.getAbsolutePath())
+ .configure(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_CONTENTS, "abc=def"));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ assertEquals(Files.readLines(pseudoBrooklynPropertiesFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
+ }
+
+ @Test(groups="Integration")
+ public void testSetsBrooklynPropertiesFromUri() throws Exception {
+ File brooklynPropertiesSourceFile = File.createTempFile("brooklynnode-test", ".properties");
+ Files.write("abc=def", brooklynPropertiesSourceFile, Charsets.UTF_8);
+
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_REMOTE_PATH, pseudoBrooklynPropertiesFile.getAbsolutePath())
+ .configure(BrooklynNode.BROOKLYN_GLOBAL_PROPERTIES_URI, brooklynPropertiesSourceFile.toURI().toString()));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ assertEquals(Files.readLines(pseudoBrooklynPropertiesFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
+ }
+
+ @Test(groups="Integration")
+ public void testSetsBrooklynCatalogFromContents() throws Exception {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.BROOKLYN_CATALOG_REMOTE_PATH, pseudoBrooklynCatalogFile.getAbsolutePath())
+ .configure(BrooklynNode.BROOKLYN_CATALOG_CONTENTS, "<catalog/>"));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ assertEquals(Files.readLines(pseudoBrooklynCatalogFile, Charsets.UTF_8), ImmutableList.of("<catalog/>"));
+ }
+
+ @Test(groups="Integration")
+ public void testSetsBrooklynCatalogFromUri() throws Exception {
+ File brooklynCatalogSourceFile = File.createTempFile("brooklynnode-test", ".catalog");
+ Files.write("abc=def", brooklynCatalogSourceFile, Charsets.UTF_8);
+
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.BROOKLYN_CATALOG_REMOTE_PATH, pseudoBrooklynCatalogFile.getAbsolutePath())
+ .configure(BrooklynNode.BROOKLYN_CATALOG_URI, brooklynCatalogSourceFile.toURI().toString()));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ assertEquals(Files.readLines(pseudoBrooklynCatalogFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
+ }
+
+ @Test(groups="Integration")
+ public void testCopiesResources() throws Exception {
+ File sourceFile = File.createTempFile("brooklynnode-test", ".properties");
+ Files.write("abc=def", sourceFile, Charsets.UTF_8);
+ File tempDir = Files.createTempDir();
+ File expectedFile = new File(tempDir, "myfile.txt");
+
+ try {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.RUN_DIR, tempDir.getAbsolutePath())
+ .configure(BrooklynNode.COPY_TO_RUNDIR, ImmutableMap.of(sourceFile.getAbsolutePath(), "${RUN}/myfile.txt")));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ assertEquals(Files.readLines(expectedFile, Charsets.UTF_8), ImmutableList.of("abc=def"));
+ } finally {
+ expectedFile.delete();
+ tempDir.delete();
+ sourceFile.delete();
+ }
+ }
+
+ @Test(groups="Integration")
+ public void testCopiesClasspathEntriesInConfigKey() throws Exception {
+ String content = "abc=def";
+ File classpathEntry1 = File.createTempFile("first", ".properties");
+ File classpathEntry2 = File.createTempFile("second", ".properties");
+ Files.write(content, classpathEntry1, Charsets.UTF_8);
+ Files.write(content, classpathEntry2, Charsets.UTF_8);
+ File tempDir = Files.createTempDir();
+ File expectedFile1 = new File(new File(tempDir, "lib"), classpathEntry1.getName());
+ File expectedFile2 = new File(new File(tempDir, "lib"), classpathEntry2.getName());
+
+ try {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.RUN_DIR, tempDir.getAbsolutePath())
+ .configure(BrooklynNode.CLASSPATH, ImmutableList.of(classpathEntry1.getAbsolutePath(), classpathEntry2.getAbsolutePath()))
+ );
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ assertEquals(Files.readLines(expectedFile1, Charsets.UTF_8), ImmutableList.of(content));
+ assertEquals(Files.readLines(expectedFile2, Charsets.UTF_8), ImmutableList.of(content));
+ } finally {
+ expectedFile1.delete();
+ expectedFile2.delete();
+ tempDir.delete();
+ classpathEntry1.delete();
+ classpathEntry2.delete();
+ }
+ }
+
+ @Test(groups="Integration")
+ public void testCopiesClasspathEntriesInBrooklynProperties() throws Exception {
+ String content = "abc=def";
+ File classpathEntry1 = File.createTempFile("first", ".properties");
+ File classpathEntry2 = File.createTempFile("second", ".properties");
+ Files.write(content, classpathEntry1, Charsets.UTF_8);
+ Files.write(content, classpathEntry2, Charsets.UTF_8);
+ File tempDir = Files.createTempDir();
+ File expectedFile1 = new File(new File(tempDir, "lib"), classpathEntry1.getName());
+ File expectedFile2 = new File(new File(tempDir, "lib"), classpathEntry2.getName());
+
+ try {
+ String propName = BrooklynNode.CLASSPATH.getName();
+ String propValue = classpathEntry1.toURI().toString() + "," + classpathEntry2.toURI().toString();
+ ((BrooklynProperties)app.getManagementContext().getConfig()).put(propName, propValue);
+
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.RUN_DIR, tempDir.getAbsolutePath())
+ );
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ assertEquals(Files.readLines(expectedFile1, Charsets.UTF_8), ImmutableList.of(content));
+ assertEquals(Files.readLines(expectedFile2, Charsets.UTF_8), ImmutableList.of(content));
+ } finally {
+ expectedFile1.delete();
+ expectedFile2.delete();
+ tempDir.delete();
+ classpathEntry1.delete();
+ classpathEntry2.delete();
+ }
+ }
+
+ // TODO test that the classpath set above is actually used
+
+ @Test(groups="Integration")
+ public void testSetsBrooklynWebConsolePort() throws Exception {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.HTTP_PORT, PortRanges.fromString("45000+")));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ Integer httpPort = brooklynNode.getAttribute(BrooklynNode.HTTP_PORT);
+ URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+ assertTrue(httpPort >= 45000 && httpPort < 54100, "httpPort="+httpPort);
+ assertEquals((Integer)webConsoleUri.getPort(), httpPort);
+ HttpTestUtils.assertHttpStatusCodeEquals(webConsoleUri.toString(), 200, 401);
+ }
+
+ @Test(groups="Integration")
+ public void testStartsAppOnStartup() throws Exception {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.APP, BasicApplicationImpl.class.getName()));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+ waitForApps(webConsoleUri, 1);
+ String apps = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
+ List<String> appType = parseJsonList(apps, ImmutableList.of("spec", "type"), String.class);
+ assertEquals(appType, ImmutableList.of(BasicApplication.class.getName()));
+ }
+
+ protected static void waitForApps(String webConsoleUri) {
+ HttpTestUtils.assertHttpStatusCodeEquals(webConsoleUri+"/v1/applications", 200, 403);
+ HttpTestUtils.assertHttpStatusCodeEventuallyEquals(webConsoleUri+"/v1/applications", 200);
+ }
+
+ // TODO Should introduce startup stages and let the client select which stage it expects to be complete
+ protected void waitForApps(final URI webConsoleUri, final int num) {
+ waitForApps(webConsoleUri.toString());
+
+ // e.g. [{"id":"UnBqPcqg","spec":{"name":"Application (UnBqPcqg)","type":"brooklyn.entity.basic.BasicApplication","locations":["pOL4NtiW"]},"status":"RUNNING","links":{"self":"/v1/applications/UnBqPcqg","entities":"/v1/applications/UnBqPcqg/entities"}}]
+ Asserts.succeedsEventually(new Runnable() {
+ @Override
+ public void run() {
+ //Wait all apps to become managed
+ String appsContent = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
+ List<String> appIds = parseJsonList(appsContent, ImmutableList.of("id"), String.class);
+ assertEquals(appIds.size(), num);
+
+ // and then to start
+ List<String> statuses = parseJsonList(appsContent, ImmutableList.of("status"), String.class);
+ for (String status : statuses) {
+ assertEquals(status, Lifecycle.RUNNING.toString().toUpperCase());
+ }
+ }});
+ }
+
+ @Test(groups="Integration")
+ public void testStartsAppViaEffector() throws Exception {
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest());
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ // note there is also a test for this in DeployApplication
+ final URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+ waitForApps(webConsoleUri.toString());
+
+ final String id = brooklynNode.invoke(BrooklynNode.DEPLOY_BLUEPRINT, ConfigBag.newInstance()
+ .configure(DeployBlueprintEffector.BLUEPRINT_TYPE, BasicApplication.class.getName())
+ .getAllConfig()).get();
+
+ String apps = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
+ List<String> appType = parseJsonList(apps, ImmutableList.of("spec", "type"), String.class);
+ assertEquals(appType, ImmutableList.of(BasicApplication.class.getName()));
+
+ HttpTestUtils.assertContentEventuallyMatches(
+ webConsoleUri.toString()+"/v1/applications/"+id+"/entities/"+id+"/sensors/service.state",
+ "\"?(running|RUNNING)\"?");
+ }
+
+ @Test(groups="Integration")
+ public void testUsesLocation() throws Exception {
+ String brooklynPropertiesContents =
+ "brooklyn.location.named.mynamedloc=localhost:(name=myname)\n"+
+ //force lat+long so test will work when offline
+ "brooklyn.location.named.mynamedloc.latitude=123\n"+
+ "brooklyn.location.named.mynamedloc.longitude=45.6\n";
+
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_CONTENTS, brooklynPropertiesContents)
+ .configure(BrooklynNode.APP, BasicApplicationImpl.class.getName())
+ .configure(BrooklynNode.LOCATIONS, "named:mynamedloc"));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+ waitForApps(webConsoleUri, 1);
+
+ // Check that "mynamedloc" has been picked up from the brooklyn.properties
+ String locsContent = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/locations");
+ List<String> locNames = parseJsonList(locsContent, ImmutableList.of("name"), String.class);
+ assertTrue(locNames.contains("mynamedloc"), "locNames="+locNames);
+
+ // Find the id of the concrete location instance of the app
+ String appsContent = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
+ List<String[]> appLocationIds = parseJsonList(appsContent, ImmutableList.of("spec", "locations"), String[].class);
+ String appLocationId = Iterables.getOnlyElement(appLocationIds)[0]; // app.getManagementContext().getLocationRegistry()
+
+ // Check that the concrete location is of the required type
+ String locatedLocationsContent = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/locations/usage/LocatedLocations");
+ assertEquals(parseJson(locatedLocationsContent, ImmutableList.of(appLocationId, "name"), String.class), "myname");
+ assertEquals(parseJson(locatedLocationsContent, ImmutableList.of(appLocationId, "longitude"), Double.class), 45.6, 0.00001);
+ }
+
+ @Test(groups="Integration")
+ public void testAuthenticationAndHttps() throws Exception {
+ String adminPassword = "p4ssw0rd";
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.ENABLED_HTTP_PROTOCOLS, ImmutableList.of("https"))
+ .configure(BrooklynNode.MANAGEMENT_PASSWORD, adminPassword)
+ .configure(BrooklynNode.BROOKLYN_LOCAL_PROPERTIES_CONTENTS,
+ Strings.lines(
+ "brooklyn.webconsole.security.https.required=true",
+ "brooklyn.webconsole.security.users=admin",
+ "brooklyn.webconsole.security.user.admin.password="+adminPassword,
+ "brooklyn.location.localhost.enabled=false") )
+ );
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+ Assert.assertTrue(webConsoleUri.toString().startsWith("https://"), "web console not https: "+webConsoleUri);
+ Integer httpsPort = brooklynNode.getAttribute(BrooklynNode.HTTPS_PORT);
+ Assert.assertTrue(httpsPort!=null && httpsPort >= 8443 && httpsPort <= 8500);
+ Assert.assertTrue(webConsoleUri.toString().contains(""+httpsPort), "web console not using right https port ("+httpsPort+"): "+webConsoleUri);
+ HttpTestUtils.assertHttpStatusCodeEquals(webConsoleUri.toString(), 401);
+
+ HttpClient http = HttpTool.httpClientBuilder()
+ .trustAll()
+ .uri(webConsoleUri)
+ .laxRedirect(true)
+ .credentials(new UsernamePasswordCredentials("admin", adminPassword))
+ .build();
+ HttpToolResponse response = HttpTool.httpGet(http, webConsoleUri, MutableMap.<String,String>of());
+ Assert.assertEquals(response.getResponseCode(), 200);
+ }
+
+ @Test(groups="Integration")
+ public void testStopPlainThrowsException() throws Exception {
+ BrooklynNode brooklynNode = setUpBrooklynNodeWithApp();
+
+ // Not using annotation with `expectedExceptions = PropagatedRuntimeException.class` because want to
+ // ensure exception comes from stop. On jenkins, was seeing setUpBrooklynNodeWithApp fail in
+ // testStopAndKillAppsEffector; so can't tell if this method was really passing!
+ try {
+ brooklynNode.stop();
+ fail("Expected "+brooklynNode+" stop to fail, because has app");
+ } catch (Exception e) {
+ IllegalStateException ise = Exceptions.getFirstThrowableOfType(e, IllegalStateException.class);
+ if (ise != null && ise.toString().contains("Can't stop instance with running applications")) {
+ // success
+ } else {
+ throw e;
+ }
+ } finally {
+ try {
+ brooklynNode.invoke(BrooklynNode.STOP_NODE_AND_KILL_APPS, ImmutableMap.of(StopNodeAndKillAppsEffector.TIMEOUT.getName(), Duration.THIRTY_SECONDS)).getUnchecked();
+ } catch (Exception e) {
+ log.warn("Error in stopNodeAndKillApps for "+brooklynNode+" (continuing)", e);
+ }
+ }
+ }
+
+ @Test(groups="Integration")
+ public void testStopAndKillAppsEffector() throws Exception {
+ createNodeAndExecStopEffector(BrooklynNode.STOP_NODE_AND_KILL_APPS);
+ }
+
+ @Test(groups="Integration")
+ public void testStopButLeaveAppsEffector() throws Exception {
+ createNodeAndExecStopEffector(BrooklynNode.STOP_NODE_BUT_LEAVE_APPS);
+ }
+
+ @Test(groups="Integration")
+ public void testStopAndRestartProcess() throws Exception {
+ persistenceDir = Files.createTempDir();
+ BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
+ .configure(BrooklynNode.EXTRA_LAUNCH_PARAMETERS, "--persist auto --persistenceDir "+persistenceDir.getAbsolutePath())
+ .configure(BrooklynNode.APP, BasicApplicationImpl.class.getName()));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+ File pidFile = new File(getDriver(brooklynNode).getPidFile());
+ URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+
+ waitForApps(webConsoleUri, 1);
+
+ // Stop just the process; will not have unmanaged entity unless machine was being terminated
+ brooklynNode.invoke(BrooklynNode.STOP, ImmutableMap.<String, Object>of(
+ BrooklynNode.StopSoftwareParameters.STOP_MACHINE_MODE.getName(), StopMode.NEVER,
+ BrooklynNode.StopSoftwareParameters.STOP_PROCESS_MODE.getName(), StopMode.ALWAYS)).getUnchecked();
+
+ assertTrue(Entities.isManaged(brooklynNode));
+ assertFalse(isPidRunning(pidFile), "pid in "+pidFile+" still running");
+
+ // Clear the startup app so it's not started second time, in addition to the rebind state
+ // TODO remove this once the startup app is created only if no previous persistence state
+ brooklynNode.config().set(BrooklynNode.APP, (String)null);
+ ((EntityLocal)brooklynNode).setAttribute(BrooklynNode.APP, null);
+
+ // Restart the process; expect persisted state to have been restored, so apps still known about
+ brooklynNode.invoke(BrooklynNode.RESTART, ImmutableMap.<String, Object>of(
+ BrooklynNode.RestartSoftwareParameters.RESTART_MACHINE.getName(), "false")).getUnchecked();
+
+ waitForApps(webConsoleUri.toString());
+ String apps = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
+ List<String> appType = parseJsonList(apps, ImmutableList.of("spec", "type"), String.class);
+ assertEquals(appType, ImmutableList.of(BasicApplication.class.getName()));
+ }
+
+ private void createNodeAndExecStopEffector(Effector<?> eff) throws Exception {
+ BrooklynNode brooklynNode = setUpBrooklynNodeWithApp();
+ File pidFile = new File(getDriver(brooklynNode).getPidFile());
+ assertTrue(isPidRunning(pidFile));
+
+ brooklynNode.invoke(eff, Collections.<String, Object>emptyMap()).getUnchecked();
+
+ // Note can't use driver.isRunning to check shutdown; can't invoke scripts on an unmanaged entity
+ EntityTestUtils.assertAttributeEquals(brooklynNode, BrooklynNode.SERVICE_UP, false);
+
+ // unmanaged if the machine is destroyed - ie false on localhost (this test by default), but true in the cloud
+// assertFalse(Entities.isManaged(brooklynNode));
+
+ assertFalse(isPidRunning(pidFile), "pid in "+pidFile+" still running");
+ }
+
+ private boolean isPidRunning(File pidFile) throws Exception {
+ SshMachineLocation machine = loc.obtain();
+ try {
+ int result = machine.execScript("check-pid", ImmutableList.of(
+ "test -f "+pidFile+" || exit 1",
+ "ps -p `cat "+pidFile+"`"));
+ return result == 0;
+ } finally {
+ loc.release(machine);
+ Locations.unmanage(machine);
+ }
+ }
+
+ private BrooklynNodeSshDriver getDriver(BrooklynNode brooklynNode) {
+ try {
+ EntityProxyImpl entityProxy = (EntityProxyImpl)Proxy.getInvocationHandler(brooklynNode);
+ Method getDriver = BrooklynNodeImpl.class.getMethod("getDriver");
+ return (BrooklynNodeSshDriver)entityProxy.invoke(brooklynNode, getDriver, new Object[]{});
+ } catch (Throwable e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+
+ private BrooklynNode setUpBrooklynNodeWithApp() throws InterruptedException,
+ ExecutionException {
+ BrooklynNode brooklynNode = app.createAndManageChild(EntitySpec.create(BrooklynNode.class)
+ .configure(BrooklynNode.NO_WEB_CONSOLE_AUTHENTICATION, Boolean.TRUE));
+ app.start(locs);
+ log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
+
+ EntityTestUtils.assertAttributeEqualsEventually(brooklynNode, BrooklynNode.SERVICE_UP, true);
+
+ String baseUrl = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI).toString();
+ waitForApps(baseUrl);
+
+ final String id = brooklynNode.invoke(BrooklynNode.DEPLOY_BLUEPRINT, ConfigBag.newInstance()
+ .configure(DeployBlueprintEffector.BLUEPRINT_TYPE, BasicApplication.class.getName())
+ .getAllConfig()).get();
+
+ String entityUrl = Urls.mergePaths(baseUrl, "v1/applications/", id, "entities", id);
+
+ Entity mirror = brooklynNode.addChild(EntitySpec.create(BrooklynEntityMirror.class)
+ .configure(BrooklynEntityMirror.MIRRORED_ENTITY_URL, entityUrl)
+ .configure(BrooklynEntityMirror.MIRRORED_ENTITY_ID, id));
+ Entities.manage(mirror);
+
+ assertEquals(brooklynNode.getChildren().size(), 1);
+ return brooklynNode;
+ }
+
+ private <T> T parseJson(String json, List<String> elements, Class<T> clazz) {
+ Function<String, T> func = Functionals.chain(
+ JsonFunctions.asJson(),
+ JsonFunctions.walk(elements),
+ JsonFunctions.cast(clazz));
+ return func.apply(json);
+ }
+
+ private <T> List<T> parseJsonList(String json, List<String> elements, Class<T> clazz) {
+ Function<String, List<T>> func = Functionals.chain(
+ JsonFunctions.asJson(),
+ JsonFunctions.forEach(Functionals.chain(
+ JsonFunctions.walk(elements),
+ JsonFunctions.cast(clazz))));
+ return func.apply(json);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeTest.java
new file mode 100644
index 0000000..60d2c6b
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeTest.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.entity.brooklynnode;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNodeImpl;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNodeSshDriver;
+import org.apache.brooklyn.entity.core.Attributes;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.trait.Startable;
+import org.apache.brooklyn.sensor.feed.ConfigToAttributes;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+public class BrooklynNodeTest {
+
+ // TODO Need test for copying/setting classpath
+
+ private TestApplication app;
+ private SshMachineLocation loc;
+
+ public static class SlowStopBrooklynNode extends BrooklynNodeImpl {
+ public SlowStopBrooklynNode() {}
+
+ @Override
+ protected void postStop() {
+ super.postStop();
+
+ //Make sure UnmanageTask will wait for the STOP effector to complete.
+ Time.sleep(Duration.FIVE_SECONDS);
+ }
+
+ }
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ app = TestApplication.Factory.newManagedInstanceForTests();
+ loc = new SshMachineLocation(MutableMap.of("address", "localhost"));
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (app != null) Entities.destroyAll(app.getManagementContext());
+ }
+
+ @Test
+ public void testGeneratesCorrectSnapshotDownload() throws Exception {
+ String version = "0.0.1-SNAPSHOT";
+ String expectedUrl = "https://repository.apache.org/service/local/artifact/maven/redirect?r=snapshots&g=org.apache.brooklyn&v="+version+"&a=brooklyn-dist&c=dist&e=tar.gz";
+ runTestGeneratesCorrectDownloadUrl(version, expectedUrl);
+ }
+
+ @Test
+ public void testGeneratesCorrectReleaseDownload() throws Exception {
+ String version = "0.0.1";
+ String expectedUrl = "http://search.maven.org/remotecontent?filepath=org/apache/brooklyn/brooklyn-dist/"+version+"/brooklyn-dist-"+version+"-dist.tar.gz";
+ runTestGeneratesCorrectDownloadUrl(version, expectedUrl);
+ }
+
+ private void runTestGeneratesCorrectDownloadUrl(String version, String expectedUrl) throws Exception {
+ // TODO Using BrooklynNodeImpl directly, because want to instantiate a BroolynNodeSshDriver.
+ // Really want to make that easier to test, without going through "wrong" code path for creating entity.
+ BrooklynNodeImpl entity = new BrooklynNodeImpl();
+ entity.setConfig(BrooklynNode.SUGGESTED_VERSION, version);
+ entity.setParent(app);
+ Entities.manage(entity);
+ ConfigToAttributes.apply(entity);
+ BrooklynNodeSshDriver driver = new BrooklynNodeSshDriver(entity, loc);
+
+ DownloadResolver resolver = Entities.newDownloader(driver);
+ List<String> urls = resolver.getTargets();
+
+ System.out.println("urls="+urls);
+ assertTrue(urls.contains(expectedUrl), "urls="+urls);
+ }
+
+ @Test(groups = "Integration")
+ public void testUnmanageOnStop() throws Exception {
+ final BrooklynNode node = app.addChild(EntitySpec.create(BrooklynNode.class).impl(SlowStopBrooklynNode.class));
+ Entities.manage(node);
+ assertTrue(Entities.isManaged(node), "Entity " + node + " must be managed.");
+ node.invoke(Startable.STOP, ImmutableMap.<String,Object>of()).asTask().getUnchecked();
+ //The UnmanageTask will unblock after the STOP effector completes, so we are competing with it here.
+ Asserts.succeedsEventually(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(Entities.isManaged(node));
+ }
+ });
+ }
+
+
+ @Test
+ public void testCanStartSameNode() throws Exception {
+ // not very interesting as do not have REST when run in this project
+ // but test BrooklynNodeRestTest in downstream project does
+ BrooklynNode bn = app.createAndManageChild(EntitySpec.create(BrooklynNode.class, SameBrooklynNodeImpl.class));
+ bn.start(MutableSet.<Location>of());
+
+ Assert.assertEquals(bn.getAttribute(Attributes.SERVICE_UP), (Boolean)true);
+ // no URI
+ Assert.assertNull(bn.getAttribute(BrooklynNode.WEB_CONSOLE_URI));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java
new file mode 100644
index 0000000..c933b9d
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java
@@ -0,0 +1,100 @@
+/*
+ * 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.brooklynnode;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.entity.brooklynnode.EntityHttpClient;
+import org.apache.brooklyn.util.core.http.HttpToolResponse;
+import org.apache.brooklyn.util.core.http.HttpTool.HttpClientBuilder;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+public class CallbackEntityHttpClient implements EntityHttpClient {
+ public static class Request {
+ private Entity entity;
+ private String method;
+ private String path;
+ private Map<String, String> params;
+ public Request(Entity entity, String method, String path, Map<String, String> params) {
+ this.entity = entity;
+ this.method = method;
+ this.path = path;
+ this.params = params;
+ }
+ public Entity getEntity() {
+ return entity;
+ }
+ public String getMethod() {
+ return method;
+ }
+ public String getPath() {
+ return path;
+ }
+ public Map<String, String> getParams() {
+ return params;
+ }
+ }
+ private Function<Request, String> callback;
+ private Entity entity;
+
+ public CallbackEntityHttpClient(Entity entity, Function<Request, String> callback) {
+ this.entity = entity;
+ this.callback = callback;
+ }
+
+ @Override
+ public HttpClientBuilder getHttpClientForBrooklynNode() {
+ throw new IllegalStateException("Method call not expected");
+ }
+
+ @Override
+ public HttpToolResponse get(String path) {
+ String result = callback.apply(new Request(entity, HttpGet.METHOD_NAME, path, Collections.<String, String>emptyMap()));
+ return new HttpToolResponse(HttpStatus.SC_OK, null, result.getBytes(), 0, 0, 0);
+ }
+
+ @Override
+ public HttpToolResponse post(String path, Map<String, String> headers, byte[] body) {
+ throw new IllegalStateException("Method call not expected");
+ }
+
+ @Override
+ public HttpToolResponse post(String path, Map<String, String> headers, Map<String, String> formParams) {
+ String result = callback.apply(new Request(entity, HttpPost.METHOD_NAME, path, formParams));
+ return new HttpToolResponse(HttpStatus.SC_OK, Collections.<String, List<String>>emptyMap(), result.getBytes(), 0, 0, 0);
+ }
+
+ @Override
+ public HttpToolResponse delete(String path, Map<String, String> headers) {
+ throw new IllegalStateException("Method call not expected");
+ }
+
+ @Override
+ public EntityHttpClient responseSuccess(Predicate<Integer> successPredicate) {
+ throw new IllegalStateException("Method call not expected");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/MockBrooklynNode.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/MockBrooklynNode.java b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/MockBrooklynNode.java
new file mode 100644
index 0000000..fcc939f
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/MockBrooklynNode.java
@@ -0,0 +1,72 @@
+/*
+ * 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.brooklynnode;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
+import org.apache.brooklyn.entity.brooklynnode.EntityHttpClient;
+import org.apache.brooklyn.entity.brooklynnode.CallbackEntityHttpClient.Request;
+import org.apache.brooklyn.entity.brooklynnode.effector.SetHighAvailabilityModeEffectorBody;
+import org.apache.brooklyn.entity.brooklynnode.effector.SetHighAvailabilityPriorityEffectorBody;
+import org.apache.brooklyn.entity.core.AbstractEntity;
+import org.apache.brooklyn.sensor.core.BasicAttributeSensor;
+
+import com.google.common.base.Function;
+import com.google.common.reflect.TypeToken;
+
+public class MockBrooklynNode extends AbstractEntity implements BrooklynNode {
+ @SuppressWarnings("serial")
+ public static final ConfigKey<Function<Request, String>> HTTP_CLIENT_CALLBACK = ConfigKeys.newConfigKey(new TypeToken<Function<Request, String>>(){}, "httpClientCallback");
+ public static final AttributeSensor<Integer> HA_PRIORITY = new BasicAttributeSensor<Integer>(Integer.class, "priority");
+
+ @Override
+ public void init() {
+ super.init();
+ getMutableEntityType().addEffector(SetHighAvailabilityPriorityEffectorBody.SET_HIGH_AVAILABILITY_PRIORITY);
+ getMutableEntityType().addEffector(SetHighAvailabilityModeEffectorBody.SET_HIGH_AVAILABILITY_MODE);
+ setAttribute(HA_PRIORITY, 0);
+ }
+
+ @Override
+ public EntityHttpClient http() {
+ return new CallbackEntityHttpClient(this, getConfig(HTTP_CLIENT_CALLBACK));
+ }
+
+ @Override
+ public void start(Collection<? extends Location> locations) {
+ }
+
+ @Override
+ public void stop() {
+ }
+
+ @Override
+ public void restart() {
+ }
+
+ @Override
+ public void populateServiceNotUpDiagnostics() {
+ // no-op
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SameBrooklynNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SameBrooklynNodeImpl.java b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SameBrooklynNodeImpl.java
new file mode 100644
index 0000000..84d2c33
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SameBrooklynNodeImpl.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.brooklynnode;
+
+import java.net.URI;
+import java.util.Collection;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
+import org.apache.brooklyn.entity.brooklynnode.EntityHttpClient;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNodeImpl.DeployBlueprintEffectorBody;
+import org.apache.brooklyn.entity.core.AbstractEntity;
+import org.apache.brooklyn.sensor.feed.http.HttpFeed;
+import org.apache.brooklyn.sensor.feed.http.HttpPollConfig;
+import org.apache.brooklyn.sensor.feed.http.HttpValueFunctions;
+
+/** Implementation of BrooklynNode which just presents the node where this is running, for convenience;
+ *
+ * start/stop/restart have no effect;
+ * sensors are connected;
+ * deploy blueprint assumes that a REST endpoint is available */
+public class SameBrooklynNodeImpl extends AbstractEntity implements BrooklynNode {
+
+ private HttpFeed httpFeed;
+
+ @Override
+ public void start(Collection<? extends Location> locations) {
+ connectSensors();
+ }
+
+ @Override
+ public void stop() {
+ disconnectSensors();
+ }
+
+ @Override
+ public void restart() {
+ return;
+ }
+
+
+ @Override
+ public void init() {
+ super.init();
+ getMutableEntityType().addEffector(DeployBlueprintEffectorBody.DEPLOY_BLUEPRINT);
+ }
+
+ protected void connectSensors() {
+ URI webConsoleUri = getManagementContext().getManagementNodeUri().orNull();
+ setAttribute(WEB_CONSOLE_URI, webConsoleUri);
+
+ if (webConsoleUri != null) {
+ httpFeed = HttpFeed.builder()
+ .entity(this)
+ .period(200)
+ .baseUri(webConsoleUri)
+ .credentialsIfNotNull(getConfig(MANAGEMENT_USER), getConfig(MANAGEMENT_PASSWORD))
+ .poll(new HttpPollConfig<Boolean>(SERVICE_UP)
+ .onSuccess(HttpValueFunctions.responseCodeEquals(200))
+ .setOnFailureOrException(false))
+ .build();
+
+ } else {
+ setAttribute(SERVICE_UP, true);
+ }
+ }
+
+ protected void disconnectSensors() {
+ if (httpFeed != null) httpFeed.stop();
+ }
+
+ @Override
+ public EntityHttpClient http() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void populateServiceNotUpDiagnostics() {
+ // no-op
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java
new file mode 100644
index 0000000..60cc6e9
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java
@@ -0,0 +1,259 @@
+/*
+ * 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.brooklynnode;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.internal.EntityLocal;
+import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.effector.core.Effectors;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynCluster;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynClusterImpl;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynCluster.SelectMasterEffector;
+import org.apache.brooklyn.entity.brooklynnode.CallbackEntityHttpClient.Request;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.sensor.feed.AttributePollHandler;
+import org.apache.brooklyn.sensor.feed.DelegatingPollHandler;
+import org.apache.brooklyn.sensor.feed.Poller;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.http.client.methods.HttpPost;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+
+public class SelectMasterEffectorTest extends BrooklynAppUnitTestSupport {
+ private static final Logger LOG = LoggerFactory.getLogger(BrooklynClusterImpl.class);
+
+ protected BrooklynCluster cluster;
+ protected HttpCallback http;
+ protected Poller<Void> poller;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // because the effector calls wait for a state change, use a separate thread to drive that
+ poller = new Poller<Void>((EntityLocal)app, false);
+ poller.scheduleAtFixedRate(
+ new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ masterFailoverIfNeeded();
+ return null;
+ }
+ },
+ new DelegatingPollHandler<Void>(Collections.<AttributePollHandler<? super Void>>emptyList()),
+ Duration.millis(20));
+ poller.start();
+ }
+
+ @Override
+ protected void setUpApp() {
+ super.setUpApp();
+ http = new HttpCallback();
+ cluster = app.createAndManageChild(EntitySpec.create(BrooklynCluster.class)
+ .location(app.newLocalhostProvisioningLocation())
+ .configure(BrooklynCluster.MEMBER_SPEC, EntitySpec.create(BrooklynNode.class)
+ .impl(MockBrooklynNode.class)
+ .configure(MockBrooklynNode.HTTP_CLIENT_CALLBACK, http)));
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ poller.stop();
+ super.tearDown();
+ }
+
+ @Test
+ public void testInvalidNewMasterIdFails() {
+ try {
+ selectMaster(cluster, "1234");
+ fail("Non-existend entity ID provided.");
+ } catch (Exception e) {
+ assertTrue(e.toString().contains("1234 is not an ID of brooklyn node in this cluster"));
+ }
+ }
+
+ @Test(groups="Integration") // because slow, due to sensor feeds
+ public void testSelectMasterAfterChange() {
+ List<Entity> nodes = makeTwoNodes();
+ EntityTestUtils.assertAttributeEqualsEventually(cluster, BrooklynCluster.MASTER_NODE, (BrooklynNode)nodes.get(0));
+
+ selectMaster(cluster, nodes.get(1).getId());
+ checkMaster(cluster, nodes.get(1));
+ }
+
+ @Test
+ public void testFindMaster() {
+ List<Entity> nodes = makeTwoNodes();
+ Assert.assertEquals(((BrooklynClusterImpl)Entities.deproxy(cluster)).findMasterChild(), nodes.get(0));
+ }
+
+ @Test(groups="Integration") // because slow, due to sensor feeds
+ public void testSelectMasterFailsAtChangeState() {
+ http.setFailAtStateChange(true);
+
+ List<Entity> nodes = makeTwoNodes();
+
+ EntityTestUtils.assertAttributeEqualsEventually(cluster, BrooklynCluster.MASTER_NODE, (BrooklynNode)nodes.get(0));
+
+ try {
+ selectMaster(cluster, nodes.get(1).getId());
+ fail("selectMaster should have failed");
+ } catch (Exception e) {
+ // expected
+ }
+ checkMaster(cluster, nodes.get(0));
+ }
+
+ private List<Entity> makeTwoNodes() {
+ List<Entity> nodes = MutableList.copyOf(cluster.resizeByDelta(2));
+ setManagementState(nodes.get(0), ManagementNodeState.MASTER);
+ setManagementState(nodes.get(1), ManagementNodeState.HOT_STANDBY);
+ return nodes;
+ }
+
+ private void checkMaster(Group cluster, Entity node) {
+ assertEquals(node.getAttribute(BrooklynNode.MANAGEMENT_NODE_STATE), ManagementNodeState.MASTER);
+ assertEquals(cluster.getAttribute(BrooklynCluster.MASTER_NODE), node);
+ for (Entity member : cluster.getMembers()) {
+ if (member != node) {
+ assertEquals(member.getAttribute(BrooklynNode.MANAGEMENT_NODE_STATE), ManagementNodeState.HOT_STANDBY);
+ }
+ assertEquals((int)member.getAttribute(MockBrooklynNode.HA_PRIORITY), 0);
+ }
+ }
+
+ private static class HttpCallback implements Function<CallbackEntityHttpClient.Request, String> {
+ private enum State {
+ INITIAL,
+ PROMOTED
+ }
+ private State state = State.INITIAL;
+ private boolean failAtStateChange;
+
+ @Override
+ public String apply(Request input) {
+ if ("/v1/server/ha/state".equals(input.getPath())) {
+ if (failAtStateChange) {
+ throw new RuntimeException("Testing failure at changing node state");
+ }
+
+ checkRequest(input, HttpPost.METHOD_NAME, "/v1/server/ha/state", "mode", "HOT_STANDBY");
+ Entity entity = input.getEntity();
+ EntityTestUtils.assertAttributeEquals(entity, BrooklynNode.MANAGEMENT_NODE_STATE, ManagementNodeState.MASTER);
+ EntityTestUtils.assertAttributeEquals(entity, MockBrooklynNode.HA_PRIORITY, 0);
+
+ setManagementState(entity, ManagementNodeState.HOT_STANDBY);
+
+ return "MASTER";
+ } else {
+ switch(state) {
+ case INITIAL:
+ checkRequest(input, HttpPost.METHOD_NAME, "/v1/server/ha/priority", "priority", "1");
+ state = State.PROMOTED;
+ setPriority(input.getEntity(), Integer.parseInt(input.getParams().get("priority")));
+ return "0";
+ case PROMOTED:
+ checkRequest(input, HttpPost.METHOD_NAME, "/v1/server/ha/priority", "priority", "0");
+ state = State.INITIAL;
+ setPriority(input.getEntity(), Integer.parseInt(input.getParams().get("priority")));
+ return "1";
+ default: throw new IllegalStateException("Illegal call at state " + state + ". Request = " + input.getMethod() + " " + input.getPath());
+ }
+ }
+ }
+
+ public void checkRequest(Request input, String methodName, String path, String key, String value) {
+ if (!input.getMethod().equals(methodName) || !input.getPath().equals(path)) {
+ throw new IllegalStateException("Request doesn't match expected state. Expected = " + input.getMethod() + " " + input.getPath() + ". " +
+ "Actual = " + methodName + " " + path);
+ }
+
+ String inputValue = input.getParams().get(key);
+ if(!Objects.equal(value, inputValue)) {
+ throw new IllegalStateException("Request doesn't match expected parameter " + methodName + " " + path + ". Parameter " + key +
+ " expected = " + value + ", actual = " + inputValue);
+ }
+ }
+
+ public void setFailAtStateChange(boolean failAtStateChange) {
+ this.failAtStateChange = failAtStateChange;
+ }
+
+ }
+
+ private void masterFailoverIfNeeded() {
+ if (!Entities.isManaged(cluster)) return;
+ if (cluster.getAttribute(BrooklynCluster.MASTER_NODE) == null) {
+ Collection<Entity> members = cluster.getMembers();
+ if (members.size() > 0) {
+ for (Entity member : members) {
+ if (member.getAttribute(MockBrooklynNode.HA_PRIORITY) == 1) {
+ masterFailover(member);
+ return;
+ }
+ }
+ masterFailover(members.iterator().next());
+ }
+ }
+ }
+
+ private void masterFailover(Entity member) {
+ LOG.debug("Master failover to " + member);
+ setManagementState(member, ManagementNodeState.MASTER);
+ EntityTestUtils.assertAttributeEqualsEventually(cluster, BrooklynCluster.MASTER_NODE, (BrooklynNode)member);
+ return;
+ }
+
+ public static void setManagementState(Entity entity, ManagementNodeState state) {
+ ((EntityLocal)entity).setAttribute(BrooklynNode.MANAGEMENT_NODE_STATE, state);
+ }
+
+ public static void setPriority(Entity entity, int priority) {
+ ((EntityLocal)entity).setAttribute(MockBrooklynNode.HA_PRIORITY, priority);
+ }
+
+ private void selectMaster(DynamicCluster cluster, String id) {
+ app.getExecutionContext().submit(Effectors.invocation(cluster, BrooklynCluster.SELECT_MASTER, ImmutableMap.of(SelectMasterEffector.NEW_MASTER_ID.getName(), id))).asTask().getUnchecked();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefConfigsTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefConfigsTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefConfigsTest.java
new file mode 100644
index 0000000..6cc8e27
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefConfigsTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.chef;
+
+import java.util.Set;
+
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.chef.ChefConfig;
+import org.apache.brooklyn.entity.chef.ChefConfigs;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.factory.ApplicationBuilder;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+public class ChefConfigsTest {
+
+ private TestApplication app = null;
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() {
+ if (app!=null) Entities.destroyAll(app.getManagementContext());
+ app = null;
+ }
+
+ @Test
+ public void testAddToRunList() {
+ app = ApplicationBuilder.newManagedApp(TestApplication.class);
+ ChefConfigs.addToLaunchRunList(app, "a", "b");
+ Set<? extends String> runs = app.getConfig(ChefConfig.CHEF_LAUNCH_RUN_LIST);
+ Assert.assertEquals(runs.size(), 2, "runs="+runs);
+ Assert.assertTrue(runs.contains("a"));
+ Assert.assertTrue(runs.contains("b"));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefLiveTestSupport.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefLiveTestSupport.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefLiveTestSupport.java
new file mode 100644
index 0000000..5ea8315
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefLiveTestSupport.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.chef;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
+import org.apache.brooklyn.entity.chef.ChefConfig;
+import org.apache.brooklyn.entity.core.EntityInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.io.FileUtil;
+import org.apache.brooklyn.util.stream.InputStreamSupplier;
+
+import com.google.common.base.Throwables;
+import com.google.common.io.Files;
+
+public class ChefLiveTestSupport extends BrooklynAppLiveTestSupport {
+
+ private static final Logger log = LoggerFactory.getLogger(ChefLiveTestSupport.class);
+
+ protected MachineProvisioningLocation<? extends SshMachineLocation> targetLocation;
+
+ @BeforeMethod(alwaysRun=true)
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ targetLocation = createLocation();
+ }
+
+ protected MachineProvisioningLocation<? extends SshMachineLocation> createLocation() {
+ return createLocation(mgmt);
+ }
+
+ /** convenience for setting up a pre-built / fixed IP machine
+ * (because you might not want to set up Chef on localhost)
+ * and ensuring tests against Chef use the same configured location
+ **/
+ @SuppressWarnings("unchecked")
+ public static MachineProvisioningLocation<? extends SshMachineLocation> createLocation(ManagementContext mgmt) {
+ Location bestLocation = mgmt.getLocationRegistry().resolve("named:ChefTests", true, null).orNull();
+ if (bestLocation==null) {
+ log.info("using AWS for chef tests because named:ChefTests does not exist");
+ bestLocation = mgmt.getLocationRegistry().resolve("jclouds:aws-ec2:us-east-1");
+ }
+ if (bestLocation==null) {
+ throw new IllegalStateException("Need a location called named:ChefTests or AWS configured for these tests");
+ }
+ return (MachineProvisioningLocation<? extends SshMachineLocation>)bestLocation;
+ }
+
+ private static String defaultConfigFile = null;
+ public synchronized static String installBrooklynChefHostedConfig() {
+ if (defaultConfigFile!=null) return defaultConfigFile;
+ File tempDir = Files.createTempDir();
+ ResourceUtils r = ResourceUtils.create(ChefServerTasksIntegrationTest.class);
+ try {
+ for (String f: new String[] { "knife.rb", "brooklyn-tests.pem", "brooklyn-validator.pem" }) {
+ String contents = r.getResourceAsString("classpath:///org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/"+f);
+ FileUtil.copyTo(InputStreamSupplier.fromString(contents).getInput(), new File(tempDir, f));
+ }
+ } catch (IOException e) {
+ throw Throwables.propagate(e);
+ }
+ File knifeConfig = new File(tempDir, "knife.rb");
+ defaultConfigFile = knifeConfig.getPath();
+ return defaultConfigFile;
+ }
+
+ public static void installBrooklynChefHostedConfig(Entity entity) {
+ ((EntityInternal)entity).setConfig(ChefConfig.KNIFE_CONFIG_FILE, ChefLiveTestSupport.installBrooklynChefHostedConfig());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java
new file mode 100644
index 0000000..79c7be0
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.chef;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.chef.ChefConfig;
+import org.apache.brooklyn.entity.chef.ChefServerTasks;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.util.stream.StreamGobbler;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+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;
+
+/** Many tests expect knife on the path, but none require any configuration beyond that.
+ * They will use the Brooklyn registered account (which has been set up with mysql cookbooks and more).
+ * <p>
+ * Note this is a free account so cannot manage many nodes.
+ * You can use the credentials in src/test/resources/hosted-chef-brooklyn-credentials/
+ * to log in and configure the settings for our tests using knife. You can also log in at:
+ * <p>
+ * https://manage.opscode.com/
+ * <p>
+ * with credentials for those with need to know (which is a lot of people, but not everyone
+ * with access to this github repo!).
+ * <p>
+ * You can easily set up your own new account, for free; download the starter kit and
+ * point {@link ChefConfig#KNIFE_CONFIG_FILE} at the knife.rb.
+ * <p>
+ * Note that if you are porting an existing machine to be managed by a new chef account, you may need to do the following:
+ * <p>
+ * ON management machine:
+ * <li>knife client delete HOST # or bulk delete, but don't delete your validator! it is a PITA recreating and adding back all the permissions!
+ * <li>knife node delete HOST
+ * <p>
+ * ON machine being managed:
+ * <li>rm -rf /{etc,var}/chef
+ * <p>
+ * Note also that some tests require a location named:ChefLive to be set up in your brooklyn.properties.
+ * This can be a cloud (but will require frequent chef-node pruning) or a permanently set-up machine.
+ **/
+public class ChefServerTasksIntegrationTest {
+
+ private static final Logger log = LoggerFactory.getLogger(ChefServerTasksIntegrationTest.class);
+
+ protected TestApplication app;
+ protected ManagementContext mgmt;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setup() throws Exception {
+ app = ApplicationBuilder.newManagedApp(TestApplication.class);
+ mgmt = app.getManagementContext();
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (mgmt != null) Entities.destroyAll(mgmt);
+ mgmt = null;
+ }
+
+ /** @deprecated use {@link ChefLiveTestSupport} */
+ @Deprecated
+ public synchronized static String installBrooklynChefHostedConfig() {
+ return ChefLiveTestSupport.installBrooklynChefHostedConfig();
+ }
+
+ @Test(groups="Integration")
+ @SuppressWarnings("resource")
+ public void testWhichKnife() throws IOException, InterruptedException {
+ // requires that knife is installed on the path of login shells
+ Process p = Runtime.getRuntime().exec(new String[] { "bash", "-l", "-c", "which knife" });
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ new StreamGobbler(p.getInputStream(), out, log).start();
+ new StreamGobbler(p.getErrorStream(), out, log).start();
+ log.info("bash -l -c 'which knife' gives exit code: "+p.waitFor());
+ Time.sleep(Duration.millis(1000));
+ log.info("output:\n"+out);
+ Assert.assertEquals(p.exitValue(), 0);
+ }
+
+ @Test(groups="Integration")
+ public void testKnifeWithoutConfig() {
+ // without config it shouldn't pass
+ // (assumes that knife global config is *not* installed on your machine)
+ ProcessTaskWrapper<Boolean> t = Entities.submit(app, ChefServerTasks.isKnifeInstalled());
+ log.info("isKnifeInstalled without config returned: "+t.get()+" ("+t.getExitCode()+")\n"+t.getStdout()+"\nERR:\n"+t.getStderr());
+ Assert.assertFalse(t.get());
+ }
+
+ @Test(groups="Integration")
+ public void testKnifeWithConfig() {
+ // requires that knife is installed on the path of login shells
+ // (creates the config in a temp space)
+ ChefLiveTestSupport.installBrooklynChefHostedConfig(app);
+ ProcessTaskWrapper<Boolean> t = Entities.submit(app, ChefServerTasks.isKnifeInstalled());
+ log.info("isKnifeInstalled *with* config returned: "+t.get()+" ("+t.getExitCode()+")\n"+t.getStdout()+"\nERR:\n"+t.getStderr());
+ Assert.assertTrue(t.get());
+ }
+
+}