You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/09/23 12:23:28 UTC

[1/8] incubator-brooklyn git commit: WinRmDriver.copyTo: handle different url types

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 29954710c -> 93f6f0f6a


WinRmDriver.copyTo: handle different url types


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/b9d95a4d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/b9d95a4d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/b9d95a4d

Branch: refs/heads/master
Commit: b9d95a4dd88704a4d57f6fcd094e69968473205a
Parents: 874a3a8
Author: Aled Sage <al...@gmail.com>
Authored: Tue Sep 22 20:06:35 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 23 01:32:48 2015 +0100

----------------------------------------------------------------------
 .../base/AbstractSoftwareProcessWinRmDriver.java    | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b9d95a4d/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
index 7091147..f0d0725 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
@@ -32,8 +32,10 @@ import org.apache.brooklyn.entity.software.base.lifecycle.NativeWindowsScriptRun
 import org.apache.brooklyn.entity.software.base.lifecycle.WinRmExecuteHelper;
 import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
 import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.exceptions.ReferenceWithError;
 import org.apache.brooklyn.util.repeat.Repeater;
+import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
 import org.python.core.PyException;
@@ -171,7 +173,19 @@ public abstract class AbstractSoftwareProcessWinRmDriver extends AbstractSoftwar
         if (createParentDir) {
             createDirectory(getDirectory(target), "Creating resource directory");
         }
-        return copyTo(new File(source), target);
+        
+        InputStream stream = null;
+        try {
+            Tasks.setBlockingDetails("retrieving resource "+source+" for copying across");
+            stream = resource.getResourceFromUrl(source);
+            Tasks.setBlockingDetails("copying resource "+source+" to server");
+            return copyTo(stream, target);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        } finally {
+            Tasks.setBlockingDetails(null);
+            if (stream != null) Streams.closeQuietly(stream);
+        }
     }
 
     @Override


[3/8] incubator-brooklyn git commit: Testing winrm exit codes

Posted by al...@apache.org.
Testing winrm exit codes


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/10071a92
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/10071a92
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/10071a92

Branch: refs/heads/master
Commit: 10071a928c57aa3178442d9f72f22d47798ff7ca
Parents: 2995471
Author: Aled Sage <al...@gmail.com>
Authored: Mon Sep 21 21:36:11 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 23 01:32:48 2015 +0100

----------------------------------------------------------------------
 ...laWindowsProcessWinrmExitStatusLiveTest.java | 291 ++++++++++
 ...nillaWindowsProcessWinrmStreamsLiveTest.java |  80 ++-
 .../location/WinRmMachineLocationLiveTest.java  | 563 ++++++++++++++++++-
 .../base/test/location/WindowsTestFixture.java  |  71 +++
 usage/camp/pom.xml                              |   6 +
 .../camp/brooklyn/WindowsYamlLiveTest.java      | 410 ++++++++++++++
 .../apache/brooklyn/camp/brooklyn/echoArg.bat   |  19 +
 .../camp/brooklyn/echoFreemarkerMyarg.bat       |  18 +
 .../camp/brooklyn/echoFreemarkerMyarg.ps1       |  18 +
 .../apache/brooklyn/camp/brooklyn/echoMyArg.ps1 |  22 +
 .../org/apache/brooklyn/camp/brooklyn/exit0.bat |  18 +
 .../org/apache/brooklyn/camp/brooklyn/exit0.ps1 |  18 +
 .../org/apache/brooklyn/camp/brooklyn/exit1.bat |  18 +
 .../org/apache/brooklyn/camp/brooklyn/exit1.ps1 |  19 +
 14 files changed, 1521 insertions(+), 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmExitStatusLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmExitStatusLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmExitStatusLiveTest.java
