/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.entity.software.base;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.entity.ImplementedBy;
import org.apache.brooklyn.api.location.LocationSpec;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
import org.apache.brooklyn.core.test.entity.TestApplication;
import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
import org.apache.brooklyn.entity.software.base.SoftwareProcess;
import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.BrooklynNetworkUtils;
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.stream.KnownSizeInputStream;
import org.apache.brooklyn.util.stream.Streams;
import org.apache.brooklyn.util.yaml.Yamls;
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.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteSource;
import com.google.common.io.Files;
public class SoftwareProcessSshDriverIntegrationTest {
private LocalManagementContext managementContext;
private LocalhostMachineProvisioningLocation localhost;
private SshMachineLocation machine127;
private TestApplication app;
private File tempDataDir;
@BeforeMethod(alwaysRun=true)
public void setUp() throws Exception {
tempDataDir = Files.createTempDir();
managementContext = new LocalManagementContext();
localhost = managementContext.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
machine127 = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
.configure("address", "localhost"));
app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
}
@AfterMethod(alwaysRun=true)
public void tearDown() throws Exception {
if (app != null) Entities.destroyAll(app.getManagementContext());
if (tempDataDir != null) Os.deleteRecursively(tempDataDir);
}
// Integration test because requires ssh'ing (and takes about 5 seconds)
// See also SoftwareProcessEntityTest.testCustomInstallDirX for a lot more mocked variants
@Test(groups="Integration")
public void testCanInstallMultipleVersionsOnSameMachine() throws Exception {
managementContext.getBrooklynProperties().put(BrooklynConfigKeys.ONBOX_BASE_DIR, tempDataDir.getAbsolutePath());
MyService entity = app.createAndManageChild(EntitySpec.create(MyService.class)
.configure(SoftwareProcess.SUGGESTED_VERSION, "0.1.0"));
MyService entity2 = app.createAndManageChild(EntitySpec.create(MyService.class)
.configure(SoftwareProcess.SUGGESTED_VERSION, "0.2.0"));
app.start(ImmutableList.of(machine127));
String installDir1 = entity.getAttribute(SoftwareProcess.INSTALL_DIR);
String installDir2 = entity2.getAttribute(SoftwareProcess.INSTALL_DIR);
assertNotEquals(installDir1, installDir2);
assertTrue(installDir1.contains("0.1.0"), "installDir1="+installDir1);
assertTrue(installDir2.contains("0.2.0"), "installDir2="+installDir2);
assertTrue(new File(new File(installDir1), "myfile").isFile());
assertTrue(new File(new File(installDir2), "myfile").isFile());
}
@Test(groups="Integration")
public void testLocalhostInTmp() throws Exception {
MyService entity = app.createAndManageChild(EntitySpec.create(MyService.class));
app.start(ImmutableList.of(localhost));
String installDir = entity.getAttribute(SoftwareProcess.INSTALL_DIR);
assertTrue(installDir.startsWith("/tmp/brooklyn-"+Os.user()+"/installs/"), "installed in "+installDir);
}
@Test(groups="Integration")
public void testMachine127InHome() throws Exception {
MyService entity = app.createAndManageChild(EntitySpec.create(MyService.class));
app.start(ImmutableList.of(machine127));
String installDir = entity.getAttribute(SoftwareProcess.INSTALL_DIR);
assertTrue(installDir.startsWith(Os.home()+"/brooklyn-managed-processes/installs/"), "installed in "+installDir);
}
@Test(groups="Integration")
public void testLocalhostInCustom() throws Exception {
localhost.config().set(BrooklynConfigKeys.ONBOX_BASE_DIR, tempDataDir.getAbsolutePath());
MyService entity = app.createAndManageChild(EntitySpec.create(MyService.class));
app.start(ImmutableList.of(localhost));
String installDir = entity.getAttribute(SoftwareProcess.INSTALL_DIR);
assertTrue(installDir.startsWith(tempDataDir.getAbsolutePath()+"/installs/"), "installed in "+installDir);
}
@Test(groups="Integration")
@Deprecated
public void testMachineInCustomFromDataDir() throws Exception {
managementContext.getBrooklynProperties().put(BrooklynConfigKeys.BROOKLYN_DATA_DIR, tempDataDir.getAbsolutePath());
MyService entity = app.createAndManageChild(EntitySpec.create(MyService.class));
app.start(ImmutableList.of(machine127));
String installDir = entity.getAttribute(SoftwareProcess.INSTALL_DIR);
assertTrue(installDir.startsWith(tempDataDir.getAbsolutePath()+"/installs/"), "installed in "+installDir);
}
@Test(groups="Integration")
public void testCopyResource() throws Exception {
File tempDest = new File(tempDataDir, "tempDest.txt");
String tempLocalContent = "abc";
File tempLocal = new File(tempDataDir, "tempLocal.txt");
Files.write(tempLocalContent, tempLocal, Charsets.UTF_8);
localhost.config().set(BrooklynConfigKeys.ONBOX_BASE_DIR, tempDataDir.getAbsolutePath());
MyService entity = app.createAndManageChild(EntitySpec.create(MyService.class));
app.start(ImmutableList.of(localhost));
// Copy local file
entity.getDriver().copyResource(tempLocal, tempDest.getAbsolutePath());
assertEquals(Files.readLines(tempDest, Charsets.UTF_8), ImmutableList.of(tempLocalContent));
tempDest.delete();
// Copy local file using url
entity.getDriver().copyResource(tempLocal.toURI().toString(), tempDest.getAbsolutePath());
assertEquals(Files.readLines(tempDest, Charsets.UTF_8), ImmutableList.of(tempLocalContent));
tempDest.delete();
// Copy reader
entity.getDriver().copyResource(new StringReader(tempLocalContent), tempDest.getAbsolutePath());
assertEquals(Files.readLines(tempDest, Charsets.UTF_8), ImmutableList.of(tempLocalContent));
tempDest.delete();
// Copy stream
entity.getDriver().copyResource(ByteSource.wrap(tempLocalContent.getBytes()).openStream(), tempDest.getAbsolutePath());
assertEquals(Files.readLines(tempDest, Charsets.UTF_8), ImmutableList.of(tempLocalContent));
tempDest.delete();
// Copy known-size stream
entity.getDriver().copyResource(new KnownSizeInputStream(Streams.newInputStreamWithContents(tempLocalContent), tempLocalContent.length()), tempDest.getAbsolutePath());
assertEquals(Files.readLines(tempDest, Charsets.UTF_8), ImmutableList.of(tempLocalContent));
tempDest.delete();
}
@Test(groups="Integration")
public void testCopyResourceCreatingParentDir() throws Exception {
/*
* TODO copyResource will now always create the parent dir, irrespective of the createParentDir value!
* In SshMachineLocation on 2014-05-29, Alex added: mkdir -p `dirname '$DEST'`
*
* Changing this test to assert that parent dir always created; should we delete boolean createParentDir
* from the copyResource method?
*
* TODO Have also deleted test that if relative path is given it will write that relative to $RUN_DIR.
* That is not the case: it is relative to $HOME, which seems fine. For example, if copyResource
* is used during install phase then $RUN_DIR would be the wrong default.
* Is there any code that relies on this behaviour?
*/
File tempDataDirSub = new File(tempDataDir, "subdir");
File tempDest = new File(tempDataDirSub, "tempDest.txt");
String tempLocalContent = "abc";
File tempLocal = new File(tempDataDir, "tempLocal.txt");
Files.write(tempLocalContent, tempLocal, Charsets.UTF_8);
localhost.config().set(BrooklynConfigKeys.ONBOX_BASE_DIR, tempDataDir.getAbsolutePath());
MyService entity = app.createAndManageChild(EntitySpec.create(MyService.class));
app.start(ImmutableList.of(localhost));
// First confirm that even if createParentDir==false that it still gets created!
try {
entity.getDriver().copyResource(tempLocal.toURI().toString(), tempDest.getAbsolutePath(), false);
assertEquals(Files.readLines(tempDest, Charsets.UTF_8), ImmutableList.of(tempLocalContent));
} finally {
Os.deleteRecursively(tempDataDirSub);
}
// Copy to absolute path
try {
entity.getDriver().copyResource(tempLocal.toURI().toString(), tempDest.getAbsolutePath(), true);
assertEquals(Files.readLines(tempDest, Charsets.UTF_8), ImmutableList.of(tempLocalContent));
} finally {
Os.deleteRecursively(tempDataDirSub);
}
}
@Test(groups="Integration")
public void testPreAndPostLaunchCommands() throws IOException {
File tempFile = new File(tempDataDir, "tempFile.txt");
localhost.config().set(BrooklynConfigKeys.ONBOX_BASE_DIR, tempDataDir.getAbsolutePath());
app.createAndManageChild(EntitySpec.create(VanillaSoftwareProcess.class)
.configure(VanillaSoftwareProcess.CHECK_RUNNING_COMMAND, "")
.configure(SoftwareProcess.PRE_LAUNCH_COMMAND, String.format("echo inPreLaunch >> %s", tempFile.getAbsoluteFile()))
.configure(VanillaSoftwareProcess.LAUNCH_COMMAND, String.format("echo inLaunch >> %s", tempFile.getAbsoluteFile()))
.configure(SoftwareProcess.POST_LAUNCH_COMMAND, String.format("echo inPostLaunch >> %s", tempFile.getAbsoluteFile())));
app.start(ImmutableList.of(localhost));
List<String> output = Files.readLines(tempFile, Charsets.UTF_8);
assertEquals(output.size(), 3);
assertEquals(output.get(0), "inPreLaunch");
assertEquals(output.get(1), "inLaunch");
assertEquals(output.get(2), "inPostLaunch");
tempFile.delete();
}
@Test(groups="Integration")
public void testInstallResourcesCopy() throws IOException {
localhost.config().set(BrooklynConfigKeys.ONBOX_BASE_DIR, tempDataDir.getAbsolutePath());
File template = new File(Os.tmp(), "template.yaml");
VanillaSoftwareProcess entity = app.createAndManageChild(EntitySpec.create(VanillaSoftwareProcess.class)
.configure(VanillaSoftwareProcess.CHECK_RUNNING_COMMAND, "")
.configure(SoftwareProcess.INSTALL_FILES, MutableMap.of("classpath://org/apache/brooklyn/entity/software/base/frogs.txt", "frogs.txt"))
.configure(SoftwareProcess.INSTALL_TEMPLATES, MutableMap.of("classpath://org/apache/brooklyn/entity/software/base/template.yaml", template.getAbsolutePath()))
.configure(VanillaSoftwareProcess.LAUNCH_COMMAND, "date"));
app.start(ImmutableList.of(localhost));
File frogs = new File(entity.getAttribute(SoftwareProcess.INSTALL_DIR), "frogs.txt");
try {
Assert.assertTrue(frogs.canRead(), "File not readable: " + frogs);
String output = Files.toString(frogs, Charsets.UTF_8);
Assert.assertTrue(output.contains("Brekekekex"), "File content not found: " + output);
} finally {
frogs.delete();
}
try {
String expectedHostname = BrooklynNetworkUtils.getLocalhostInetAddress().getHostName();
String expectedIp = BrooklynNetworkUtils.getLocalhostInetAddress().getHostAddress();
Map<?,?> data = (Map) Iterables.getOnlyElement(Yamls.parseAll(Files.toString(template, Charsets.UTF_8)));
Assert.assertEquals(data.size(), 3);
Assert.assertEquals(data.get("entity.hostname"), expectedHostname);
Assert.assertEquals(data.get("entity.address"), expectedIp);
Assert.assertEquals(data.get("frogs"), Integer.valueOf(12));
} finally {
template.delete();
}
}
@Test(groups="Integration")
public void testRuntimeResourcesCopy() throws IOException {
localhost.config().set(BrooklynConfigKeys.ONBOX_BASE_DIR, tempDataDir.getAbsolutePath());
File template = new File(Os.tmp(), "template.yaml");
VanillaSoftwareProcess entity = app.createAndManageChild(EntitySpec.create(VanillaSoftwareProcess.class)
.configure(VanillaSoftwareProcess.CHECK_RUNNING_COMMAND, "")
.configure(SoftwareProcess.RUNTIME_FILES, MutableMap.of("classpath://org/apache/brooklyn/entity/software/base/frogs.txt", "frogs.txt"))
.configure(SoftwareProcess.RUNTIME_TEMPLATES, MutableMap.of("classpath://org/apache/brooklyn/entity/software/base/template.yaml", template.getAbsolutePath()))
.configure(VanillaSoftwareProcess.LAUNCH_COMMAND, "date"));
app.start(ImmutableList.of(localhost));
File frogs = new File(entity.getAttribute(SoftwareProcess.RUN_DIR), "frogs.txt");
try {
Assert.assertTrue(frogs.canRead(), "File not readable: " + frogs);
String output = Files.toString(frogs, Charsets.UTF_8);
Assert.assertTrue(output.contains("Brekekekex"), "File content not found: " + output);
} finally {
frogs.delete();
}
try {
String expectedHostname = BrooklynNetworkUtils.getLocalhostInetAddress().getHostName();
String expectedIp = BrooklynNetworkUtils.getLocalhostInetAddress().getHostAddress();
Map<?,?> data = (Map) Iterables.getOnlyElement(Yamls.parseAll(Files.toString(template, Charsets.UTF_8)));
Assert.assertEquals(data.size(), 3);
Assert.assertEquals(data.get("entity.hostname"), expectedHostname);
Assert.assertEquals(data.get("entity.address"), expectedIp);
Assert.assertEquals(data.get("frogs"), Integer.valueOf(12));
} finally {
template.delete();
}
}
@ImplementedBy(MyServiceImpl.class)
public interface MyService extends SoftwareProcess {
public SimulatedDriver getDriver();
}
public static class MyServiceImpl extends SoftwareProcessImpl implements MyService {
public MyServiceImpl() {
}
@Override
public Class<?> getDriverInterface() {
return SimulatedDriver.class;
}
@Override
public SimulatedDriver getDriver() {
return (SimulatedDriver) super.getDriver();
}
}
public static class SimulatedDriver extends AbstractSoftwareProcessSshDriver {
public List<String> events = new ArrayList<String>();
private volatile boolean launched = false;
public SimulatedDriver(EntityLocal entity, SshMachineLocation machine) {
super(entity, machine);
}
@Override
public void install() {
events.add("install");
newScript(INSTALLING)
.failOnNonZeroResultCode()
.body.append("touch myfile")
.execute();
}
@Override
public void customize() {
events.add("customize");
}
@Override
public void launch() {
events.add("launch");
launched = true;
entity.sensors().set(Startable.SERVICE_UP, true);
}
@Override
public boolean isRunning() {
return launched;
}
@Override
public void stop() {
events.add("stop");
launched = false;
entity.sensors().set(Startable.SERVICE_UP, false);
}
@Override
public void kill() {
events.add("kill");
launched = false;
entity.sensors().set(Startable.SERVICE_UP, false);
}
}
}