/*
* 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.Map;
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.EntityLocal;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.internal.BrooklynProperties;
import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.core.location.PortRanges;
import org.apache.brooklyn.core.objs.proxy.EntityProxyImpl;
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
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.software.base.SoftwareProcess.StopSoftwareParameters.StopMode;
import org.apache.brooklyn.entity.stock.BasicApplication;
import org.apache.brooklyn.entity.stock.BasicApplicationImpl;
import org.apache.brooklyn.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.http.HttpTool;
import org.apache.brooklyn.util.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.localhost.LocalhostMachineProvisioningLocation;
import org.apache.brooklyn.location.ssh.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: org.apache.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 destDir = new File(new File(tempDir, "lib"), "dropins");
File expectedFile1 = new File(destDir, classpathEntry1.getName());
File expectedFile2 = new File(destDir, 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 testCopiesClasspathEntriesInConfigKey2() 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();
String testName1 = "test_" + classpathEntry1.getName();
File destDir = new File(new File(tempDir, "lib"), "dropins");
File expectedFile1 = new File(destDir, testName1);
String testName2 = "test_" + classpathEntry2.getName();
File expectedFile2 = new File(destDir, testName2);
Map entry1 = ImmutableMap.of("url", classpathEntry1.getAbsolutePath(), "filename", testName1);
Map entry2 = ImmutableMap.of("url", classpathEntry2.getAbsolutePath(), "filename", testName2);
try {
BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest()
.configure(BrooklynNode.RUN_DIR, tempDir.getAbsolutePath())
.configure(BrooklynNode.CLASSPATH, ImmutableList.of(entry1, entry2))
);
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();
}
}
/*
Exception java.io.FileNotFoundException
Message: /tmp/1445824492556-0/lib/first4759470075693094333.properties (No such file or directory)
Stacktrace:
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:146)
at com.google.common.io.Files$FileByteSource.openStream(Files.java:126)
at com.google.common.io.Files$FileByteSource.openStream(Files.java:116)
at com.google.common.io.ByteSource$AsCharSource.openStream(ByteSource.java:435)
at com.google.common.io.CharSource.getInput(CharSource.java:94)
at com.google.common.io.CharSource.getInput(CharSource.java:65)
at com.google.common.io.CharStreams.readLines(CharStreams.java:344)
at com.google.common.io.Files.readLines(Files.java:741)
at com.google.common.io.Files.readLines(Files.java:712)
at org.apache.brooklyn.entity.brooklynnode.BrooklynNodeIntegrationTest.testCopiesClasspathEntriesInBrooklynProperties(BrooklynNodeIntegrationTest.java:358)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
at org.testng.TestRunner.privateRun(TestRunner.java:767)
at org.testng.TestRunner.run(TestRunner.java:617)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:348)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305)
at org.testng.SuiteRunner.run(SuiteRunner.java:254)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
at org.testng.TestNG.run(TestNG.java:1057)
at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:115)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:205)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:108)
at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:111)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103)
*/
@Test(groups={"Integration","Broken"})
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":"org.apache.brooklyn.entity.stock.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).sensors().set(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));
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);
}
}