blob: 79943652ba45075a47969a0bc610b30b7a95cab8 [file] [log] [blame]
/*
* 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.camp.brooklyn;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags.WrappedStream;
import org.apache.brooklyn.location.winrm.PlainWinRmExecTaskFactory;
import static org.testng.Assert.fail;
import java.util.List;
import java.util.Map;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.location.MachineProvisioningLocation;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.Dumper;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityAsserts;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
import org.apache.brooklyn.entity.software.base.SoftwareProcess;
import org.apache.brooklyn.entity.software.base.VanillaWindowsProcess;
import org.apache.brooklyn.entity.software.base.test.location.WindowsTestFixture;
import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.core.task.TaskPredicates;
import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
import org.apache.brooklyn.util.text.StringPredicates;
import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* Tests Windows YAML blueprint features.
*/
@Test
public class WindowsYamlLiveTest extends AbstractWindowsYamlTest {
// set EXISTING_WINDOWS_TEST_USER_PASS_HOST_ENV_VAR as per WindowsTestFixture to re-use existing machines
private static final Logger log = LoggerFactory.getLogger(WindowsYamlLiveTest.class);
/**
* Maps from the task names that are used to the names used in log/exception messages.
*/
private static final Map<String, String> TASK_REGEX_TO_COMMAND = ImmutableMap.<String, String>builder()
.put("winrm: pre-install-command.*", "pre-install-command")
.put("winrm: install.*", "install-command")
.put("winrm: post-install-command.*", "post-install-command")
.put("winrm: customize.*", "customize-command")
.put("winrm: pre-launch-command.*", "pre-launch-command")
.put("winrm: launch.*", "launch-command")
.put("winrm: post-launch-command.*", "post-launch-command")
.put("winrm: stop-command.*", "stop-command")
.put("winrm: is-running-command.*", "is-running-command")
.build();
protected List<String> yamlLocation;
protected MachineProvisioningLocation<WinRmMachineLocation> location;
protected WinRmMachineLocation machine;
protected Entity app;
protected boolean useDefaultProperties() {
return true;
}
@BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
super.setUp();
location = WindowsTestFixture.setUpWindowsLocation(mgmt());
machine = location.obtain(ImmutableMap.of());
yamlLocation = ImmutableList.of(
"location:",
" byon:",
" hosts:",
" - winrm: "+machine.getAddress().getHostAddress()
// this is the default, probably not necessary but kept for posterity
+":5985",
" password: "+JavaStringEscapes.wrapJavaString(machine.config().get(WinRmMachineLocation.PASSWORD)),
" user: "+machine.config().get(WinRmMachineLocation.USER),
" osFamily: windows");
}
@AfterClass(alwaysRun = true)
public void tearDownClass() throws Exception {
try {
if (location != null) location.release(machine);
} catch (Throwable t) {
log.error("Caught exception in tearDownClass method", t);
} finally {
super.tearDown();
}
}
@BeforeMethod(alwaysRun = true)
@Override
public void setUp() {
// no-op; everything done @BeforeClass
}
@AfterMethod(alwaysRun = true)
@Override
public void tearDown() {
try {
if (app != null) Entities.destroy(app, true);
} catch (Throwable t) {
log.error("Caught exception in tearDown method", t);
} finally {
app = null;
}
}
@Override
protected ManagementContextInternal mgmt() {
return (ManagementContextInternal) super.mgmt();
}
@Test(groups="Live")
public void testPowershellMinimalist() throws Exception {
Map<String, String> cmds = ImmutableMap.<String, String>builder()
.put("myarg", "myval")
.put("launch.powershell.command", JavaStringEscapes.wrapJavaString("& \"$Env:INSTALL_DIR\\exit0.ps1\""))
.put("checkRunning.powershell.command", JavaStringEscapes.wrapJavaString("& \"$Env:INSTALL_DIR\\exit0.bat\""))
.build();
Map<String, List<String>> stdouts = ImmutableMap.of();
runWindowsApp(cmds, stdouts, true, null);
}
@Test(groups="Live")
public void testPowershell() throws Exception {
Map<String, String> cmds = ImmutableMap.<String, String>builder()
.put("myarg", "myval")
.put("pre.install.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("install.powershell.command", "\"& c:\\\\echoMyArg.ps1 -myarg myInstall\"")
.put("post.install.powershell.command", "\"& c:\\\\echoArg.bat myPostInstall\"")
.put("customize.powershell.command", "\"& c:\\\\echoFreemarkerMyarg.bat\"")
.put("pre.launch.powershell.command", "\"& c:\\\\echoFreemarkerMyarg.ps1\"")
.put("launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("post.launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("checkRunning.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("stop.powershell.command", "\"& c:\\\\exit0.ps1\"")
.build();
Map<String, List<String>> stdouts = ImmutableMap.<String, List<String>>builder()
.put("winrm: install.*", ImmutableList.of("myInstall"))
.put("winrm: post-install-command.*", ImmutableList.of("myPostInstall"))
.put("winrm: customize.*", ImmutableList.of("myval"))
.put("winrm: pre-launch-command.*", ImmutableList.of("myval"))
.build();
runWindowsApp(cmds, stdouts, false, null);
}
@Test(groups="Live")
public void testBatch() throws Exception {
Map<String, String> cmds = ImmutableMap.<String, String>builder()
.put("myarg", "myval")
.put("pre.install.command", "\"PowerShell -NonInteractive -NoProfile -Command c:\\\\exit0.ps1\"")
.put("install.command", "\"PowerShell -NonInteractive -NoProfile -Command c:\\\\echoMyArg.ps1 -myarg myInstall\"")
.put("post.install.command", "\"c:\\\\echoArg.bat myPostInstall\"")
.put("customize.command", "\"c:\\\\echoFreemarkerMyarg.bat\"")
.put("pre.launch.command", "\"PowerShell -NonInteractive -NoProfile -Command c:\\\\echoFreemarkerMyarg.ps1\"")
.put("launch.command", "\"PowerShell -NonInteractive -NoProfile -Command c:\\\\exit0.ps1\"")
.put("post.launch.command", "\"PowerShell -NonInteractive -NoProfile -Command c:\\\\exit0.ps1\"")
.put("checkRunning.command", "\"PowerShell -NonInteractive -NoProfile -Command c:\\\\exit0.ps1\"")
.put("stop.command", "\"PowerShell -NonInteractive -NoProfile -Command c:\\\\exit0.ps1\"")
.build();
Map<String, List<String>> stdouts = ImmutableMap.<String, List<String>>builder()
.put("winrm: install.*", ImmutableList.of("myInstall"))
.put("winrm: post-install-command.*", ImmutableList.of("myPostInstall"))
.put("winrm: customize.*", ImmutableList.of("myval"))
.put("winrm: pre-launch-command.*", ImmutableList.of("myval"))
.build();
runWindowsApp(cmds, stdouts, false, null);
}
@Test(groups="Live")
public void testPowershellExit1() throws Exception {
Map<String, String> cmds = ImmutableMap.<String, String>builder()
.put("myarg", "myval")
.put("pre.install.powershell.command", "\"& c:\\\\exit1.ps1\"")
.put("install.powershell.command", "\"& c:\\\\echoMyArg.ps1 -myarg myInstall\"")
.put("post.install.powershell.command", "\"& c:\\\\echoArg.bat myPostInstall\"")
.put("customize.powershell.command", "\"& c:\\\\echoFreemarkerMyarg.bat\"")
.put("pre.launch.powershell.command", "\"& c:\\\\echoFreemarkerMyarg.ps1\"")
.put("launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("post.launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("checkRunning.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("stop.powershell.command", "\"& c:\\\\exit0.ps1\"")
.build();
Map<String, List<String>> stdouts = ImmutableMap.of();
runWindowsApp(cmds, stdouts, false, "winrm: pre-install-command.*");
}
// FIXME Failing to match the expected exception, but looks fine! Needs more investigation.
@Test(groups="Live")
public void testPowershellCheckRunningExit1() throws Exception {
Map<String, String> cmds = ImmutableMap.<String, String>builder()
.put("myarg", "myval")
.put("pre.install.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("install.powershell.command", "\"& c:\\\\echoMyArg.ps1 -myarg myInstall\"")
.put("post.install.powershell.command", "\"& c:\\\\echoArg.bat myPostInstall\"")
.put("customize.powershell.command", "\"& c:\\\\echoFreemarkerMyarg.bat\"")
.put("pre.launch.powershell.command", "\"& c:\\\\echoFreemarkerMyarg.ps1\"")
.put("launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("post.launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("checkRunning.powershell.command", "\"& c:\\\\exit1.ps1\"")
.put("stop.powershell.command", "\"& c:\\\\exit0.ps1\"")
.build();
Map<String, List<String>> stdouts = ImmutableMap.of();
runWindowsApp(cmds, stdouts, false, "winrm: is-running-command.*");
}
// FIXME Needs more work to get the stop's task that failed, so can assert got the right error message
@Test(groups="Live")
public void testPowershellStopExit1() throws Exception {
Map<String, String> cmds = ImmutableMap.<String, String>builder()
.put("myarg", "myval")
.put("pre.install.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("install.powershell.command", "\"& c:\\\\echoMyArg.ps1 -myarg myInstall\"")
.put("post.install.powershell.command", "\"& c:\\\\echoArg.bat myPostInstall\"")
.put("customize.powershell.command", "\"& c:\\\\echoFreemarkerMyarg.bat\"")
.put("pre.launch.powershell.command", "\"& c:\\\\echoFreemarkerMyarg.ps1\"")
.put("launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("post.launch.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("checkRunning.powershell.command", "\"& c:\\\\exit0.ps1\"")
.put("stop.powershell.command", "\"& c:\\\\exit1.ps1\"")
.build();
Map<String, List<String>> stdouts = ImmutableMap.of();
runWindowsApp(cmds, stdouts, false, "winrm: stop-command.*");
}
protected void runWindowsApp(Map<String, String> commands, Map<String, List<String>> stdouts, boolean useInstallDir, String taskRegexFailed) throws Exception {
String cmdFailed = (taskRegexFailed == null) ? null : TASK_REGEX_TO_COMMAND.get(taskRegexFailed);
List<String> yaml = Lists.newArrayList();
yaml.addAll(yamlLocation);
String prefix = useInstallDir ? "" : "c:\\";
yaml.addAll(ImmutableList.of(
"services:",
"- type: org.apache.brooklyn.entity.software.base.VanillaWindowsProcess",
" brooklyn.config:",
" templates.preinstall:",
" classpath://org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.bat: c:\\echoFreemarkerMyarg.bat",
" classpath://org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.ps1: c:\\echoFreemarkerMyarg.ps1",
" files.preinstall:",
" classpath://org/apache/brooklyn/camp/brooklyn/echoArg.bat: "+prefix+"echoArg.bat",
" classpath://org/apache/brooklyn/camp/brooklyn/echoMyArg.ps1: "+prefix+"echoMyArg.ps1",
" classpath://org/apache/brooklyn/camp/brooklyn/exit0.bat: "+prefix+"exit0.bat",
" classpath://org/apache/brooklyn/camp/brooklyn/exit1.bat: "+prefix+"exit1.bat",
" classpath://org/apache/brooklyn/camp/brooklyn/exit0.ps1: "+prefix+"exit0.ps1",
" classpath://org/apache/brooklyn/camp/brooklyn/exit1.ps1: "+prefix+"exit1.ps1",
""));
for (Map.Entry<String, String> entry : commands.entrySet()) {
yaml.add(" "+entry.getKey()+": "+entry.getValue());
}
if (Strings.isBlank(cmdFailed)) {
app = createAndStartApplication(Joiner.on("\n").join(yaml));
waitForApplicationTasks(app);
log.info("App started:");
Dumper.dumpInfo(app);
VanillaWindowsProcess entity = (VanillaWindowsProcess) app.getChildren().iterator().next();
EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
assertStreams(entity, stdouts);
} else if (cmdFailed.equals("stop-command")) {
app = createAndStartApplication(Joiner.on("\n").join(yaml));
waitForApplicationTasks(app);
log.info("App started:");
Dumper.dumpInfo(app);
VanillaWindowsProcess entity = (VanillaWindowsProcess) app.getChildren().iterator().next();
EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
entity.stop();
assertSubTaskFailures(entity, ImmutableMap.of(taskRegexFailed, StringPredicates.containsLiteral("for "+cmdFailed)));
} else {
try {
app = createAndStartApplication(Joiner.on("\n").join(yaml));
fail("start should have failed for app="+app);
} catch (Exception e) {
if (!e.toString().contains("invalid result") || !e.toString().contains("for "+cmdFailed)) throw e;
}
}
}
@Test(groups="Live")
public void testEnvVarResolution() throws Exception {
List<String> yaml = Lists.newArrayList();
yaml.addAll(yamlLocation);
String in = "%KEY1%: %ADDR_RESOLVED%";
yaml.addAll(ImmutableList.of(
"services:",
" - type: org.apache.brooklyn.entity.software.base.VanillaWindowsProcess",
" brooklyn.config:",
" install.command: "+JavaStringEscapes.wrapJavaString("echo "+in),
" customize.command: "+JavaStringEscapes.wrapJavaString("echo "+in),
" launch.command: "+JavaStringEscapes.wrapJavaString("echo "+in),
" stop.command: echo true",
" checkRunning.command: echo true",
" shell.env:",
" KEY1: Address",
" ADDR_RESOLVED: $brooklyn:attributeWhenReady(\"host.address\")"));
app = createAndStartApplication(Joiner.on("\n").join(yaml));
waitForApplicationTasks(app);
log.info("App started:");
Dumper.dumpInfo(app);
Entity win = Iterables.getOnlyElement(app.getChildren());
String out = "Address: "+win.sensors().get(SoftwareProcess.ADDRESS);
assertPhaseStreamSatisfies(win, "install", "stdout", Predicates.equalTo(in));
assertPhaseStreamSatisfies(win, "customize", "stdout", Predicates.equalTo(out));
assertPhaseStreamSatisfies(win, "launch", "stdout", Predicates.equalTo(out));
}
@Test(groups="Live")
public void testDifferentLogLevels() throws Exception {
List<String> yaml = Lists.newArrayList();
yaml.addAll(yamlLocation);
String hostMsg = "Host Message";
String progressMsg = "Progress Message";
String outputMsg = "Output Message";
String errMsg = "Error Message";
String warningMsg = "Warning Message";
String verboseMsg = "Verbose Message";
String debugMsg = "Debug Message";
String informationMsg = "Information Message";
String cmd =
"$DebugPreference = \"Continue\"\n" +
"$VerbosePreference = \"Continue\"\n" +
"\n" +
"Write-Host \"" + hostMsg + "\"\n" +
"Write-Output \"" + outputMsg + "\"\n" +
"Write-Progress \"" + progressMsg + "\"\n" +
"Write-Error \"" + errMsg + "\" \n" +
"Write-Warning \"" + warningMsg + "\"\n" +
"Write-Verbose \"" + verboseMsg + "\"\n" +
"Write-Debug \"" + debugMsg + "\" \n" +
// "Write-Information \"" + informationMsg + "\" \n" + // Write-Host is a wrapper for Write-Information since PowerShell 5.0
"";
yaml.addAll(ImmutableList.of(
"services:",
" - type: org.apache.brooklyn.entity.software.base.VanillaWindowsProcess",
" brooklyn.config:",
" checkRunning.command: echo true",
" install.powershell.command: |",
Strings.indent(8, cmd) ));
app = createAndStartApplication(Joiner.on("\n").join(yaml));
waitForApplicationTasks(app);
log.info("App started:");
Dumper.dumpInfo(app);
Entity win = Iterables.getOnlyElement(app.getChildren());
// Verify stdout output.
assertPhaseStreamSatisfies(win, "install", "stdout", s ->
s.trim().equalsIgnoreCase(hostMsg+"\n"+outputMsg));
// Verify WinRM output to contain error, warning, verbose and debug messages in CLI XML format.
assertPhaseStreamSatisfies(win, "install", PlainWinRmExecTaskFactory.WINRM_STREAM, s ->
Strings.countOccurrences(s, errMsg + "_x000D__x000A_</S>") == 1 && // error always includes \r\n markers
Strings.countOccurrences(s, warningMsg + "</S>") == 1 &&
Strings.countOccurrences(s, verboseMsg + "</S>") == 1 &&
Strings.countOccurrences(s, debugMsg + "</S>") == 1 &&
Strings.countOccurrences(s, "<Obj S=\"progress\"") == 1 && // Progress is a composite object;
Strings.countOccurrences(s, progressMsg + "</AV>") == 1 && // message is wrapped with 'AV' tag.
// host and output are not included
Strings.countOccurrences(s, hostMsg + "</S>") == 0 &&
Strings.countOccurrences(s, outputMsg + "</S>") == 0 &&
Strings.countOccurrences(s, informationMsg + "</S>") == 0);
}
private void assertPhaseStreamSatisfies(Entity entity, String phase, String stream, Predicate<String> check) {
WrappedStream streamV = getWrappedStream(entity, phase, stream);
Asserts.assertNotNull(streamV, "phase "+phase+" stream "+stream+" not found");
Asserts.assertThat(streamV.streamContents.get().trim(), check, "phase "+phase+" stream "+stream+" not as expected: '"+streamV.streamContents.get().trim()+"'");
}
private WrappedStream getWrappedStream(Entity entity, String phase, String stream) {
Optional<Task<?>> t = findTaskOrSubTask(entity, TaskPredicates.displayNameSatisfies(StringPredicates.startsWith("winrm: "+ phase)));
Asserts.assertTrue(t.isPresent(), "phase "+ phase +" not found in tasks");
WrappedStream streamV = BrooklynTaskTags.stream(t.get(), stream);
return streamV;
}
@Override
protected Logger getLogger() {
return log;
}
}