You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sv...@apache.org on 2016/09/26 06:35:46 UTC

[1/2] brooklyn-server git commit: Add TestWinrmCommand for test-framework

Repository: brooklyn-server
Updated Branches:
  refs/heads/master 6418c335f -> 8888a4823


Add TestWinrmCommand for test-framework


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

Branch: refs/heads/master
Commit: 0674a5cc5b9abfe0202be6ec1f3a857e31428c0c
Parents: f4281af
Author: Aled Sage <al...@gmail.com>
Authored: Thu Sep 22 20:35:50 2016 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Sep 22 21:04:09 2016 +0100

----------------------------------------------------------------------
 .../core/internal/winrm/RecordingWinRmTool.java |  65 ++++-
 test-framework/pom.xml                          |   7 +
 .../test/framework/TestWinrmCommand.java        |  84 ++++++
 .../test/framework/TestWinrmCommandImpl.java    | 262 +++++++++++++++++
 .../test/framework/TestWinrmCommandTest.java    | 285 +++++++++++++++++++
 5 files changed, 699 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/0674a5cc/software/winrm/src/test/java/org/apache/brooklyn/util/core/internal/winrm/RecordingWinRmTool.java
----------------------------------------------------------------------
diff --git a/software/winrm/src/test/java/org/apache/brooklyn/util/core/internal/winrm/RecordingWinRmTool.java b/software/winrm/src/test/java/org/apache/brooklyn/util/core/internal/winrm/RecordingWinRmTool.java
index 14fcd76..0795880 100644
--- a/software/winrm/src/test/java/org/apache/brooklyn/util/core/internal/winrm/RecordingWinRmTool.java
+++ b/software/winrm/src/test/java/org/apache/brooklyn/util/core/internal/winrm/RecordingWinRmTool.java
@@ -18,15 +18,19 @@
 */
 package org.apache.brooklyn.util.core.internal.winrm;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.io.InputStream;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import org.apache.brooklyn.util.stream.Streams;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 /**
  * For stubbing out the {@link WinRmTool}, so that no real winrm commands are executed.
@@ -60,13 +64,51 @@ public class RecordingWinRmTool implements WinRmTool {
         }
     }
 
+    public interface CustomResponseGenerator {
+        public CustomResponse generate(ExecParams execParams);
+    }
+
+    public static class CustomResponse {
+        public final int exitCode;
+        public final String stdout;
+        public final String stderr;
+        
+        public CustomResponse(int exitCode, String stdout, String stderr) {
+            this.exitCode = exitCode;
+            this.stdout = stdout;
+            this.stderr = stderr;
+        }
+        
+        @Override
+        public String toString() {
+            return "CustomResponse["+exitCode+"; "+stdout+"; "+stderr+"]";
+        }
+        
+        public CustomResponseGenerator toGenerator() {
+            return new CustomResponseGenerator() {
+                @Override public CustomResponse generate(ExecParams execParams) {
+                    return CustomResponse.this;
+                }
+            };
+        }
+    }
     
     public static List<ExecParams> execs = Lists.newCopyOnWriteArrayList();
     public static List<Map<?,?>> constructorProps = Lists.newCopyOnWriteArrayList();
+    public static Map<String, CustomResponseGenerator> customResponses = Maps.newConcurrentMap();
     
     public static void clear() {
         execs.clear();
         constructorProps.clear();
+        customResponses.clear();
+    }
+    
+    public static void setCustomResponse(String cmdRegex, CustomResponseGenerator response) {
+        customResponses.put(cmdRegex, checkNotNull(response, "response"));
+    }
+    
+    public static void setCustomResponse(String cmdRegex, CustomResponse response) {
+        customResponses.put(cmdRegex, checkNotNull(response, "response").toGenerator());
     }
     
     public static List<ExecParams> getExecs() {
@@ -83,14 +125,16 @@ public class RecordingWinRmTool implements WinRmTool {
     
     @Override
     public WinRmToolResponse executeCommand(List<String> commands) {
-        execs.add(new ExecParams(ExecType.COMMAND, commands));
-        return new WinRmToolResponse("", "", 0);
+        ExecParams execParams = new ExecParams(ExecType.COMMAND, commands);
+        execs.add(execParams);
+        return generateResponse(execParams);
     }
 
     @Override
     public WinRmToolResponse executePs(List<String> commands) {
-        execs.add(new ExecParams(ExecType.POWER_SHELL, commands));
-        return new WinRmToolResponse("", "", 0);
+        ExecParams execParams = new ExecParams(ExecType.POWER_SHELL, commands);
+        execs.add(execParams);
+        return generateResponse(execParams);
     }
 
     @Override
@@ -104,4 +148,17 @@ public class RecordingWinRmTool implements WinRmTool {
     public WinRmToolResponse executeScript(List<String> commands) {
         throw new UnsupportedOperationException();
     }
+    
+    protected WinRmToolResponse generateResponse(ExecParams execParams) {
+        for (String cmd : execParams.commands) {
+            for (Entry<String, CustomResponseGenerator> entry : customResponses.entrySet()) {
+                if (cmd.matches(entry.getKey())) {
+                    CustomResponseGenerator responseGenerator = entry.getValue();
+                    CustomResponse response = responseGenerator.generate(execParams);
+                    return new WinRmToolResponse(response.stdout, response.stderr, response.exitCode);
+                }
+            }
+        }
+        return new WinRmToolResponse("", "", 0);
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/0674a5cc/test-framework/pom.xml
----------------------------------------------------------------------
diff --git a/test-framework/pom.xml b/test-framework/pom.xml
index 66e626a..5faf6f7 100644
--- a/test-framework/pom.xml
+++ b/test-framework/pom.xml
@@ -72,6 +72,13 @@
             <classifier>tests</classifier>
         </dependency>
         <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-software-winrm</artifactId>
+            <version>${brooklyn.version}</version>
+            <scope>test</scope>
+            <classifier>tests</classifier>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <version>${assertj.version}</version>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/0674a5cc/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestWinrmCommand.java
----------------------------------------------------------------------
diff --git a/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestWinrmCommand.java b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestWinrmCommand.java
new file mode 100644
index 0000000..3466796
--- /dev/null
+++ b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestWinrmCommand.java
@@ -0,0 +1,84 @@
+/*
+ * 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.test.framework;
+
+import static org.apache.brooklyn.core.config.ConfigKeys.newConfigKey;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Tests ssh command execution, against the {@link org.apache.brooklyn.location.ssh.SshMachineLocation} 
+ * of the target entity.
+ */
+@ImplementedBy(TestWinrmCommandImpl.class)
+public interface TestWinrmCommand extends BaseTest {
+
+    /**
+     * Supply the PowerShell script to invoke directly.
+     */
+    ConfigKey<String> PS_SCRIPT = ConfigKeys.newConfigKey(String.class, "psScript", "PowerShell script to invoke");
+
+    /**
+     * Supply a bat command to invoke directly.
+     */
+    ConfigKey<String> COMMAND = ConfigKeys.newConfigKey(String.class, "command", "Command to invoke");
+
+    /**
+     * The working directory that the script will be run from on the target machine.
+     */
+    @SetFromFlag("runDir")
+    ConfigKey<String> RUN_DIR = newConfigKey(String.class, "run.dir", "directory where downloaded scripts should be run from");
+
+    /**
+     * If no assertions are configured in the test then the default is this assertion that exit status of the command
+     * is zero (successful).
+     */
+    Map<String, Object> DEFAULT_ASSERTION = ImmutableMap.<String,Object>of(TestFrameworkAssertions.EQUALS, 0);
+
+    /**
+     * Assertions on the exit code of the simple command.
+     *
+     * If not explicitly configured, the default assertion is a non-zero exit code.
+     */
+    @SetFromFlag("assertStatus")
+    ConfigKey<Object> ASSERT_STATUS = ConfigKeys.newConfigKey(Object.class, "assert.status", "Assertions on command exit code",
+        ImmutableList.<Map<String, Object>>of());
+
+    /**
+     * Assertions on the standard output of the command as a String.
+     */
+    @SetFromFlag("assertOut")
+    ConfigKey<Object> ASSERT_OUT = ConfigKeys.newConfigKey(Object.class, "assert.out", "Assertions on command standard output",
+        ImmutableList.<Map<String, Object>>of());
+
+    /**
+     * Assertions on the standard error of the command as a String.
+     */
+    @SetFromFlag("assertErr")
+    ConfigKey<Object> ASSERT_ERR = ConfigKeys.newConfigKey(Object.class, "assert.err", "Assertions on command standard error",
+        ImmutableList.<Map<String, Object>>of());
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/0674a5cc/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestWinrmCommandImpl.java
----------------------------------------------------------------------
diff --git a/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestWinrmCommandImpl.java b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestWinrmCommandImpl.java
new file mode 100644
index 0000000..f8b9e0d
--- /dev/null
+++ b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestWinrmCommandImpl.java
@@ -0,0 +1,262 @@
+/*
+ * 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.test.framework;
+
+import static org.apache.brooklyn.core.entity.lifecycle.Lifecycle.ON_FIRE;
+import static org.apache.brooklyn.core.entity.lifecycle.Lifecycle.RUNNING;
+import static org.apache.brooklyn.core.entity.lifecycle.Lifecycle.STARTING;
+import static org.apache.brooklyn.core.entity.lifecycle.Lifecycle.STOPPED;
+import static org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.setExpectedState;
+import static org.apache.brooklyn.test.framework.TestFrameworkAssertions.checkActualAgainstAssertions;
+import static org.apache.brooklyn.test.framework.TestFrameworkAssertions.getAssertions;
+import static org.apache.brooklyn.util.text.Strings.isNonBlank;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.location.Machines;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.TaskBuilder;
+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.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+
+public class TestWinrmCommandImpl extends TargetableTestComponentImpl implements TestWinrmCommand {
+
+    // FIXME Do we want dest-path?
+    // FIXME Do I need COMPUTER_NAME in flags?
+
+    private static final Logger LOG = LoggerFactory.getLogger(TestWinrmCommandImpl.class);
+    
+    private static final int A_LINE = 80;
+
+    @Override
+    public void start(Collection<? extends Location> locations) {
+        setExpectedState(this, STARTING);
+        execute();
+    }
+
+    @Override
+    public void stop() {
+        LOG.debug("{} Stopping simple command", this);
+        setUpAndRunState(false, STOPPED);
+    }
+
+    @Override
+    public void restart() {
+        LOG.debug("{} Restarting simple command", this);
+        execute();
+    }
+
+    private String shorten(String text) {
+        return Strings.maxlenWithEllipsis(text, A_LINE);
+    }
+
+    @SuppressWarnings("serial")
+    private static class MarkerException extends Exception {
+        public MarkerException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    public void execute() {
+        try {
+            checkConfig();
+            final WinRmMachineLocation machineLocation =
+                    Machines.findUniqueMachineLocation(resolveTarget().getLocations(), WinRmMachineLocation.class).get();
+            final Duration timeout = getRequiredConfig(TIMEOUT);
+
+            ReferenceWithError<Boolean> result = Repeater.create("Running winrm-command tests")
+                    .limitTimeTo(timeout)
+                    .every(timeout.multiply(0.1))
+                    .until(new Callable<Boolean>() {
+                        @Override
+                        public Boolean call() throws Exception {
+                            try {
+                                WinRmToolResponse result = execute(machineLocation);
+                                handle(result);
+                            } catch (AssertionError e) {
+                                // Repeater will only handle Exceptions gracefully. Other Throwables are thrown
+                                // immediately, so the AssertionError thrown by handle causes early exit.
+                                throw new MarkerException(e);
+                            }
+                            return true;
+                        }
+                    })
+                    .runKeepingError();
+
+            if (!result.hasError()) {
+                setUpAndRunState(true, RUNNING);
+            } else {
+                setUpAndRunState(false, ON_FIRE);
+                Throwable error = result.getError();
+                if (error instanceof MarkerException) {
+                    error = error.getCause();
+                }
+                throw Exceptions.propagate(error);
+            }
+
+        } catch (Throwable t) {
+            setUpAndRunState(false, ON_FIRE);
+            throw Exceptions.propagate(t);
+        }
+    }
+
+    private WinRmToolResponse execute(WinRmMachineLocation machineLocation) {
+        WinRmToolResponse result = null;
+        String psScript = getConfig(PS_SCRIPT);
+        String command = getConfig(COMMAND);
+
+        if (isNonBlank(psScript)) {
+            result = executePsScript(machineLocation, psScript);
+        } else if (isNonBlank(command)) {
+            result = executeCommand(machineLocation, command);
+        } else {
+            // should have been caught by checkConfig() earlier; maybe someone reconfigured it on-the-fly?!
+            throw illegal("Must specify exactly one of", PS_SCRIPT.getName(), "and", COMMAND.getName());
+        }
+
+        return result;
+    }
+    
+    protected void checkConfig() {
+        String psScript = getConfig(PS_SCRIPT);
+        String command = getConfig(COMMAND);
+
+        if (!(isNonBlank(psScript) ^ isNonBlank(command))) {
+            String psScriptName = PS_SCRIPT.getName();
+            String commandName = COMMAND.getName();
+            throw illegal("Must specify exactly one of", psScriptName, "and", commandName);
+        }
+    }
+
+    protected void handle(WinRmToolResponse result) {
+        LOG.debug("{}, Result is {}\nwith output [\n{}\n] and error [\n{}\n]", new Object[] {
+            this, result.getStatusCode(), shorten(result.getStdOut()), shorten(result.getStdErr())
+        });
+        TestFrameworkAssertions.AssertionSupport support = new TestFrameworkAssertions.AssertionSupport();
+        for (Map<String, Object> assertion : exitCodeAssertions()) {
+            checkActualAgainstAssertions(support, assertion, "exit code", result.getStatusCode());
+        }
+        for (Map<String, Object> assertion : getAssertions(this, ASSERT_OUT)) {
+            checkActualAgainstAssertions(support, assertion, "stdout", result.getStdOut());
+        }
+        for (Map<String, Object> assertion : getAssertions(this, ASSERT_ERR)) {
+            checkActualAgainstAssertions(support, assertion, "stderr", result.getStdErr());
+        }
+        support.validate();
+    }
+
+    private WinRmToolResponse executeCommand(final WinRmMachineLocation machine, final String command) {
+        TaskBuilder<WinRmToolResponse> tb = Tasks.<WinRmToolResponse>builder().displayName("winrm-exec").body(
+                new Callable<WinRmToolResponse>() {
+                    public WinRmToolResponse call() throws Exception {
+                        return machine.executeCommand(command);
+                    }
+                });
+        return execute(tb, command);
+    }
+    
+    private WinRmToolResponse executePsScript(final WinRmMachineLocation machine, final String psScript) {
+        TaskBuilder<WinRmToolResponse> tb = Tasks.<WinRmToolResponse>builder().displayName("winrm-exec").body(
+                new Callable<WinRmToolResponse>() {
+                    public WinRmToolResponse call() throws Exception {
+                        // FIXME Do I need COMPUTER_NAME in flags?
+                        return machine.executePsScript(psScript);
+                    }
+                });
+        return execute(tb, psScript);
+    }
+    
+    private WinRmToolResponse execute(TaskBuilder<WinRmToolResponse> tb, String cmdIn) {
+        try {
+            ByteArrayOutputStream stdin = new ByteArrayOutputStream();
+            if (cmdIn != null) {
+                stdin.write(cmdIn.getBytes());
+            }
+            tb.tag(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDIN, stdin));
+        } catch (IOException e) {
+            LOG.warn("Error registering stream "+BrooklynTaskTags.STREAM_STDIN+" on "+tb+": "+e, e);
+        }
+
+        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+        tb.tag(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDOUT, stdout));
+        ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+        tb.tag(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDERR, stderr));
+        
+        Task<WinRmToolResponse> task = tb.build();
+        
+        WinRmToolResponse result;
+        DynamicTasks.queueIfPossible(task).orSubmitAndBlock().getTask().blockUntilEnded();
+        try {
+            result = task.get();
+            if (result.getStdOut() != null) stdout.write(result.getStdOut().getBytes());
+            if (result.getStdErr() != null) stderr.write(result.getStdErr().getBytes());
+        } catch (InterruptedException | ExecutionException e) {
+            throw Exceptions.propagate(e);
+        } catch (IOException e) {
+            // Should not happen, as just using ByteArrayOutputStream
+            throw Exceptions.propagate(e);
+        }
+        
+        return result;
+    }
+
+    private IllegalArgumentException illegal(String message, String... messages) {
+        Iterable<String> allmsgs = Iterables.concat(MutableList.of(this.toString() + ":", message), Arrays.asList(messages));
+        return new IllegalArgumentException(Joiner.on(' ').join(allmsgs));
+    }
+
+    private List<Map<String, Object>> exitCodeAssertions() {
+
+        List<Map<String, Object>> assertStatus = getAssertions(this, ASSERT_STATUS);
+        List<Map<String, Object>> assertOut = getAssertions(this, ASSERT_OUT);
+        List<Map<String, Object>> assertErr = getAssertions(this, ASSERT_ERR);
+
+        List<Map<String, Object>> result;
+        if (assertStatus.isEmpty() && assertOut.isEmpty() && assertErr.isEmpty()) {
+            Map<String, Object> shouldSucceed = DEFAULT_ASSERTION;
+            result = MutableList.of(shouldSucceed);
+        } else {
+            result = assertStatus;
+        }
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/0674a5cc/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestWinrmCommandTest.java
----------------------------------------------------------------------
diff --git a/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestWinrmCommandTest.java b/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestWinrmCommandTest.java
new file mode 100644
index 0000000..ff387e5
--- /dev/null
+++ b/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestWinrmCommandTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.test.framework;
+
+import static org.apache.brooklyn.core.entity.EntityAsserts.assertEntityFailed;
+import static org.apache.brooklyn.core.entity.EntityAsserts.assertEntityHealthy;
+import static org.apache.brooklyn.test.framework.BaseTest.TIMEOUT;
+import static org.apache.brooklyn.test.framework.TargetableTestComponent.TARGET_ENTITY;
+import static org.apache.brooklyn.test.framework.TestFrameworkAssertions.CONTAINS;
+import static org.apache.brooklyn.test.framework.TestFrameworkAssertions.EQUALS;
+import static org.apache.brooklyn.test.framework.TestWinrmCommand.ASSERT_ERR;
+import static org.apache.brooklyn.test.framework.TestWinrmCommand.ASSERT_OUT;
+import static org.apache.brooklyn.test.framework.TestWinrmCommand.ASSERT_STATUS;
+import static org.apache.brooklyn.test.framework.TestWinrmCommand.COMMAND;
+import static org.apache.brooklyn.test.framework.TestWinrmCommand.PS_SCRIPT;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.internal.winrm.RecordingWinRmTool;
+import org.apache.brooklyn.util.core.internal.winrm.RecordingWinRmTool.ExecType;
+import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.time.Duration;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class TestWinrmCommandTest extends BrooklynAppUnitTestSupport {
+
+    private TestEntity testEntity;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        RecordingWinRmTool.clear();
+        LocationSpec<WinRmMachineLocation> machineSpec = LocationSpec.create(WinRmMachineLocation.class)
+                .configure("address", "1.2.3.4")
+                .configure(WinRmMachineLocation.WINRM_TOOL_CLASS, RecordingWinRmTool.class.getName());
+        testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .location(machineSpec));
+    }
+
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        RecordingWinRmTool.clear();
+    }
+    
+    @DataProvider(name = "insistOnJustOneOfCommandAndPsScript")
+    public Object[][] commandAndPsScriptPermutations() {
+        return new Object[][] {
+                { "pwd", "pwd", Boolean.FALSE },
+                { null, null, Boolean.FALSE },
+                { "pwd", null, Boolean.TRUE },
+                { null, "pwd", Boolean.TRUE }
+        };
+    }
+
+    @Test(dataProvider = "insistOnJustOneOfCommandAndPsScript")
+    public void shouldInsistOnJustOneOfCommandAndPsScript(String command, String psScript, boolean valid) throws Exception {
+        try {
+            app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+                    .configure(TARGET_ENTITY, testEntity)
+                    .configure(COMMAND, command)
+                    .configure(PS_SCRIPT, psScript));
+
+            app.start(ImmutableList.<Location>of());
+            if (!valid) {
+                Asserts.shouldHaveFailedPreviously();
+            }
+
+        } catch (Exception e) {
+            if (valid) {
+                throw e;
+            }
+            Asserts.expectedFailureContains(e, "Must specify exactly one of psScript and command");
+        }
+    }
+    
+    @Test
+    public void shouldInvokePsScript() throws Exception {
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+            .configure(TARGET_ENTITY, testEntity)
+            .configure(PS_SCRIPT, "uptime"));
+
+        app.start(ImmutableList.<Location>of());
+
+        assertEntityHealthy(test);
+        assertThat(RecordingWinRmTool.getLastExec().commands.toString()).contains("uptime");
+        assertThat(RecordingWinRmTool.getLastExec().type).isEqualTo(ExecType.POWER_SHELL);
+    }
+
+    @Test
+    public void shouldSucceedUsingSuccessfulExitAsDefaultCondition() {
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+            .configure(TARGET_ENTITY, testEntity)
+            .configure(COMMAND, "uptime"));
+
+        app.start(ImmutableList.<Location>of());
+
+        assertEntityHealthy(test);
+        assertThat(RecordingWinRmTool.getLastExec().commands).isEqualTo(ImmutableList.of("uptime"));
+        assertThat(RecordingWinRmTool.getLastExec().type).isEqualTo(ExecType.COMMAND);
+    }
+
+    @Test
+    public void shouldFailUsingSuccessfulExitAsDefaultCondition() {
+        String cmd = "commandExpectedToFail-" + Identifiers.randomLong();
+        RecordingWinRmTool.setCustomResponse(cmd, new RecordingWinRmTool.CustomResponse(1, null, null));
+        
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+            .configure(TARGET_ENTITY, testEntity)
+            .configure(COMMAND, cmd));
+
+        try {
+            app.start(ImmutableList.<Location>of());
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Throwable t) {
+            Asserts.expectedFailureContains(t, "exit code expected equals 0 but found 1");
+        }
+
+        assertEntityFailed(test);
+        assertThat(RecordingWinRmTool.getLastExec().commands).isEqualTo(ImmutableList.of(cmd));
+    }
+
+    @Test
+    public void shouldMatchStdoutAndStderr() {
+        String cmd = "stdoutAndStderr-" + Identifiers.randomLong();
+        RecordingWinRmTool.setCustomResponse(cmd, new RecordingWinRmTool.CustomResponse(0, "mystdout", "mystderr"));
+        
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+            .configure(TARGET_ENTITY, testEntity)
+            .configure(COMMAND, cmd)
+            .configure(ASSERT_OUT, makeAssertions(ImmutableMap.of(CONTAINS, "mystdout")))
+            .configure(ASSERT_ERR, makeAssertions(ImmutableMap.of(CONTAINS, "mystderr"))));
+
+        app.start(ImmutableList.<Location>of());
+
+        assertEntityHealthy(test);
+    }
+
+    @Test
+    public void shouldFailOnUnmatchedStdout() {
+        String cmd = "stdoutAndStderr-" + Identifiers.randomLong();
+        RecordingWinRmTool.setCustomResponse(cmd, new RecordingWinRmTool.CustomResponse(0, "wrongstdout", null));
+        
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+            .configure(TARGET_ENTITY, testEntity)
+            .configure(COMMAND, cmd)
+            .configure(ASSERT_OUT, makeAssertions(ImmutableMap.of(CONTAINS, "mystdout"))));
+
+        try {
+            app.start(ImmutableList.<Location>of());
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Throwable t) {
+            Asserts.expectedFailureContains(t, "stdout expected contains mystdout but found wrongstdout");
+        }
+
+        assertEntityFailed(test);
+    }
+
+    @Test
+    public void shouldFailOnUnmatchedStderr() {
+        String cmd = "stdoutAndStderr-" + Identifiers.randomLong();
+        RecordingWinRmTool.setCustomResponse(cmd, new RecordingWinRmTool.CustomResponse(0, null, "wrongstderr"));
+        
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+            .configure(TARGET_ENTITY, testEntity)
+            .configure(COMMAND, cmd)
+            .configure(ASSERT_ERR, makeAssertions(ImmutableMap.of(CONTAINS, "mystderr"))));
+
+        try {
+            app.start(ImmutableList.<Location>of());
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Throwable t) {
+            Asserts.expectedFailureContains(t, "stderr expected contains mystderr but found wrongstderr");
+        }
+
+        assertEntityFailed(test);
+    }
+
+    @Test
+    public void shouldNotBeUpIfAssertionsFail() {
+        Map<String, ?> equalsOne = ImmutableMap.of(EQUALS, 1);
+
+        Map<String, ?> equals255 = ImmutableMap.of(EQUALS, 255);
+
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+            .configure(TARGET_ENTITY, testEntity)
+            .configure(COMMAND, "uptime")
+            .configure(ASSERT_STATUS, makeAssertions(equalsOne, equals255)));
+
+        try {
+            app.start(ImmutableList.<Location>of());
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureContains(e, "exit code expected equals 1 but found 0", "exit code expected equals 255 but found 0");
+        }
+
+        assertEntityFailed(test);
+    }
+
+    @Test
+    public void shouldFailIfTestEntityHasNoMachine() throws Exception {
+        TestEntity testEntityWithNoMachine = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+            .configure(TARGET_ENTITY, testEntityWithNoMachine)
+            .configure(COMMAND, "mycmd"));
+
+        try {
+            app.start(ImmutableList.<Location>of());
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureContains(e, "No instances of class "+WinRmMachineLocation.class.getName()+" available");
+        }
+
+        assertEntityFailed(test);
+    }
+    
+    @Test
+    public void shouldFailFastIfNoCommand() throws Exception {
+        Duration longTimeout = Asserts.DEFAULT_LONG_TIMEOUT;
+        
+        Map<String, ?> equalsZero = ImmutableMap.of(EQUALS, 0);
+        
+        TestWinrmCommand test = app.createAndManageChild(EntitySpec.create(TestWinrmCommand.class)
+                .configure(TIMEOUT, longTimeout.multiply(2))
+                .configure(TARGET_ENTITY, testEntity)
+                .configure(ASSERT_STATUS, makeAssertions(equalsZero)));
+
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        try {
+            app.start(ImmutableList.<Location>of());
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            // note: sleep(1000) can take a few millis less than 1000ms, according to a stopwatch.
+            Asserts.expectedFailureContains(e, "Must specify exactly one of psScript and command");
+            Duration elapsed = Duration.of(stopwatch);
+            Asserts.assertTrue(elapsed.isShorterThan(longTimeout.subtract(Duration.millis(20))), "elapsed="+elapsed);
+        }
+
+        assertEntityFailed(test);
+    }
+    
+    private List<Map<String, ?>> makeAssertions(Map<String, ?> map) {
+        return ImmutableList.<Map<String, ?>>of(map);
+    }
+
+    private List<Map<String, ?>> makeAssertions(Map<String, ?> map1, Map<String, ?> map2) {
+        return ImmutableList.of(map1, map2);
+    }
+}


[2/2] brooklyn-server git commit: Closes #343

Posted by sv...@apache.org.
Closes #343

Add TestWinrmCommand for test-framework


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

Branch: refs/heads/master
Commit: 8888a4823dfa938ee6ebbef8e154d61161d624ef
Parents: 6418c33 0674a5c
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Mon Sep 26 09:35:19 2016 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Mon Sep 26 09:35:19 2016 +0300

----------------------------------------------------------------------
 .../core/internal/winrm/RecordingWinRmTool.java |  65 ++++-
 test-framework/pom.xml                          |   7 +
 .../test/framework/TestWinrmCommand.java        |  84 ++++++
 .../test/framework/TestWinrmCommandImpl.java    | 262 +++++++++++++++++
 .../test/framework/TestWinrmCommandTest.java    | 285 +++++++++++++++++++
 5 files changed, 699 insertions(+), 4 deletions(-)
----------------------------------------------------------------------