new file mode 100644
index 0000000..998d987
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmExitStatusLiveTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.fail;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.entity.Attributes;
+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.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.software.base.test.location.WindowsTestFixture;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.time.Duration;
+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.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class VanillaWindowsProcessWinrmExitStatusLiveTest {
+    private static final Logger LOG = LoggerFactory.getLogger(VanillaWindowsProcessWinrmExitStatusLiveTest.class);
+
+    private static final String INVALID_CMD = "thisCommandDoesNotExistAEFafiee3d";
+    
+    protected ManagementContextInternal mgmt;
+    protected TestApplication app;
+    protected MachineProvisioningLocation<WinRmMachineLocation> location;
+    protected WinRmMachineLocation machine;
+
+    @BeforeClass(alwaysRun=true)
+    public void setUpClass() throws Exception {
+        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault());
+
+        location = WindowsTestFixture.setUpWindowsLocation(mgmt);
+        machine = location.obtain(ImmutableMap.of());
+    }
+
+    @AfterClass(alwaysRun=true)
+    public void tearDownClass() throws Exception {
+        try {
+            try {
+                if (location != null) location.release(machine);
+            } finally {
+                if (mgmt != null) Entities.destroyAll(mgmt);
+            }
+        } catch (Throwable t) {
+            LOG.error("Caught exception in tearDownClass method", t);
+        } finally {
+            mgmt = null;
+        }
+    }
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true);
+        app = ApplicationBuilder.newManagedApp(appSpec, mgmt);
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        try {
+            try {
+                if (app != null) Entities.destroy(app);
+            } catch (Throwable t) {
+                LOG.error("Caught exception in tearDown method", t);
+            }
+        } finally {
+            app = null;
+        }
+    }
+
+    @Test(groups = "Live")
+    public void testExecWithZeroExitCodes() {
+        VanillaWindowsProcess entity = app.createAndManageChild(EntitySpec.create(VanillaWindowsProcess.class)
+                .configure(VanillaWindowsProcess.PRE_INSTALL_COMMAND, "echo preinstall")
+                .configure(VanillaWindowsProcess.INSTALL_COMMAND, "echo install")
+                .configure(VanillaWindowsProcess.POST_INSTALL_COMMAND, "echo postinstall")
+                .configure(VanillaWindowsProcess.CUSTOMIZE_COMMAND, "echo customize")
+                .configure(VanillaWindowsProcess.PRE_LAUNCH_COMMAND, "echo prelaunch")
+                .configure(VanillaWindowsProcess.LAUNCH_COMMAND, "echo launch")
+                .configure(VanillaWindowsProcess.POST_LAUNCH_COMMAND, "echo postlaunch")
+                .configure(VanillaWindowsProcess.CHECK_RUNNING_COMMAND, "echo checkrunning")
+                .configure(VanillaWindowsProcess.STOP_COMMAND, "echo stop"));
+        
+        app.start(ImmutableList.of(machine));
+        LOG.info("app started; asserting up");
+        EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+        EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+        
+        entity.stop();
+        LOG.info("stopping entity");
+        EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED);
+        EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, false);
+    }
+
+    @Test(groups = "Live")
+    public void testExecPsWithZeroExitCodes() {
+        VanillaWindowsProcess entity = app.createAndManageChild(EntitySpec.create(VanillaWindowsProcess.class)
+                .configure(VanillaWindowsProcess.PRE_INSTALL_POWERSHELL_COMMAND, "Write-Host preinstall")
+                .configure(VanillaWindowsProcess.INSTALL_POWERSHELL_COMMAND, "Write-Host install")
+                .configure(VanillaWindowsProcess.POST_INSTALL_POWERSHELL_COMMAND, "Write-Host postinstall")
+                .configure(VanillaWindowsProcess.CUSTOMIZE_POWERSHELL_COMMAND, "Write-Host customize")
+                .configure(VanillaWindowsProcess.PRE_LAUNCH_POWERSHELL_COMMAND, "Write-Host prelaunch")
+                .configure(VanillaWindowsProcess.LAUNCH_POWERSHELL_COMMAND, "Write-Host launch")
+                .configure(VanillaWindowsProcess.POST_LAUNCH_POWERSHELL_COMMAND, "Write-Host postlaunch")
+                .configure(VanillaWindowsProcess.CHECK_RUNNING_POWERSHELL_COMMAND, "Write-Host checkrunning")
+                .configure(VanillaWindowsProcess.STOP_POWERSHELL_COMMAND, "Write-Host stop"));
+        
+        app.start(ImmutableList.of(machine));
+        LOG.info("app started; asserting up");
+        EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+        EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+        
+        entity.stop();
+        LOG.info("stopping entity");
+        EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED);
+        EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, false);
+    }
+
+    @Test(groups = "Live")
+    public void testPreInstallNonZeroExitCode() {
+        runExecNonZeroExitCode("pre-install-command");
+    }
+
+    @Test(groups = "Live")
+    public void testInstallNonZeroExitCode() {
+        runExecNonZeroExitCode("install-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPostInstallNonZeroExitCode() {
+        runExecNonZeroExitCode("post-install-command");
+    }
+
+    @Test(groups = "Live")
+    public void testCustomizeNonZeroExitCode() {
+        runExecNonZeroExitCode("customize-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPreLaunchNonZeroExitCode() {
+        runExecNonZeroExitCode("pre-launch-command");
+    }
+
+    @Test(groups = "Live")
+    public void testLaunchNonZeroExitCode() {
+        runExecNonZeroExitCode("launch-command");
+    }
+
+    @Test(groups = "Live")
+    public void testCheckRunningNonZeroExitCode() {
+        runExecNonZeroExitCode("is-running-command");
+    }
+
+    @Test(groups = "Live")
+    public void testStopNonZeroExitCode() {
+        runExecNonZeroExitCode("stop-command");
+    }
+    
+    @Test(groups = "Live")
+    public void testPsPreInstallNonZeroExitCode() {
+        runExecPsNonZeroExitCode("pre-install-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPsInstallNonZeroExitCode() {
+        runExecPsNonZeroExitCode("install-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPsPostInstallNonZeroExitCode() {
+        runExecPsNonZeroExitCode("post-install-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPsCustomizeNonZeroExitCode() {
+        runExecPsNonZeroExitCode("customize-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPsPreLaunchNonZeroExitCode() {
+        runExecPsNonZeroExitCode("pre-launch-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPsLaunchNonZeroExitCode() {
+        runExecPsNonZeroExitCode("launch-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPsCheckRunningNonZeroExitCode() {
+        runExecPsNonZeroExitCode("is-running-command");
+    }
+
+    @Test(groups = "Live")
+    public void testPsStopNonZeroExitCode() {
+        runExecPsNonZeroExitCode("stop-command");
+    }
+
+    protected void runExecNonZeroExitCode(String phase) {
+        VanillaWindowsProcess entity = app.createAndManageChild(EntitySpec.create(VanillaWindowsProcess.class)
+                .configure(VanillaWindowsProcess.PRE_INSTALL_COMMAND, phase.equals("pre-install-command") ? INVALID_CMD : "echo install")
+                .configure(VanillaWindowsProcess.INSTALL_COMMAND, phase.equals("install-command") ? INVALID_CMD : "echo install")
+                .configure(VanillaWindowsProcess.POST_INSTALL_COMMAND, phase.equals("post-install-command") ? INVALID_CMD : "echo postinstall")
+                .configure(VanillaWindowsProcess.CUSTOMIZE_COMMAND, phase.equals("customize-command") ? INVALID_CMD : "echo customize")
+                .configure(VanillaWindowsProcess.PRE_LAUNCH_COMMAND, phase.equals("pre-launch-command") ? INVALID_CMD : "echo prelaunch")
+                .configure(VanillaWindowsProcess.LAUNCH_COMMAND, phase.equals("launch-command") ? INVALID_CMD : "echo launch")
+                .configure(VanillaWindowsProcess.POST_LAUNCH_COMMAND, phase.equals("post-launch-command") ? INVALID_CMD : "echo postlaunch")
+                .configure(VanillaWindowsProcess.CHECK_RUNNING_COMMAND, phase.equals("is-running-command") ? INVALID_CMD : "echo checkrunning")
+                .configure(VanillaWindowsProcess.STOP_COMMAND, phase.equals("stop-command") ? INVALID_CMD : "echo stop")
+                .configure(BrooklynConfigKeys.START_TIMEOUT, Duration.ONE_MINUTE));
+
+        if (phase.equals("stop-command")) {
+            app.start(ImmutableList.of(machine));
+            try {
+                entity.stop();
+                fail();
+            } catch (Exception e) {
+                if (!(e.toString().contains("invalid result") && e.toString().contains("for "+phase))) throw e;
+            }
+        } else {
+            try {
+                app.start(ImmutableList.of(machine));
+                fail();
+            } catch (Exception e) {
+                if (!(e.toString().contains("invalid result") && e.toString().contains("for "+phase))) throw e;
+            }
+        }
+    }
+    
+    protected void runExecPsNonZeroExitCode(String phase) {
+        VanillaWindowsProcess entity = app.createAndManageChild(EntitySpec.create(VanillaWindowsProcess.class)
+                .configure(VanillaWindowsProcess.PRE_INSTALL_POWERSHELL_COMMAND, phase.equals("pre-install-command") ? INVALID_CMD : "Write-Host install")
+                .configure(VanillaWindowsProcess.INSTALL_POWERSHELL_COMMAND, phase.equals("install-command") ? INVALID_CMD : "Write-Host install")
+                .configure(VanillaWindowsProcess.POST_INSTALL_POWERSHELL_COMMAND, phase.equals("post-install-command") ? INVALID_CMD : "Write-Host postinstall")
+                .configure(VanillaWindowsProcess.CUSTOMIZE_POWERSHELL_COMMAND, phase.equals("customize-command") ? INVALID_CMD : "Write-Host customize")
+                .configure(VanillaWindowsProcess.PRE_LAUNCH_POWERSHELL_COMMAND, phase.equals("pre-launch-command") ? INVALID_CMD : "Write-Host prelaunch")
+                .configure(VanillaWindowsProcess.LAUNCH_POWERSHELL_COMMAND, phase.equals("launch-command") ? INVALID_CMD : "Write-Host launch")
+                .configure(VanillaWindowsProcess.POST_LAUNCH_POWERSHELL_COMMAND, phase.equals("post-launch-command") ? INVALID_CMD : "Write-Host postlaunch")
+                .configure(VanillaWindowsProcess.CHECK_RUNNING_POWERSHELL_COMMAND, phase.equals("is-running-command") ? INVALID_CMD : "Write-Host checkrunning")
+                .configure(VanillaWindowsProcess.STOP_POWERSHELL_COMMAND, phase.equals("stop-command") ? INVALID_CMD : "Write-Host stop")
+                .configure(BrooklynConfigKeys.START_TIMEOUT, Duration.ONE_MINUTE));
+
+        if (phase.equals("stop-command")) {
+            app.start(ImmutableList.of(machine));
+            try {
+                entity.stop();
+                fail();
+            } catch (Exception e) {
+                if (!(e.toString().contains("invalid result") && e.toString().contains("for "+phase))) throw e;
+            }
+        } else {
+            try {
+                app.start(ImmutableList.of(machine));
+                fail();
+            } catch (Exception e) {
+                if (!(e.toString().contains("invalid result") && e.toString().contains("for "+phase))) throw e;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmStreamsLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmStreamsLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmStreamsLiveTest.java
index 2ee310b..fdc4c59 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmStreamsLiveTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmStreamsLiveTest.java
@@ -18,41 +18,78 @@
  */
 package org.apache.brooklyn.entity.software.base;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
 import org.apache.brooklyn.api.entity.EntitySpec;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.location.jclouds.JcloudsLocation;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+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.test.entity.TestApplication;
+import org.apache.brooklyn.entity.software.base.test.location.WindowsTestFixture;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+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 java.util.Map;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 
 public class VanillaWindowsProcessWinrmStreamsLiveTest extends AbstractSoftwareProcessStreamsTest {
-    private Location location;
+    
+    private static final Logger LOG = LoggerFactory.getLogger(VanillaWindowsProcessWinrmStreamsLiveTest.class);
+
+    protected MachineProvisioningLocation<WinRmMachineLocation> location;
+    protected WinRmMachineLocation machine;
+    
+    // Using BeforeClass so that uses just a single VM for all tests
+    @BeforeClass(alwaysRun = true)
+    public void setUpClass() throws Exception {
+        super.setUp();
+        if (app != null) Entities.destroy(app);
+        
+        location = WindowsTestFixture.setUpWindowsLocation(mgmt);
+        machine = location.obtain(ImmutableMap.of());
+    }
+
+    @AfterClass(alwaysRun = true)
+    public void tearDownClass() throws Exception {
+        try {
+            if (location != null && machine != null) location.release(machine);
+        } catch (Throwable t) {
+            LOG.error("Caught exception in tearDownClass method", t);
+        } finally {
+            super.tearDown();
+        }
+    }
 
-    @BeforeMethod(alwaysRun=true)
+    @BeforeMethod(alwaysRun = true)
     @Override
     public void setUp() throws Exception {
-        super.setUp();
+        app = ApplicationBuilder.newManagedApp(TestApplication.class, mgmt);
+    }
 
-        Map<String, Object> config = ImmutableMap.<String, Object>builder()
-                .put("inboundPorts", ImmutableList.of(5985, 3389))
-                .put("osFamily", "windows")
-                .put("displayName", "AWS Oregon (Windows)")
-                .put("imageOwner", "801119661308")
-                .put("imageNameRegex", "Windows_Server-2012-R2_RTM-English-64Bit-Base-.*")
-                .put("hardwareId", "m3.medium")
-                .put("checkRunning.command", "echo true")
-                .put("useJcloudsSshInit", false)
-                .build();
-        location = ((JcloudsLocation)mgmt.getLocationRegistry().resolve("jclouds:aws-ec2:us-west-1", config)).obtain();
+    @AfterMethod(alwaysRun = true)
+    @Override
+    public void tearDown() throws Exception {
+        try {
+            if (app != null) Entities.destroy(app);
+        } catch (Throwable t) {
+            LOG.error("Caught exception in tearDown method", t);
+        } finally {
+            app = null;
+        }
     }
 
     @Test(groups = "Live")
     @Override
     public void testGetsStreams() {
         VanillaWindowsProcess entity = app.createAndManageChild(EntitySpec.create(VanillaWindowsProcess.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true)
                 .configure(VanillaSoftwareProcess.PRE_INSTALL_COMMAND, "echo " + getCommands().get("winrm: pre-install-command.*"))
                 .configure(VanillaSoftwareProcess.INSTALL_COMMAND, "echo " + getCommands().get("winrm: install.*"))
                 .configure(VanillaSoftwareProcess.POST_INSTALL_COMMAND, "echo " + getCommands().get("winrm: post-install-command.*"))
@@ -61,13 +98,14 @@ public class VanillaWindowsProcessWinrmStreamsLiveTest extends AbstractSoftwareP
                 .configure(VanillaSoftwareProcess.LAUNCH_COMMAND, "echo " + getCommands().get("winrm: launch.*"))
                 .configure(VanillaSoftwareProcess.POST_LAUNCH_COMMAND, "echo " + getCommands().get("winrm: post-launch-command.*"))
                 .configure(VanillaSoftwareProcess.CHECK_RUNNING_COMMAND, "echo true"));
-        app.start(ImmutableList.of(location));
+        app.start(ImmutableList.of(machine));
         assertStreams(entity);
     }
 
     @Test(groups = "Live")
     public void testGetsStreamsPowerShell() {
         VanillaWindowsProcess entity = app.createAndManageChild(EntitySpec.create(VanillaWindowsProcess.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true)
                 .configure(VanillaWindowsProcess.PRE_INSTALL_POWERSHELL_COMMAND, "echo " + getCommands().get("winrm: pre-install-command.*"))
                 .configure(VanillaWindowsProcess.INSTALL_POWERSHELL_COMMAND, "echo " + getCommands().get("winrm: install.*"))
                 .configure(VanillaWindowsProcess.POST_INSTALL_POWERSHELL_COMMAND, "echo " + getCommands().get("winrm: post-install-command.*"))
@@ -76,7 +114,7 @@ public class VanillaWindowsProcessWinrmStreamsLiveTest extends AbstractSoftwareP
                 .configure(VanillaWindowsProcess.LAUNCH_POWERSHELL_COMMAND, "echo " + getCommands().get("winrm: launch.*"))
                 .configure(VanillaWindowsProcess.POST_LAUNCH_POWERSHELL_COMMAND, "echo " + getCommands().get("winrm: post-launch-command.*"))
                 .configure(VanillaWindowsProcess.CHECK_RUNNING_POWERSHELL_COMMAND, "echo true"));
-        app.start(ImmutableList.of(location));
+        app.start(ImmutableList.of(machine));
         assertStreams(entity);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
index dd4dee7..615eda7 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
@@ -19,76 +19,581 @@
 package org.apache.brooklyn.entity.software.base.test.location;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
 
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.internal.BrooklynProperties;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.test.entity.TestApplication;
-import org.apache.brooklyn.location.jclouds.JcloudsLocation;
-
-import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
-
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.time.Time;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
-import org.apache.brooklyn.location.jclouds.JcloudsWinRmMachineLocation;
 
+import com.google.api.client.util.Charsets;
+import com.google.common.base.Joiner;
+import com.google.common.base.Stopwatch;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
 
+/**
+ * Tests execution of commands (batch and powershell) on Windows over WinRM, and of
+ * file upload.
+ * 
+ * There are limitations with what is supported by PyWinRM. These are highlighted in
+ * tests marked as "WIP" (see individual tests).
+ * 
+ * These limitations are documented in docs/guide/yaml/winrm/index.md.
+ * Please update the docs if you encountered new situations, or change the behaviuor 
+ * of existing use-cases.
+ */
 public class WinRmMachineLocationLiveTest {
-    
-    // FIXME failing locally with:
-    //   Caused by: Traceback (most recent call last):
-    //     File "__pyclasspath__/winrm/__init__.py", line 40, in run_cmd
-    //     File "__pyclasspath__/winrm/protocol.py", line 118, in open_shell
-    //     File "__pyclasspath__/winrm/protocol.py", line 190, in send_message
-    //     File "__pyclasspath__/winrm/transport.py", line 112, in send_message
-    //     winrm.exceptions.WinRMTransportError: 500 WinRMTransport. [Errno 20001] getaddrinfo failed
-    //     at org.python.core.PyException.doRaise(PyException.java:226)
 
+    private static final int MAX_EXECUTOR_THREADS = 100;
+
+    /*
+     * TODO: Deferred implementing copyFrom or environment variables.
+     */
+    
     private static final Logger LOG = LoggerFactory.getLogger(BrooklynAppLiveTestSupport.class);
 
-    protected JcloudsLocation loc;
+    private static final String INVALID_CMD = "thisCommandDoesNotExistAEFafiee3d";
+    private static final String PS_ERR_ACTION_PREF_EQ_STOP = "$ErrorActionPreference = \"Stop\"";
+
+    protected MachineProvisioningLocation<WinRmMachineLocation> loc;
     protected TestApplication app;
     protected ManagementContextInternal mgmt;
 
-    private JcloudsWinRmMachineLocation machine;
+    private WinRmMachineLocation machine;
+    
+    private ListeningExecutorService executor;
     
     @BeforeClass(alwaysRun=true)
     public void setUpClass() throws Exception {
+        executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(MAX_EXECUTOR_THREADS));
+        
         mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault());
-        JcloudsLocation loc = (JcloudsLocation) mgmt.getLocationRegistry().resolve("jclouds:aws-ec2:us-west-2", ImmutableMap.<String, Object>builder()
-                .put("inboundPorts", ImmutableList.of(5985, 3389))
-                .put("displayName", "AWS Oregon (Windows)")
-                .put("imageOwner", "801119661308")
-                .put("imageNameRegex", "Windows_Server-2012-R2_RTM-English-64Bit-Base-.*")
-                .put("hardwareId", "m3.medium")
-                .put("useJcloudsSshInit", false)
-                .build());
-        machine = (JcloudsWinRmMachineLocation) loc.obtain();
+        
+        loc = WindowsTestFixture.setUpWindowsLocation(mgmt);
+        machine = loc.obtain(ImmutableMap.of());
     }
-
+    
     @AfterClass(alwaysRun=true)
     public void tearDownClass() throws Exception {
         try {
+            if (executor != null) executor.shutdownNow();
             if (machine != null) loc.release(machine);
             if (mgmt != null) Entities.destroyAll(mgmt);
         } catch (Throwable t) {
             LOG.error("Caught exception in tearDown method", t);
         } finally {
+            executor = null;
             mgmt = null;
         }
     }
 
     @Test(groups="Live")
+    public void testCopyTo() throws Exception {
+        String contents = "abcdef";
+        runCopyTo(contents);
+        runCopyFileTo(contents);
+    }
+    
+    // Takes several minutes to upload/download!
+    @Test(groups="Live")
+    public void testLargeFileCopyTo() throws Exception {
+        String contents = Identifiers.makeRandomId(65537);
+        runCopyTo(contents);
+        runCopyFileTo(contents);
+    }
+    
+    protected void runCopyTo(String contents) throws Exception {
+        String remotePath = "C:\\myfile-"+Identifiers.makeRandomId(4)+".txt";
+        machine.copyTo(new ByteArrayInputStream(contents.getBytes()), remotePath);
+        
+        WinRmToolResponse response = machine.executeScript("type "+remotePath);
+        String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
+        assertEquals(response.getStatusCode(), 0, msg);
+        assertEquals(response.getStdOut().trim(), contents, msg);
+    }
+    
+    protected void runCopyFileTo(String contents) throws Exception {
+        File localFile = File.createTempFile("winrmtest"+Identifiers.makeRandomId(4), ".txt");
+        try {
+            Files.write(contents, localFile, Charsets.UTF_8);
+            String remotePath = "C:\\myfile-"+Identifiers.makeRandomId(4)+".txt";
+            machine.copyTo(localFile, remotePath);
+            
+            WinRmToolResponse response = machine.executeScript("type "+remotePath);
+            String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
+            assertEquals(response.getStatusCode(), 0, msg);
+            assertEquals(response.getStdOut().trim(), contents, msg);
+        } finally {
+            localFile.delete();
+        }
+    }
+    
+    @Test(groups="Live")
     public void testExecScript() throws Exception {
-        WinRmToolResponse response = machine.executeScript("echo true");
-        assertEquals(response.getStatusCode(), 0);
-        assertEquals(response.getStdErr(), "");
+        assertExecSucceeds("echo myline", "myline", "");
+    }
+    
+    /*
+     * TODO Not supported in PyWinRM.
+     * 
+     * Executing (in python):
+     *     import winrm
+     *     s = winrm.Session('52.12.211.247', auth=('Administrator', 'pa55w0rd'))
+     *     r = s.run_cmd("echo first \r\n echo second")
+     * gives just "first".
+     */
+    @Test(groups={"Live", "WIP"})
+    public void testExecMultiLineScript() throws Exception {
+        assertExecSucceeds("echo first" + "\r\n" + "echo second", "first"+"\r\n"+"second", "");
+    }
+    
+    /*
+     * TODO Not supported in PyWinRM. Under the covers, we just concatenate the commands.
+     * See {@link #testExecMultiLineScript()}.
+     */
+    @Test(groups={"Live", "WIP"})
+    public void testExecMultiPartScript() throws Exception {
+        assertExecSucceeds(ImmutableList.of("echo first", "echo second"), "first"+"\r\n"+"second", "");
+    }
+    
+    @Test(groups="Live")
+    public void testExecFailingScript() throws Exception {
+        final String INVALID_CMD = "thisCommandDoesNotExistAEFafiee3d";
+        
+        // Single commands
+        assertExecFails(INVALID_CMD);
+        assertExecFails(ImmutableList.of(INVALID_CMD));
+    }
+
+    @Test(groups="Live")
+    public void testExecScriptExit0() throws Exception {
+        assertExecSucceeds("exit /B 0", "", "");
+        assertExecSucceeds(ImmutableList.of("exit /B 0"), "", "");
+    }
+
+    /*
+     * TODO Not supported in PyWinRM.
+     * 
+     * Executing (in python):
+     *     import winrm
+     *     s = winrm.Session('52.12.211.247', auth=('Administrator', 'pa55w0rd'))
+     *     r = s.run_cmd("exit /B 1")
+     * gives exit code 0.
+     */
+    @Test(groups={"Live", "WIP"})
+    public void testExecScriptExit1() throws Exception {
+        // Single commands
+        assertExecFails("exit /B 1");
+        assertExecFails(ImmutableList.of("exit /B 1"));
+    }
+
+    @Test(groups="Live")
+    public void testExecBatchFileSingleLine() throws Exception {
+        String script = "EXIT /B 0";
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".bat";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecSucceeds(scriptPath, null, "");
+    }
+
+    @Test(groups="Live")
+    public void testExecBatchFileMultiLine() throws Exception {
+        String script = Joiner.on("\n").join(
+                "@ECHO OFF",
+                "echo first", 
+                "echo second", 
+                "EXIT /B 0");
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".bat";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecSucceeds(scriptPath, "first"+"\r\n"+"second", "");
+    }
+
+    @Test(groups="Live")
+    public void testExecBatchFileWithArgs() throws Exception {
+        String script = Joiner.on("\n").join(
+                "@ECHO OFF",
+                "echo got %1", 
+                "echo got %2", 
+                "EXIT /B 0");
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".bat";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecSucceeds(scriptPath+" first second", "got first"+"\r\n"+"got second", "");
+    }
+
+    @Test(groups="Live")
+    public void testExecBatchFileWithExit1() throws Exception {
+        String script = "EXIT /B 1";
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".bat";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecFails(scriptPath);
+    }
+
+    @Test(groups="Live")
+    public void testExecCorruptExe() throws Exception {
+        String exe = "garbage";
+        String exePath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".exe";
+        machine.copyTo(new ByteArrayInputStream(exe.getBytes()), exePath);
+
+        assertExecFails(exePath);
+    }
+
+    @Test(groups="Live")
+    public void testExecFilePs() throws Exception {
+        String script = Joiner.on("\r\n").join(
+                "Write-Host myline", 
+                "exit 0");
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".ps1";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecPsSucceeds(
+                "PowerShell -NonInteractive -NoProfile -Command "+scriptPath,
+                "myline",
+                "");
+    }
+
+    @Test(groups="Live")
+    public void testExecFilePsWithExit1() throws Exception {
+        String script = Joiner.on("\r\n").join(
+                "Write-Host myline", 
+                "exit 1");
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".ps1";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecFails("PowerShell -NonInteractive -NoProfile -Command "+scriptPath);
+    }
+
+    /*
+     * TODO Not supported in PyWinRM - single line .ps1 file with "exit 1" gives an
+     * exit code 0 over PyWinRM, but an exit code 1 when executed locally!
+     * 
+     * Executing (in python):
+     *     import winrm
+     *     s = winrm.Session('52.12.211.247', auth=('Administrator', 'pa55w0rd'))
+     *     r = s.run_cmd("PowerShell -NonInteractive -NoProfile -Command C:\singleLineExit1.ps1")
+     * gives exit code 0.
+     */
+    @Test(groups={"Live", "WIP"})
+    public void testExecFilePsWithSingleLineExit1() throws Exception {
+        String script = "exit 1";
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".ps1";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecFails("PowerShell -NonInteractive -NoProfile -Command "+scriptPath);
+    }
+
+    @Test(groups="Live")
+    public void testExecPsScript() throws Exception {
+        assertExecPsSucceeds("Write-Host myline", "myline", "");
+    }
+    
+    @Test(groups="Live")
+    public void testExecPsMultiLineScript() throws Exception {
+        // Note stdout is "\n" rather than "\r\n" (the latter is returned for run_cmd, versus run_ps)
+        assertExecPsSucceeds("Write-Host first" + "\r\n" + "Write-Host second", "first"+"\n"+"second", "");
+    }
+    
+    @Test(groups="Live")
+    public void testExecPsMultiLineScriptWithoutSlashR() throws Exception {
+        assertExecPsSucceeds("Write-Host first" + "\n" + "Write-Host second", "first"+"\n"+"second", "");
+    }
+    
+    @Test(groups="Live")
+    public void testExecPsMultiPartScript() throws Exception {
+        assertExecPsSucceeds(ImmutableList.of("Write-Host first", "Write-Host second"), "first"+"\n"+"second", "");
+    }
+
+    @Test(groups="Live")
+    public void testExecPsBatchFile() throws Exception {
+        String script = "EXIT /B 0";
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".bat";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecPsSucceeds("& '"+scriptPath+"'", null, "");
+    }
+    
+    @Test(groups="Live")
+    public void testExecPsBatchFileExit1() throws Exception {
+        String script = "EXIT /B 1";
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".bat";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecPsFails("& '"+scriptPath+"'");
+    }
+
+    /*
+     * TODO Not supported in PyWinRM - gives exit status 1, rather than the 3 from the batch file.
+     */
+    @Test(groups={"Live", "WIP"})
+    public void testExecPsBatchFileExit3() throws Exception {
+        String script = "EXIT /B 3";
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".bat";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        WinRmToolResponse response = machine.executePsScript("& '"+scriptPath+"'");
+        String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
+        assertEquals(response.getStatusCode(), 3, msg);
+    }
+
+    @Test(groups="Live")
+    public void testExecPsCorruptExe() throws Exception {
+        String exe = "garbage";
+        String exePath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".exe";
+        machine.copyTo(new ByteArrayInputStream(exe.getBytes()), exePath);
+
+        assertExecPsFails("& '"+exePath+"'");
+    }
+
+    @Test(groups="Live")
+    public void testExecPsFileWithArg() throws Exception {
+        String script = Joiner.on("\r\n").join(
+                "Param(",
+                "  [string]$myarg",
+                ")",
+                "Write-Host got $myarg", 
+                "exit 0");
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".ps1";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecPsSucceeds("& "+scriptPath+" -myarg myval", "got myval", "");
+    }
+
+    @Test(groups="Live")
+    public void testExecPsFilePs() throws Exception {
+        String script = Joiner.on("\r\n").join(
+                "Write-Host myline", 
+                "exit 0");
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".ps1";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecPsSucceeds("& "+scriptPath, "myline", "");
+    }
+
+    @Test(groups="Live")
+    public void testExecPsFilePsWithExit1() throws Exception {
+        String script = Joiner.on("\r\n").join(
+                "Write-Host myline", 
+                "exit 1");
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".ps1";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+        System.out.println(scriptPath);
+
+        assertExecPsFails("& "+scriptPath);
+    }
+
+    /*
+     * TODO Not supported in PyWinRM - single line .ps1 file with "exit 1" gives an
+     * exit code 0 over PyWinRM, but an exit code 1 when executed locally!
+     * 
+     * Executing (in python):
+     *     import winrm
+     *     s = winrm.Session('52.12.211.247', auth=('Administrator', 'pa55w0rd'))
+     *     r = s.run_cmd("PowerShell -NonInteractive -NoProfile -Command C:\singleLineExit1.ps1")
+     * gives exit code 0.
+     */
+    @Test(groups={"Live", "WIP"})
+    public void testExecPsFilePsSingleLineWithExit1() throws Exception {
+        String script = "exit 1";
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".ps1";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecPsFails("& "+scriptPath);
+    }
+
+    /*
+     * TODO Not supported in PyWinRM - single line .ps1 file with "exit 1" gives an
+     * exit code 0 over PyWinRM, but an exit code 1 when executed locally!
+     * 
+     * Executing (in python):
+     *     import winrm
+     *     s = winrm.Session('52.12.211.247', auth=('Administrator', 'pa55w0rd'))
+     *     r = s.run_cmd("PowerShell -NonInteractive -NoProfile -Command C:\singleLineGarbage.ps1")
+     * gives exit code 0.
+     */
+    @Test(groups={"Live", "WIP"})
+    public void testExecPsFilePsSingleLineWithInvalidCommand() throws Exception {
+        String script = INVALID_CMD;
+        String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".ps1";
+        machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);
+
+        assertExecPsFails("& "+scriptPath);
+    }
+
+    @Test(groups="Live")
+    public void testConfirmUseOfErrorActionPreferenceDoesNotCauseErr() throws Exception {
+        // Confirm that ErrorActionPreference=Stop does not itself cause a failure, and still get output on success.
+        assertExecPsSucceeds(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, "Write-Host myline"), "myline", "");
+    }
+
+    /*
+     * TODO Not supported in PyWinRM
+     * 
+     * Executing (in python):
+     *     import winrm
+     *     s = winrm.Session('52.12.211.247', auth=('Administrator', 'pa55w0rd'))
+     *     r = s.run_ps("exit 1")
+     * gives exit code 0.
+     */
+    @Test(groups={"Live", "WIP"})
+    public void testExecPsExit1() throws Exception {
+        // Single commands
+        assertExecPsFails("exit 1");
+        assertExecPsFails(ImmutableList.of("exit 1"));
+        
+        // Multi-part
+        assertExecPsFails(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, "Write-Host myline", "exit 1"));
+        
+        // Multi-line
+        assertExecPsFails(PS_ERR_ACTION_PREF_EQ_STOP + "\n" + "Write-Host myline" + "\n" + "exit 1");
+    }
+
+    @Test(groups="Live")
+    public void testExecFailingPsScript() throws Exception {
+        // Single commands
+        assertExecPsFails(INVALID_CMD);
+        assertExecPsFails(ImmutableList.of(INVALID_CMD));
+        
+        // Multi-part commands
+        assertExecPsFails(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, "Write-Host myline", INVALID_CMD));
+        assertExecPsFails(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, INVALID_CMD, "Write-Host myline"));
+    }
+
+    @Test(groups={"Live", "Acceptance"})
+    public void testExecConcurrently() throws Exception {
+        final int NUM_RUNS = 10;
+        final int TIMEOUT_MINS = 30;
+        final AtomicInteger counter = new AtomicInteger();
+        
+        // Find the test methods that are enabled, and that are not WIP 
+        List<Method> methodsToRun = Lists.newArrayList();
+        Method[] allmethods = WinRmMachineLocationLiveTest.class.getMethods();
+        for (Method method : allmethods) {
+            Test annotatn = method.getAnnotation(Test.class);
+            if (method.getParameterTypes().length != 0) {
+                continue;
+            }
+            if (method.getName().equals("testExecConcurrently")) {
+                continue;
+            }
+            if (annotatn == null || !annotatn.enabled()) {
+                continue;
+            }
+            String[] groups = annotatn.groups();
+            if (groups != null && Arrays.asList(groups).contains("WIP")) {
+                continue;
+            }
+            methodsToRun.add(method);
+        }
+
+        // Execute all the methods many times
+        LOG.info("Executing "+methodsToRun.size()+" methods "+NUM_RUNS+" times each, with "+MAX_EXECUTOR_THREADS+" threads for concurrent execution; max permitted time "+TIMEOUT_MINS+"mins; methods="+methodsToRun);
+        
+        List<ListenableFuture<?>> results = Lists.newArrayList();
+        for (int i = 0; i < NUM_RUNS; i++) {
+            for (final Method method : methodsToRun) {
+                results.add(executor.submit(new Callable<Void>() {
+                    public Void call() throws Exception {
+                        LOG.info("Executing "+method.getName()+" in thread "+Thread.currentThread());
+                        Stopwatch stopwatch = Stopwatch.createStarted();
+                        try {
+                            method.invoke(WinRmMachineLocationLiveTest.this);
+                            LOG.info("Executed "+method.getName()+" in "+Time.makeTimeStringRounded(stopwatch)+", in thread "+Thread.currentThread()+"; total "+counter.incrementAndGet()+" methods done");
+                            return null;
+                        } catch (Exception e) {
+                            LOG.error("Execute failed for "+method.getName()+" after "+Time.makeTimeStringRounded(stopwatch)+", in thread "+Thread.currentThread()+"; total "+counter.incrementAndGet()+" methods done");
+                            throw e;
+                        }
+                    }}));
+            }
+        }
+        
+        Futures.allAsList(results).get(TIMEOUT_MINS, TimeUnit.MINUTES);
+    }
+
+    private void assertExecFails(String cmd) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        assertFailed(cmd, machine.executeScript(cmd), stopwatch);
+    }
+
+    private void assertExecFails(List<String> cmds) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        assertFailed(cmds, machine.executeScript(cmds), stopwatch);
+    }
+    
+    private void assertExecPsFails(String cmd) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        assertFailed(cmd, machine.executePsScript(cmd), stopwatch);
+    }
+    
+    private void assertExecPsFails(List<String> cmds) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        assertFailed(cmds, machine.executePsScript(cmds), stopwatch);
+    }
+
+    private void assertExecSucceeds(String cmd, String stdout, String stderr) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        assertSucceeded(cmd, machine.executeScript(cmd), stdout, stderr, stopwatch);
+    }
+
+    private void assertExecSucceeds(List<String> cmds, String stdout, String stderr) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        assertSucceeded(cmds, machine.executeScript(cmds), stdout, stderr, stopwatch);
+    }
+
+    private void assertExecPsSucceeds(String cmd, String stdout, String stderr) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        assertSucceeded(cmd, machine.executePsScript(cmd), stdout, stderr, stopwatch);
+    }
+
+    private void assertExecPsSucceeds(List<String> cmds, String stdout, String stderr) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        assertSucceeded(cmds, machine.executePsScript(cmds), stdout, stderr, stopwatch);
+    }
+
+    private void assertFailed(Object cmd, WinRmToolResponse response, Stopwatch stopwatch) {
+        String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
+        LOG.info("Executed in "+Time.makeTimeStringRounded(stopwatch)+" (asserting failed): "+msg+"; cmd="+cmd);
+        assertNotEquals(response.getStatusCode(), 0, msg);
+    }
+    
+    private WinRmToolResponse assertSucceeded(Object cmd, WinRmToolResponse response, String stdout, String stderr, Stopwatch stopwatch) {
+        String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
+        LOG.info("Executed in "+Time.makeTimeStringRounded(stopwatch)+" (asserting success): "+msg+"; cmd="+cmd);
+        assertEquals(response.getStatusCode(), 0, msg);
+        if (stdout != null) assertEquals(response.getStdOut().trim(), stdout, msg);
+        if (stderr != null) assertEquals(response.getStdErr().trim(), stderr, msg);
+        return response;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WindowsTestFixture.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WindowsTestFixture.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WindowsTestFixture.java
new file mode 100644
index 0000000..10aa4b7
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WindowsTestFixture.java
@@ -0,0 +1,71 @@
+/*
+ * 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.test.location;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.location.jclouds.JcloudsLocation;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class WindowsTestFixture {
+
+    @SuppressWarnings("unchecked")
+    public static MachineProvisioningLocation<WinRmMachineLocation> setUpWindowsLocation(ManagementContext mgmt) throws Exception {
+        // Commented out / unused code included here to make it easy to supply a 
+        // pre-existing Windows VM for use in a bunch of different tests.
+//        return (MachineProvisioningLocation<WinRmMachineLocation>) newByonLocation((ManagementContextInternal) mgmt);
+        return (MachineProvisioningLocation<WinRmMachineLocation>) newJcloudsLocation((ManagementContextInternal) mgmt);
+    }
+    
+    private static MachineProvisioningLocation<?> newJcloudsLocation(ManagementContextInternal mgmt) {
+        // Requires no userMetadata to be set, so that we use WinRmMachineLocation.getDefaultUserMetadataString()
+        mgmt.getBrooklynProperties().remove("brooklyn.location.jclouds.aws-ec2.userMetadata");
+        mgmt.getBrooklynProperties().remove("brooklyn.location.jclouds.userMetadata");
+        mgmt.getBrooklynProperties().remove("brooklyn.location.userMetadata");
+        
+        return (JcloudsLocation) mgmt.getLocationRegistry().resolve("jclouds:aws-ec2:us-west-2", ImmutableMap.<String, Object>builder()
+                .put("inboundPorts", ImmutableList.of(5985, 3389))
+                .put("displayName", "AWS Oregon (Windows)")
+                .put("imageOwner", "801119661308")
+                .put("imageNameRegex", "Windows_Server-2012-R2_RTM-English-64Bit-Base-.*")
+                .put("hardwareId", "m3.medium")
+                .put("useJcloudsSshInit", false)
+                .build());
+    }
+    
+    @SuppressWarnings("unused")
+    private static MachineProvisioningLocation<?> newByonLocation(ManagementContextInternal mgmt) {
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("hosts", "52.12.211.123:5985");
+        config.put("osFamily", "windows");
+        config.put("winrm", "52.12.211.123:5985");
+        config.put("user", "Administrator");
+        config.put("password", "pa55w0rd");
+        config.put("useJcloudsSshInit", "false");
+        config.put("byonIdentity", "123");
+        return (MachineProvisioningLocation<?>) mgmt.getLocationRegistry().resolve("byon", config);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/pom.xml
----------------------------------------------------------------------
diff --git a/usage/camp/pom.xml b/usage/camp/pom.xml
index 911ff6a..fb5d7ee 100644
--- a/usage/camp/pom.xml
+++ b/usage/camp/pom.xml
@@ -157,6 +157,12 @@
         </dependency>
         <dependency>
             <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-locations-jclouds</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
             <artifactId>brooklyn-test-support</artifactId>
             <version>${project.version}</version>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/WindowsYamlLiveTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/WindowsYamlLiveTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/WindowsYamlLiveTest.java
new file mode 100644
index 0000000..15626db
--- /dev/null
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/WindowsYamlLiveTest.java
@@ -0,0 +1,410 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.StringReader;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.mgmt.HasTaskChildren;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.Entities;
+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.EntityTestUtils;
+import org.apache.brooklyn.util.core.task.TaskPredicates;
+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.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+/**
+ * Tests Windows YAML blueprint features.
+ */
+@Test
+public class WindowsYamlLiveTest extends AbstractYamlTest {
+    
+    // TODO Remove duplication of assertStreams and VanillaWindowsProcessWinrmStreamsLiveTest.assertStreams
+    
+    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;
+    
+    @BeforeClass(alwaysRun = true)
+    public void setUpClass() throws Exception {
+        super.setUp();
+        
+        location = WindowsTestFixture.setUpWindowsLocation(mgmt());
+        machine = (WinRmMachineLocation) location.obtain(ImmutableMap.of());
+        String ip = machine.getAddress().getHostAddress();
+        String password = machine.config().get(WinRmMachineLocation.PASSWORD);
+
+        yamlLocation = ImmutableList.of(
+                "location:",
+                "  byon:",
+                "    hosts:",
+                "    - winrm: "+ip+":5985",
+                "      password: "+password,
+                "      user: Administrator",
+                "      osFamily: windows");
+    }
+
+    @AfterClass(alwaysRun = true)
+    public void tearDownClass() {
+        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);
+        } 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", "\"& c:\\\\exit0.ps1\"")
+                .put("checkRunning.powershell.command", "\"& c:\\\\exit0.bat\"")
+                .build();
+        
+        Map<String, List<String>> stdouts = ImmutableMap.of();
+        
+        runWindowsApp(cmds, stdouts, 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, 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, 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, "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, "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, "winrm: stop-command.*");
+    }
+    
+    protected void runWindowsApp(Map<String, String> commands, Map<String, List<String>> stdouts, String taskRegexFailed) throws Exception {
+        String cmdFailed = (taskRegexFailed == null) ? null : TASK_REGEX_TO_COMMAND.get(taskRegexFailed);
+        
+        List<String> yaml = Lists.newArrayList();
+        yaml.addAll(yamlLocation);
+        yaml.addAll(ImmutableList.of(
+                "services:",
+                "- type: org.apache.brooklyn.entity.software.base.VanillaWindowsProcess",
+                "  brooklyn.config:",
+                "    onbox.base.dir.skipResolution: true",
+                "    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: c:\\echoArg.bat",
+                "      classpath://org/apache/brooklyn/camp/brooklyn/echoMyArg.ps1: c:\\echoMyArg.ps1",
+                "      classpath://org/apache/brooklyn/camp/brooklyn/exit0.bat: c:\\exit0.bat",
+                "      classpath://org/apache/brooklyn/camp/brooklyn/exit1.bat: c:\\exit1.bat",
+                "      classpath://org/apache/brooklyn/camp/brooklyn/exit0.ps1: c:\\exit0.ps1",
+                "      classpath://org/apache/brooklyn/camp/brooklyn/exit1.ps1: c:\\exit1.ps1",
+                ""));
+        
+        for (Map.Entry<String, String> entry : commands.entrySet()) {
+            yaml.add("    "+entry.getKey()+": "+entry.getValue());
+        }
+
+        if (Strings.isBlank(cmdFailed)) {
+            app = createAndStartApplication(new StringReader(Joiner.on("\n").join(yaml)));
+            waitForApplicationTasks(app);
+            log.info("App started:");
+            Entities.dumpInfo(app);
+            
+            VanillaWindowsProcess entity = (VanillaWindowsProcess) app.getChildren().iterator().next();
+            
+            EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+            assertStreams(entity, stdouts);
+            
+        } else if (cmdFailed.equals("stop-command")) {
+            app = createAndStartApplication(new StringReader(Joiner.on("\n").join(yaml)));
+            waitForApplicationTasks(app);
+            log.info("App started:");
+            Entities.dumpInfo(app);
+            VanillaWindowsProcess entity = (VanillaWindowsProcess) app.getChildren().iterator().next();
+            EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+            
+            entity.stop();
+            assertSubTaskFailures(entity, ImmutableMap.of(taskRegexFailed, StringPredicates.containsLiteral("for "+cmdFailed)));
+            
+        } else {
+            try {
+                app = createAndStartApplication(new StringReader(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;
+            }
+        }
+    }
+
+    protected void assertStreams(SoftwareProcess entity, Map<String, List<String>> stdouts) {
+        Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(mgmt().getExecutionManager(), entity);
+
+        for (Map.Entry<String, List<String>> entry : stdouts.entrySet()) {
+            String taskNameRegex = entry.getKey();
+            List<String> expectedOuts = entry.getValue();
+
+            Task<?> subTask = findTaskOrSubTask(tasks, TaskPredicates.displayNameMatches(StringPredicates.matchesRegex(taskNameRegex))).get();
+
+            String stdin = getStreamOrFail(subTask, BrooklynTaskTags.STREAM_STDIN);
+            String stdout = getStreamOrFail(subTask, BrooklynTaskTags.STREAM_STDOUT);
+            String stderr = getStreamOrFail(subTask, BrooklynTaskTags.STREAM_STDERR);
+            String env = getStream(subTask, BrooklynTaskTags.STREAM_ENV);
+            String msg = "stdin="+stdin+"; stdout="+stdout+"; stderr="+stderr+"; env="+env;
+
+            for (String expectedOut : expectedOuts) {
+                assertTrue(stdout.contains(expectedOut), msg);
+            }
+        }
+    }
+
+    protected void assertSubTaskFailures(SoftwareProcess entity, Map<String, Predicate<CharSequence>> taskErrs) throws Exception {
+        Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(mgmt().getExecutionManager(), entity);
+
+        for (Map.Entry<String, Predicate<CharSequence>> entry : taskErrs.entrySet()) {
+            String taskNameRegex = entry.getKey();
+            Predicate<? super String> errChecker = entry.getValue();
+            Task<?> subTask = findTaskOrSubTask(tasks, TaskPredicates.displayNameMatches(StringPredicates.matchesRegex(taskNameRegex))).get();
+            String msg = "regex="+taskNameRegex+"; task="+subTask;
+            assertNotNull(subTask, msg);
+            assertTrue(subTask.isDone(), msg);
+            assertTrue(subTask.isError(), msg);
+            try {
+                subTask.get();
+                fail();
+            } catch (Exception e) {
+                if (!errChecker.apply(e.toString())) {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    public static String getStreamOrFail(Task<?> task, String streamType) {
+        String msg = "task="+task+"; stream="+streamType;
+        BrooklynTaskTags.WrappedStream stream = checkNotNull(BrooklynTaskTags.stream(task, streamType), "Stream null: " + msg);
+        return checkNotNull(stream.streamContents.get(), "Contents null: "+msg);
+    }
+
+    public static String getStream(Task<?> task, String streamType) {
+        BrooklynTaskTags.WrappedStream stream = BrooklynTaskTags.stream(task, streamType);
+        return (stream != null) ? stream.streamContents.get() : null;
+    }
+
+    protected Optional<Task<?>> findTaskOrSubTask(Iterable<? extends Task<?>> tasks, Predicate<? super Task<?>> matcher) {
+        List<String> taskNames = Lists.newArrayList();
+        Optional<Task<?>> result = findTaskOrSubTaskImpl(tasks, matcher, taskNames);
+        if (!result.isPresent() && log.isDebugEnabled()) {
+            log.debug("Task not found matching "+matcher+"; contender names were "+taskNames);
+        }
+        return result;
+    }
+
+    protected Optional<Task<?>> findTaskOrSubTaskImpl(Iterable<? extends Task<?>> tasks, Predicate<? super Task<?>> matcher, List<String> taskNames) {
+        for (Task<?> task : tasks) {
+            if (matcher.apply(task)) return Optional.<Task<?>>of(task);
+
+            if (!(task instanceof HasTaskChildren)) {
+                return Optional.absent();
+            } else {
+                Optional<Task<?>> subResult = findTaskOrSubTask(((HasTaskChildren) task).getChildren(), matcher);
+                if (subResult.isPresent()) return subResult;
+            }
+        }
+
+        return Optional.<Task<?>>absent();
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return log;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoArg.bat
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoArg.bat b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoArg.bat
new file mode 100644
index 0000000..dd77ba6
--- /dev/null
+++ b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoArg.bat
@@ -0,0 +1,19 @@
+REM Licensed to the Apache Software Foundation (ASF) under one
+REM or more contributor license agreements.  See the NOTICE file
+REM distributed with this work for additional information
+REM regarding copyright ownership.  The ASF licenses this file
+REM to you under the Apache License, Version 2.0 (the
+REM "License"); you may not use this file except in compliance
+REM with the License.  You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing,
+REM software distributed under the License is distributed on an
+REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+REM KIND, either express or implied.  See the License for the
+REM specific language governing permissions and limitations
+REM under the License.
+
+@ECHO OFF
+echo %1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.bat
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.bat b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.bat
new file mode 100644
index 0000000..33f7b7e
--- /dev/null
+++ b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.bat
@@ -0,0 +1,18 @@
+REM Licensed to the Apache Software Foundation (ASF) under one
+REM or more contributor license agreements.  See the NOTICE file
+REM distributed with this work for additional information
+REM regarding copyright ownership.  The ASF licenses this file
+REM to you under the Apache License, Version 2.0 (the
+REM "License"); you may not use this file except in compliance
+REM with the License.  You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing,
+REM software distributed under the License is distributed on an
+REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+REM KIND, either express or implied.  See the License for the
+REM specific language governing permissions and limitations
+REM under the License.
+
+echo ${config['myarg']}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.ps1
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.ps1 b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.ps1
new file mode 100644
index 0000000..c2535ea
--- /dev/null
+++ b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoFreemarkerMyarg.ps1
@@ -0,0 +1,18 @@
+# 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.
+
+Write-Host ${config['myarg']}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoMyArg.ps1
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoMyArg.ps1 b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoMyArg.ps1
new file mode 100644
index 0000000..3a2d33d
--- /dev/null
+++ b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/echoMyArg.ps1
@@ -0,0 +1,22 @@
+# 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.
+
+param(
+  [string]$myarg
+)
+
+Write-Host $myarg 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit0.bat
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit0.bat b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit0.bat
new file mode 100644
index 0000000..2a926b6
--- /dev/null
+++ b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit0.bat
@@ -0,0 +1,18 @@
+REM Licensed to the Apache Software Foundation (ASF) under one
+REM or more contributor license agreements.  See the NOTICE file
+REM distributed with this work for additional information
+REM regarding copyright ownership.  The ASF licenses this file
+REM to you under the Apache License, Version 2.0 (the
+REM "License"); you may not use this file except in compliance
+REM with the License.  You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing,
+REM software distributed under the License is distributed on an
+REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+REM KIND, either express or implied.  See the License for the
+REM specific language governing permissions and limitations
+REM under the License.
+
+EXIT /B 0
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit0.ps1
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit0.ps1 b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit0.ps1
new file mode 100644
index 0000000..fe0e1f1
--- /dev/null
+++ b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit0.ps1
@@ -0,0 +1,18 @@
+# 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.
+
+exit 0
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit1.bat
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit1.bat b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit1.bat
new file mode 100644
index 0000000..f8b79b9
--- /dev/null
+++ b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit1.bat
@@ -0,0 +1,18 @@
+REM Licensed to the Apache Software Foundation (ASF) under one
+REM or more contributor license agreements.  See the NOTICE file
+REM distributed with this work for additional information
+REM regarding copyright ownership.  The ASF licenses this file
+REM to you under the Apache License, Version 2.0 (the
+REM "License"); you may not use this file except in compliance
+REM with the License.  You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing,
+REM software distributed under the License is distributed on an
+REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+REM KIND, either express or implied.  See the License for the
+REM specific language governing permissions and limitations
+REM under the License.
+
+EXIT /B 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/10071a92/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit1.ps1
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit1.ps1 b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit1.ps1
new file mode 100644
index 0000000..a7fc049
--- /dev/null
+++ b/usage/camp/src/test/resources/org/apache/brooklyn/camp/brooklyn/exit1.ps1
@@ -0,0 +1,19 @@
+# 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.
+
+Write-Host single line file gives exit code 0 from PyWinRM
+exit 1
\ No newline at end of file


[8/8] incubator-brooklyn git commit: This closes #917

Posted by al...@apache.org.
This closes #917


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/93f6f0f6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/93f6f0f6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/93f6f0f6

Branch: refs/heads/master
Commit: 93f6f0f6a835152588a1128a01b7db6610bcbe7f
Parents: 2995471 178ee9b
Author: Aled Sage <al...@gmail.com>
Authored: Wed Sep 23 11:23:16 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 23 11:23:16 2015 +0100

----------------------------------------------------------------------
 docs/guide/yaml/winrm/index.md                  | 175 +++++-
 .../AbstractSoftwareProcessWinRmDriver.java     |  20 +-
 .../software/base/SoftwareProcessImpl.java      |   2 +
 .../base/lifecycle/WinRmExecuteHelper.java      |  31 +-
 ...laWindowsProcessWinrmExitStatusLiveTest.java | 291 ++++++++++
 ...nillaWindowsProcessWinrmStreamsLiveTest.java |  80 ++-
 .../location/WinRmMachineLocationLiveTest.java  | 563 ++++++++++++++++++-
 .../base/test/location/WindowsTestFixture.java  |  71 +++
 usage/camp/pom.xml                              |   6 +
 .../camp/brooklyn/WindowsYamlLiveTest.java      | 410 ++++++++++++++
 .../apache/brooklyn/camp/brooklyn/echoArg.bat   |  19 +
 .../camp/brooklyn/echoFreemarkerMyarg.bat       |  18 +
 .../camp/brooklyn/echoFreemarkerMyarg.ps1       |  18 +
 .../apache/brooklyn/camp/brooklyn/echoMyArg.ps1 |  22 +
 .../org/apache/brooklyn/camp/brooklyn/exit0.bat |  18 +
 .../org/apache/brooklyn/camp/brooklyn/exit0.ps1 |  18 +
 .../org/apache/brooklyn/camp/brooklyn/exit1.bat |  18 +
 .../org/apache/brooklyn/camp/brooklyn/exit1.ps1 |  19 +
 .../blueprints/AbstractBlueprintTest.java       |  38 +-
 .../Windows7zipBlueprintLiveTest.java           | 100 ++++
 .../src/test/resources/7zip-catalog.yaml        |  42 ++
 .../launcher/src/test/resources/install7zip.ps1 |  35 ++
 22 files changed, 1929 insertions(+), 85 deletions(-)
----------------------------------------------------------------------



[5/8] incubator-brooklyn git commit: winrm: avoid NPE if command==null && psCommand==null

Posted by al...@apache.org.
winrm: avoid NPE if command==null && psCommand==null


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/868f765a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/868f765a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/868f765a

Branch: refs/heads/master
Commit: 868f765a7e0532b7a46a70af496ffef9e195f168
Parents: b9d95a4
Author: Aled Sage <al...@gmail.com>
Authored: Wed Sep 23 00:54:17 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 23 01:32:49 2015 +0100

----------------------------------------------------------------------
 .../base/lifecycle/WinRmExecuteHelper.java      | 31 ++++++++++++--------
 1 file changed, 18 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/868f765a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java
index 703a44b..65d05ab 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java
@@ -18,10 +18,16 @@
  */
 package org.apache.brooklyn.entity.software.base.lifecycle;
 
-import com.google.common.annotations.Beta;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import org.apache.brooklyn.api.entity.Entity;
+import static java.lang.String.format;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import javax.annotation.Nullable;
+
 import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.mgmt.TaskQueueingContext;
@@ -33,14 +39,9 @@ import org.apache.brooklyn.util.stream.Streams;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.annotation.Nullable;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
-
-import static java.lang.String.format;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 
 /**
  * <code>org.apache.brooklyn.entity.software.base.lifecycle.ScriptHelper</code> analog for WinRM
@@ -96,7 +97,11 @@ public class WinRmExecuteHelper {
 
         try {
             ByteArrayOutputStream stdin = new ByteArrayOutputStream();
-            stdin.write((command != null ? command : psCommand).getBytes());
+            if (command != null) {
+                stdin.write(command.getBytes());
+            } else if (psCommand != null) {
+                stdin.write(psCommand.getBytes());
+            }
             tb.tag(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDIN, stdin));
         } catch (IOException e) {
             LOG.warn("Error registering stream "+BrooklynTaskTags.STREAM_STDIN+" on "+tb+": "+e, e);


[6/8] incubator-brooklyn git commit: winrm: improve err msgs when no cmd set

Posted by al...@apache.org.
winrm: improve err msgs when no cmd set


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/d499c81b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/d499c81b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/d499c81b

Branch: refs/heads/master
Commit: d499c81b5453c7ef1ff4cb384eeaaaf448c61034
Parents: 868f765
Author: Aled Sage <al...@gmail.com>
Authored: Wed Sep 23 01:02:07 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 23 01:32:49 2015 +0100

----------------------------------------------------------------------
 .../entity/software/base/AbstractSoftwareProcessWinRmDriver.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d499c81b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
index f0d0725..8e3ea37 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
@@ -207,10 +207,10 @@ public abstract class AbstractSoftwareProcessWinRmDriver extends AbstractSoftwar
             if (allowNoOp) {
                 return new WinRmToolResponse("", "", 0).getStatusCode();
             } else {
-                throw new IllegalStateException(String.format("Exactly one of %s or %s must be set", regularCommand, powerShellCommand));
+                throw new IllegalStateException(String.format("Exactly one of cmd or psCmd must be set for %s of %s", phase, entity));
             }
         } else if (!Strings.isBlank(regularCommand) && !Strings.isBlank(powerShellCommand)) {
-            throw new IllegalStateException(String.format("%s and %s cannot both be set", regularCommand, powerShellCommand));
+            throw new IllegalStateException(String.format("%s and %s cannot both be set for %s of %s", regularCommand, powerShellCommand, phase, entity));
         }
 
         ByteArrayOutputStream stdIn = new ByteArrayOutputStream();


[4/8] incubator-brooklyn git commit: Add 7zip windows blueprint test

Posted by al...@apache.org.
Add 7zip windows blueprint test


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/f25c6162
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/f25c6162
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/f25c6162

Branch: refs/heads/master
Commit: f25c616286884f718f66a8fc266b1865b16e5157
Parents: d499c81
Author: Aled Sage <al...@gmail.com>
Authored: Wed Sep 23 01:06:01 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 23 01:32:49 2015 +0100

----------------------------------------------------------------------
 .../blueprints/AbstractBlueprintTest.java       |  38 ++++++-
 .../Windows7zipBlueprintLiveTest.java           | 100 +++++++++++++++++++
 .../src/test/resources/7zip-catalog.yaml        |  42 ++++++++
 .../launcher/src/test/resources/install7zip.ps1 |  35 +++++++
 4 files changed, 212 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f25c6162/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java
index 3e0468d..e367a2f 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java
@@ -19,12 +19,14 @@
 package org.apache.brooklyn.launcher.blueprints;
 
 import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertTrue;
 
 import java.io.File;
 import java.io.Reader;
 import java.io.StringReader;
 import java.util.Collection;
 
+import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
@@ -37,17 +39,21 @@ import org.apache.brooklyn.core.mgmt.persist.FileBasedObjectStore;
 import org.apache.brooklyn.core.mgmt.rebind.RebindOptions;
 import org.apache.brooklyn.core.mgmt.rebind.RebindTestUtils;
 import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.launcher.BrooklynLauncher;
+import org.apache.brooklyn.launcher.SimpleYamlLauncherForTests;
+import org.apache.brooklyn.launcher.camp.BrooklynCampPlatformLauncher;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.stream.Streams;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
-import org.apache.brooklyn.launcher.BrooklynLauncher;
-import org.apache.brooklyn.launcher.SimpleYamlLauncherForTests;
-import org.apache.brooklyn.launcher.camp.BrooklynCampPlatformLauncher;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 
 public abstract class AbstractBlueprintTest {
 
@@ -107,6 +113,32 @@ public abstract class AbstractBlueprintTest {
         }
     }
 
+    protected void runCatalogTest(String catalogFile, Reader yamlApp) throws Exception {
+        runCatalogTest(catalogFile, yamlApp, Predicates.alwaysTrue());
+    }
+    
+    protected void runCatalogTest(String catalogFile, Reader yamlApp, Predicate<? super Application> assertion) throws Exception {
+        Reader catalogInput = Streams.reader(new ResourceUtils(this).getResourceFromUrl(catalogFile));
+        String catalogContent = Streams.readFully(catalogInput);
+        Iterable<? extends CatalogItem<?, ?>> items = launcher.getManagementContext().getCatalog().addItems(catalogContent);
+        
+        try {
+            final Application app = launcher.launchAppYaml(yamlApp);
+            
+            assertNoFires(app);
+            assertTrue(assertion.apply(app));
+            
+            Application newApp = rebind();
+            assertNoFires(newApp);
+            assertTrue(assertion.apply(app));
+            
+        } finally {
+            for (CatalogItem<?, ?> item : items) {
+                launcher.getManagementContext().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+            }
+        }
+    }
+    
     protected void runTest(String yamlFile) throws Exception {
         final Application app = launcher.launchAppYaml(yamlFile);
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f25c6162/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/Windows7zipBlueprintLiveTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/Windows7zipBlueprintLiveTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/Windows7zipBlueprintLiveTest.java
new file mode 100644
index 0000000..068f428
--- /dev/null
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/Windows7zipBlueprintLiveTest.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.launcher.blueprints;
+
+import java.io.StringReader;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.entity.software.base.VanillaWindowsProcess;
+import org.apache.brooklyn.entity.software.base.test.location.WindowsTestFixture;
+import org.apache.brooklyn.location.winrm.AdvertiseWinrmLoginPolicy;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.util.text.Strings;
+import org.python.google.common.collect.Iterables;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import io.cloudsoft.winrm4j.winrm.WinRmTool;
+import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
+
+public class Windows7zipBlueprintLiveTest extends AbstractBlueprintTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(Windows7zipBlueprintLiveTest.class);
+
+    protected MachineProvisioningLocation<WinRmMachineLocation> location;
+    protected WinRmMachineLocation machine;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+        location = WindowsTestFixture.setUpWindowsLocation(mgmt);
+        machine = location.obtain(ImmutableMap.of());
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        try {
+            if (location != null && machine != null) location.release(machine);
+        } finally {
+            super.tearDown();
+        }
+    }
+    
+    @Test(groups={"Live"})
+    public void test7zip() throws Exception {
+        String yamlApp = Joiner.on("\n").join(
+                "location:",
+                "  byon:",
+                "    hosts:",
+                "    - winrm: "+machine.getAddress().getHostAddress()+":5985",
+                "      password: "+machine.config().get(WinRmMachineLocation.PASSWORD),
+                "      user: Administrator",
+                "      osFamily: windows",
+                "services:",
+                "- type: org.apache.brooklyn.windows.7zip:1.0");
+        
+        Predicate<Application> asserter = new Predicate<Application>() {
+            @Override public boolean apply(Application app) {
+                VanillaWindowsProcess entity = Iterables.getOnlyElement(Entities.descendants(app, VanillaWindowsProcess.class));
+                String winRMAddress = entity.getAttribute(AdvertiseWinrmLoginPolicy.VM_USER_CREDENTIALS); 
+                String ipPort = Strings.getFirstWordAfter(winRMAddress, "@");
+                String user = Strings.getFirstWord(winRMAddress);
+                String password = Strings.getFirstWordAfter(winRMAddress, ":");
+                
+                WinRmTool winRmTool = WinRmTool.connect(ipPort, user, password);
+                WinRmToolResponse winRmResponse = winRmTool.executePs(ImmutableList.of("(Get-Item \"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\").name"));
+                
+                LOG.info("winRmResponse: code="+winRmResponse.getStatusCode()+"; out="+winRmResponse.getStdOut()+"; err="+winRmResponse.getStdErr());
+                return "7z.exe\r\n".equals(winRmResponse.getStdOut());
+            }
+        };
+        
+        runCatalogTest("7zip-catalog.yaml", new StringReader(yamlApp), asserter);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f25c6162/usage/launcher/src/test/resources/7zip-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/resources/7zip-catalog.yaml b/usage/launcher/src/test/resources/7zip-catalog.yaml
new file mode 100644
index 0000000..55c4389
--- /dev/null
+++ b/usage/launcher/src/test/resources/7zip-catalog.yaml
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+brooklyn.catalog:
+  id: org.apache.brooklyn.windows.7zip
+  version: 1.0
+  description: A Windows Server, with 7Zip installed
+  displayName: 7Zip
+  iconUrl: http://www.7-zip.org/7ziplogo.png
+  itemType: template
+
+  item:
+    services:
+    - type: brooklyn.entity.basic.VanillaWindowsProcess
+      name: 7Zip
+      brooklyn.config:
+        onbox.base.dir.skipResolution: true
+        templates.install:
+          classpath://install7zip.ps1: "C:\\install7zip.ps1"
+        install.command: powershell -NonInteractive -NoProfile -Command "C:\\install7zip.ps1"
+        customize.command: echo true
+        launch.command: echo true
+        stop.command: echo true
+        checkRunning.command: echo true
+        installer.download.url: http://www.7-zip.org/a/7z938-x64.msi
+      brooklyn.policies:
+      - type: brooklyn.policy.os.AdvertiseWinrmLoginPolicy

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f25c6162/usage/launcher/src/test/resources/install7zip.ps1
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/resources/install7zip.ps1 b/usage/launcher/src/test/resources/install7zip.ps1
new file mode 100644
index 0000000..6308fc6
--- /dev/null
+++ b/usage/launcher/src/test/resources/install7zip.ps1
@@ -0,0 +1,35 @@
+# 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.
+
+$Path="C:\InstallTemp"
+New-Item -ItemType Directory -Force -Path $Path
+
+$Url = "${config['installer.download.url']}"
+$Dl = [System.IO.Path]::Combine($Path, "installer.msi")
+
+$WebClient = New-Object System.Net.WebClient
+
+$WebClient.DownloadFile($Url, $Dl)
+
+$pass = '${attribute['windows.password']}'
+$secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
+$mycreds = New-Object System.Management.Automation.PSCredential ($($env:COMPUTERNAME + "\Administrator"), $secpasswd)
+
+Invoke-Command -ComputerName localhost -credential $mycreds -scriptblock {
+    param($Dl)
+    Start-Process "msiexec" -ArgumentList '/qn','/i',$Dl,'/L c:\7ziplog.txt' -Wait
+} -Authentication CredSSP -argumentlist $Dl


[2/8] incubator-brooklyn git commit: service-not-up-diagnotics: don’t call driver.isRunning on creation

Posted by al...@apache.org.
service-not-up-diagnotics: don’t call driver.isRunning on creation


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/874a3a86
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/874a3a86
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/874a3a86

Branch: refs/heads/master
Commit: 874a3a86d1689bdbf63d4835d70bd84d25a07ca6
Parents: 10071a9
Author: Aled Sage <al...@gmail.com>
Authored: Mon Sep 21 21:36:36 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 23 01:32:48 2015 +0100

----------------------------------------------------------------------
 .../apache/brooklyn/entity/software/base/SoftwareProcessImpl.java  | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/874a3a86/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
index b635242..d7d8e42f 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
@@ -171,6 +171,8 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft
             Lifecycle state = entity.getAttribute(SERVICE_STATE_ACTUAL);
             if (up == null || up) {
                 entity.sensors().set(ServiceStateLogic.SERVICE_NOT_UP_DIAGNOSTICS, ImmutableMap.<String, Object>of());
+            } else if (state == null || state == Lifecycle.CREATED) {
+                // not yet started; do nothing
             } else if (state == Lifecycle.STOPPING || state == Lifecycle.STOPPED || state == Lifecycle.DESTROYED) {
                 // stopping/stopped, so expect not to be up; get rid of the diagnostics.
                 entity.sensors().set(ServiceStateLogic.SERVICE_NOT_UP_DIAGNOSTICS, ImmutableMap.<String, Object>of());


[7/8] incubator-brooklyn git commit: winrm docs: add more tips+limitations

Posted by al...@apache.org.
winrm docs: add more tips+limitations


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/178ee9b4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/178ee9b4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/178ee9b4

Branch: refs/heads/master
Commit: 178ee9b4187aa5ce68f25d68cf55321dbb3f4369
Parents: f25c616
Author: Aled Sage <al...@gmail.com>
Authored: Wed Sep 23 01:06:42 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 23 01:32:50 2015 +0100

----------------------------------------------------------------------
 docs/guide/yaml/winrm/index.md | 175 ++++++++++++++++++++++++++++++++----
 1 file changed, 159 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/178ee9b4/docs/guide/yaml/winrm/index.md
----------------------------------------------------------------------
diff --git a/docs/guide/yaml/winrm/index.md b/docs/guide/yaml/winrm/index.md
index e18ae52..2a6ce6a 100644
--- a/docs/guide/yaml/winrm/index.md
+++ b/docs/guide/yaml/winrm/index.md
@@ -4,7 +4,7 @@ layout: website-normal
 ---
 
 Brooklyn can deploy to Windows servers using WinRM to run commands. These deployments can be 
-expressed in pure YAML, and utilise PowerShell to install and manage the software process. 
+expressed in pure YAML, and utilise Powershell to install and manage the software process. 
 This approach is similar to the use of SSH for UNIX-like servers.
 
 
@@ -73,6 +73,10 @@ to `VanillaSoftwareProcess`, but adapted to work for Windows and WinRM instead o
 [documentation for VanillaSoftwareProcess](../custom-entities.html#vanilla-software-using-bash) to find out what you
 can do with this entity.
 
+Entity authors are strongly encouraged to write Windows Powershell or Batch scripts as separate 
+files, to configure these to be uploaded, and then to configure the appropriate command as a 
+single line that executes given script.
+
 For example - here is a simplified blueprint (but see [Tips and Tricks](#tips-and-tricks) below!):
 
     name: Server with 7-Zip
@@ -91,7 +95,7 @@ For example - here is a simplified blueprint (but see [Tips and Tricks](#tips-an
     services:
     - type: org.apache.brooklyn.entity.software.base.VanillaWindowsProcess
       brooklyn.config:
-        templates.install:
+        templates.preinstall:
           file:///Users/richard/install7zip.ps1: "C:\\install7zip.ps1"
         install.command: powershell -command "C:\\install7zip.ps1"
         customize.command: echo true
@@ -112,7 +116,7 @@ The installation script - referred to as `/Users/richard/install7zip.ps1` in the
 
     Start-Process "msiexec" -ArgumentList '/qn','/i',$Dl -RedirectStandardOutput ( [System.IO.Path]::Combine($Path, "stdout.txt") ) -RedirectStandardError ( [System.IO.Path]::Combine($Path, "stderr.txt") ) -Wait
 
-This is only a very simple example. A core complex example can be found in the [Microsoft SQL Server blueprint in the
+This is only a very simple example. A more complex example can be found in the [Microsoft SQL Server blueprint in the
 Brooklyn source code]({{ site.brooklyn.url.git }}/software/database/src/main/resources/org/apache/brooklyn/entity/database/mssql).
 
 
@@ -130,16 +134,52 @@ process.
 
 ### Powershell
 
-Powershell commands can be supplied directly using config options such as `launch.powershell.command`.
+Powershell commands can be supplied using config options such as `launch.powershell.command`.
 
-### Rebooting
+This is an alternative to supplying a standard batch command using config such as `launch.command`.
+For a given phase, only one of the commands (Powershell or Batch) should be supplied.
 
-Where a reboot is required as part of the entity setup, this can be configured using
-config like `pre.install.reboot.required` and `install.reboot.required`. If required, the 
-installation commands can be split between the pre-install, install and post-install phases
-in order to do a reboot at the appropriate point of the VM setup.
+### Getting the Right Exit Codes
+
+WinRM (or at least the chosen WinRM client!) can return a zero exit code even on error in certain 
+circumstances. It is therefore advisable to follow the guidelines below.
+
+*For a given command, write the Powershell or Batch script as a separate multi-command file. 
+Upload this (e.g. by including it in the `files.preinstall` configuration). For the configuration
+of the given command, execute the file.*
+
+For Powershell files, consider including the line below at the start of the file. 
+Without this, it defaults to "continue" which means that if a command in the Powershell file fails
+then it will continue and return an exit code based on only the final command.
+
+    $ErrorActionPreference = "Stop"
+
+See [Incorrect Exit Codes](#incorrect-exit-codes) under Known Limitations below.
+
+### Executing Scripts From Batch Commands
+
+In a batch command, you can execute a batch file or Powershell file. For example:
+
+    install.command: powershell -NonInteractive -NoProfile -Command "C:\\install7zip.ps1"
+
+Or alternatively:
+
+    install.command: C:\\install7zip.bat
 
-## Powershell scripts and configuration files on classpath
+### Executing Scripts From Powershell
+
+In a Powershell command, you can execute a batch file or Powershell file. There are many ways
+to do this (see official Powershell docs). For example:
+ 
+    install.powershell.command: "& C:\\install7zip.ps1"
+
+Or alternatively:
+
+    install.powershell.command: "& C:\\install7zip.bat"
+
+Note the quotes around the command. This is because the "&" has special meaning in a YAML value. 
+
+### Uploading Script and Configuration Files
 
 Often, blueprints will require that (parameterized) scripts and configuration files are available to be copied to the
 target VM. These must be URLs resolvable from the Brooklyn instance, or on the Brooklyn classpath. One simple way 
@@ -148,12 +188,66 @@ an OSGi bundle can be used, referenced from the catalog item.
 
 Ensure that these scripts end each line with "\r\n", rather than just "\n".
 
+There are two types of file that can be uploaded: plain files and templated files. A plain 
+file is uploaded unmodified. A templated file is interpreted as a [FreeMarker](http://freemarker.org) 
+template. This supports a powerful set of substitutions. In brief, anything (unescaped) of the form
+`${name}` will be substituted, in this case looking up "name" for the value to use.
+
+Templated files (be they configuration files or scripts) gives a powerful way to inject dependent 
+configuration when installing an entity (e.g. for customising the install, or for referencing the
+connection details of another entity). A common substitution is of the form `${config['mykey']}`. 
+This looks up a config key (in this case named "mykey") and will insert the value into the file.
+Another common substitution is is of the form `${attribute['myattribute']}` - this looks up the
+attribute named "myattribute" of this entity.
+
+Files can be referenced as URLs. This includes support for things like `classpath://mypath/myfile.bat`. 
+This looks for the given (fully qualified) resource on the Brooklyn classpath.
+
+The destination for the file upload is specified in the entity's configuration. Note that "\" must
+be escaped. For example `"C:\\install7zip.ps1"`.
+
+A list of plain files to be uploaded can be configured under `files.preinstall`, `files.install` and
+`files.runtime`. These are uploaded respectively prior to executing the `pre.install.command`,
+prior to `install.command` and prior to `pre.launch.command`.
+
+A list of templated files to be uploaded can be configured under `templates.preinstall`, `templates.install`
+and `templates.runtime`. The times these are uploaded is as for the plain files. The templates 
+substitutions will be resolved only at the point when the file is to be uploaded.
+
+For example:
+
+    files.preinstall:
+    - classpath://com/acme/installAcme.ps1
+    - classpath://com/acme/acme.conf
+
+### Parameterised Scripts
+
+Calling parameterised Batch and Powershell scripts is done in the normal Windows way - see
+offical Microsoft docs. For example:
+
+    install.command: "c:\\myscript.bat myarg1 myarg2"
+
+Or as a Powershell example:
+
+    install.powershell.command: "& c:\\myscript.ps1 -key1 myarg1 -key2 myarg2"
+
+It is also possible to construct the script parameters by referencing attributes of this or
+other entities using the standard `attributeWhenReady` mechanism. For example:
+
+    install.command: $brooklyn:formatString("c:\\myscript.bat %s", component("db").attributeWhenReady("datastore.url"))
+
+### Rebooting
+
+Where a reboot is required as part of the entity setup, this can be configured using
+config like `pre.install.reboot.required` and `install.reboot.required`. If required, the 
+installation commands can be split between the pre-install, install and post-install phases
+in order to do a reboot at the appropriate point of the VM setup.
+
 ### Install Location
 
 Blueprint authors are encouraged to explicitly specify the full path for file uploads, and 
 for paths in their Powershell scripts (e.g. for installation, configuration files, log files, etc).
 
-
 ### How and Why to re-authenticate within a powershell script
 
 Brooklyn will run powershell scripts by making a WinRM call over HTTP. For most scripts this will work, however for
@@ -207,9 +301,9 @@ a similarly named AMI. For example:
     brooklyn.location.named.AWS\ Oregon\ Win.imageOwner = 801119661308
     ...
 
-## stdout and stderr in a PowerShell script
+## stdout and stderr in a Powershell script
 
-When calling an executable in a PowerShell script, the stdout and stderr will usually be output to the console.
+When calling an executable in a Powershell script, the stdout and stderr will usually be output to the console.
 This is captured by Brooklyn, and shown in the activities view under the specific tasks.
 
 An alternative is to redirect stdout and stderr to a file on the VM, which can be helpful if one expects sys admins
@@ -225,7 +319,7 @@ For example, instead of running the following:
 
 The `-ArgumentList` is simply the arguments that are to be passed to the executable, `-RedirectStandardOutput` and
 `RedirectStandardError` take file locations for the output (if the file already exists, it will be overwritten). The
-`-PassThru` argument indicates that PowerShell should write to the file *in addition* to the console, rather than
+`-PassThru` argument indicates that Powershell should write to the file *in addition* to the console, rather than
 *instead* of the console. The `-Wait` argument will cause the scriptlet to block until the process is complete.
 
 Further details can be found on the [Start-Process documentation page](https://technet.microsoft.com/en-us/library/hh849848.aspx)
@@ -249,7 +343,6 @@ If the configuration options `userMetadata` or `userMetadataString` are used on
 the default setup script. This allows one to supply a custom setup script. However, if userMetadata contains something
 else then the setup will not be done and the VM may not not be accessible remotely over WinRM.
 
-
 ### Credentials issue requiring special configuration
 
 When a script is run over WinRM over HTTP, the credentials under which the script are run are marked as
@@ -279,7 +372,7 @@ Additional logs may be created by some Windows programs. For example, MSSQL crea
 Known Limitations
 -----------------
 
-### Use of unencrypted HTTP
+### Use of Unencrypted HTTP
 
 Brooklyn is currently using unencrypted HTTP for WinRM communication. This means that the login credentials will be
 transmitted in clear text.
@@ -287,6 +380,56 @@ transmitted in clear text.
 In future we aim to improve Brooklyn to support HTTPS. However this requires adding support to the underlying 
 WinRM library, and also involves certificate creation and verification.
 
+### Incorrect Exit Codes
+
+Some limitations with WinRM (or at least the chosen WinRM Client!) are listed below:
+
+##### Single-line Powershell files
+
+When a Powershell file contains just a single command, the execution of that file over WinRM returns exit code 0
+even if the command fails! This is the case for even simple examples like `exit 1` or `thisFileDoesNotExist.exe`.
+
+A workaround is to add an initial command, for example:
+
+    Write-Host dummy line for workaround 
+    exit 1
+
+##### Direct Configuration of Powershell commands
+
+If a command is directly configured with Powershell that includes `exit`, the return code over WinRM
+is not respected. For example, the command below will receive an exit code of 0.
+
+    launch.powershell.command: |
+      echo first
+      exit 1
+
+##### Direct Configuration of Batch commands
+
+If a command is directly configured with a batch exit, the return code over WinRM
+is not respected. For example, the command below will receive an exit code of 0.
+
+    launch.command: exit /B 1
+
+##### Non-zero Exit Code Returned as One
+
+If a batch or Powershell file exits with an exit code greater than one (or negative), this may 
+be reported as 1 over WinRM. For example, if a batch file ends with `exit /B 3`, the WinRM 
+result from executing that file will be 1.
+
+### Direct Configuration of Multi-line Batch Commands Not Executed
+
+If a command is directly configured with multi-line batch commands, then only the first line 
+will be executed. For example the command below will only output "first":
+
+    launch.command: |
+      echo first
+      echo second
+
+The workaround is to write a file with the batch commands, have that file uploaded, and execute it.
+
+Note this is not done automatically because that could affect the capture and returning
+of the exit code for the commands executed.
+
 ### Install location
 
 Work is required to better configure a default install location on the VM (e.g. so that