Rename everything as SimpleShellCommand. https://github.com/apache/incubator-brooklyn/pull/1030#issuecomment-157311566
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/5f98079e Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/5f98079e Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/5f98079e Branch: refs/heads/master Commit: 5f98079e62a7668aec8139fab4848b8e064824b6 Parents: f3a6395 Author: Geoff Macartney <[email protected]> Authored: Tue Nov 17 14:14:57 2015 +0000 Committer: Geoff Macartney <[email protected]> Committed: Tue Nov 17 14:14:57 2015 +0000 ---------------------------------------------------------------------- .../brooklyn/test/framework/SimpleCommand.java | 75 ------ .../test/framework/SimpleCommandDriver.java | 70 ------ .../test/framework/SimpleCommandImpl.java | 252 ------------------- .../SimpleCommandLifecycleEffectorTasks.java | 58 ----- .../test/framework/SimpleCommandTest.java | 78 ------ .../test/framework/SimpleCommandTestImpl.java | 149 ----------- .../test/framework/SimpleShellCommand.java | 75 ++++++ .../test/framework/SimpleShellCommandImpl.java | 252 +++++++++++++++++++ ...impleShellCommandLifecycleEffectorTasks.java | 57 +++++ .../test/framework/SimpleShellCommandTest.java | 78 ++++++ .../framework/SimpleShellCommandTestImpl.java | 147 +++++++++++ .../framework/SimpleCommandIntegrationTest.java | 166 ------------ .../SimpleShellCommandIntegrationTest.java | 166 ++++++++++++ 13 files changed, 775 insertions(+), 848 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommand.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommand.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommand.java deleted file mode 100644 index 3959b39..0000000 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommand.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.ImplementedBy; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.entity.trait.Startable; -import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey; -import org.apache.brooklyn.entity.software.base.SoftwareProcess; -import org.apache.brooklyn.util.core.flags.SetFromFlag; - -import static org.apache.brooklyn.core.config.ConfigKeys.newConfigKey; - -/** - * Entity to invoke on a node a simple command that will immediately succeed or fail. - * - * Invokes the command in the start operation, and declares itself RUNNING. - */ -@ImplementedBy(SimpleCommandImpl.class) -public interface SimpleCommand extends Entity, Startable { - - String TMP_DEFAULT = "/tmp"; - - /** - * Result of a command invocation. - */ - interface Result { - int getExitCode(); - String getStdout(); - String getStderr(); - - } - - /** - * Supply the command to invoke directly. Cannot be used together with {@link #DOWNLOAD_URL}. - */ - @SetFromFlag(nullable = false) - ConfigKey<String> COMMAND = ConfigKeys.newConfigKey(String.class, "command", "Command to invoke"); - - /** - * Download a script to invoke. Cannot be used together with {@link #COMMAND}. - */ - @SetFromFlag("downloadUrl") - AttributeSensorAndConfigKey<String, String> DOWNLOAD_URL = SoftwareProcess.DOWNLOAD_URL; - - /** - * Where the script will be downloaded on the target machine. - */ - @SetFromFlag("scriptDir") - ConfigKey<String> SCRIPT_DIR = newConfigKey("script.dir", "directory where downloaded scripts should be put", TMP_DEFAULT); - - /** - * The working directory that the script will be run from on the target machine. - */ - @SetFromFlag("runDir") - ConfigKey<String> RUN_DIR = newConfigKey("run.dir", "directory where downloaded scripts should be put", TMP_DEFAULT); -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandDriver.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandDriver.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandDriver.java deleted file mode 100644 index d95d509..0000000 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandDriver.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 org.apache.brooklyn.api.entity.EntityLocal; -import org.apache.brooklyn.api.entity.drivers.EntityDriver; -import org.apache.brooklyn.api.location.Location; - -import java.util.Collection; - -/** - * Driver to invoke a command on a node. - */ -public interface SimpleCommandDriver extends EntityDriver { - - /** - * Result of the command invocation. - */ - interface Result { - int getExitCode(); - String getStdout(); - String getStderr(); - } - - /** - * The entity whose components we are controlling. - */ - EntityLocal getEntity(); - - /** - * Execute the simple command during the start operation. - */ - void start(); - - /** - * Execute the simple command during the restart. - */ - void restart(); - - /** - * Does nothing. - */ - void stop(); - - /** - * Execute the given command on the supplied host. - */ - Result execute(Collection<? extends Location> hostLocations, String command); - - /** - * Download the script at the given URL to the given directory on the host and execute it. - */ - Result executeDownloadedScript(Collection<? extends Location> hostLocations, String url, String directory); -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandImpl.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandImpl.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandImpl.java deleted file mode 100644 index 7731801..0000000 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandImpl.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 com.google.common.base.Joiner; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.api.mgmt.TaskFactory; -import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks; -import org.apache.brooklyn.core.entity.AbstractEntity; -import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; -import org.apache.brooklyn.core.location.Locations; -import org.apache.brooklyn.location.ssh.SshMachineLocation; -import org.apache.brooklyn.util.collections.MutableList; -import org.apache.brooklyn.util.core.task.DynamicTasks; -import org.apache.brooklyn.util.core.task.ssh.SshTasks; -import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.guava.Maybe; -import org.apache.brooklyn.util.text.Strings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Random; - -import static org.apache.brooklyn.core.entity.lifecycle.Lifecycle.*; -import static org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.setExpectedState; -import static org.apache.brooklyn.util.text.Strings.isBlank; -import static org.apache.brooklyn.util.text.Strings.isNonBlank; - -/** - * Implementation for {@link SimpleCommand}. - */ -public class SimpleCommandImpl extends AbstractEntity implements SimpleCommand { - - private static final Logger LOG = LoggerFactory.getLogger(SimpleCommandImpl.class); - private static final int A_LINE = 80; - public static final String DEFAULT_NAME = "download.sh"; - private static final String CD = "cd"; - private static final String SHELL_AND = "&&"; - - @Override - public void init() { - super.init(); - getLifecycleEffectorTasks().attachLifecycleEffectors(this); - } - - protected SimpleCommandLifecycleEffectorTasks getLifecycleEffectorTasks() { - return new SimpleCommandLifecycleEffectorTasks(); - } - - /** - * Gives the opportunity to sub-classes to do additional work based on the result of the command. - */ - protected void handle(SimpleCommand.Result result) { - LOG.debug("{}, Result is {}\nwith output [\n{}\n] and error [\n{}\n]", new Object[] { - this, result.getExitCode(), shorten(result.getStdout()), shorten(result.getStderr()) - }); - } - - private String shorten(String text) { - return Strings.maxlenWithEllipsis(text, A_LINE); - } - - /** - * Does nothing in this class but gives sub-classes the opportunity to filter locations according to some criterion. - */ - public Collection<? extends Location> filterLocations(Collection<? extends Location> locations) { - return locations; - } - - - @Override - public void start(Collection<? extends Location> locations) { - addLocations(locations); - setExpectedState(this, STARTING); - } - - @Override - public void stop() { - LOG.debug("{} Stopping simple command", this); - setUpAndRunState(false, STOPPED); - } - - @Override - public void restart() { - LOG.debug("{} Restarting simple command", this); - setUpAndRunState(true, RUNNING); - } - - private void setUpAndRunState(boolean up, Lifecycle status) { - sensors().set(SERVICE_UP, up); - setExpectedState(this, status); - } - - public void execute(MachineLocation machineLocation) { - try { - executeCommand(machineLocation); - setUpAndRunState(true, RUNNING); - } catch (Exception e) { - setUpAndRunState(false, ON_FIRE); - throw Exceptions.propagate(e); - } - } - - private void executeCommand(MachineLocation machineLocation) { - - SimpleCommand.Result result = null; - String downloadUrl = getConfig(DOWNLOAD_URL); - String command = getConfig(COMMAND); - - String downloadName = DOWNLOAD_URL.getName(); - String commandName = COMMAND.getName(); - - if (isNonBlank(downloadUrl) && isNonBlank(command)) { - throw illegal("Cannot specify both", downloadName, "and", commandName); - } - - if (isBlank(downloadUrl) && isBlank(commandName)) { - throw illegal("No", downloadName, "and no", commandName, "provided"); - } - - if (Strings.isNonBlank(downloadUrl)) { - String scriptDir = getConfig(SCRIPT_DIR); - String scriptPath = calculateDestPath(downloadUrl, scriptDir); - result = executeDownloadedScript(machineLocation, downloadUrl, scriptPath); - } - - if (Strings.isNonBlank(command)) { - result = executeShellCommand(machineLocation, command); - } - - handle(result); - } - - private IllegalArgumentException illegal(String ...messages) { - return new IllegalArgumentException(Joiner.on(' ').join(this.toString() + ":", messages)); - } - - private SimpleCommand.Result executeDownloadedScript(MachineLocation machineLocation, String url, String scriptPath) { - - SshMachineLocation machine = getSshMachine(ImmutableList.<Location>of(machineLocation)); - - TaskFactory<?> install = SshTasks.installFromUrl(ImmutableMap.<String, Object>of(), machine, url, scriptPath); - DynamicTasks.queue(install); - DynamicTasks.waitForLast(); - - machine.execCommands("make the script executable", ImmutableList.<String>of("chmod u+x " + scriptPath)); - - String runDir = getConfig(RUN_DIR); - String cdAndRun = Joiner.on(' ').join(CD, runDir, SHELL_AND, scriptPath); - - return executeShellCommand(machineLocation, cdAndRun); - } - - - private SimpleCommand.Result executeShellCommand(MachineLocation machineLocation, String command) { - - SshMachineLocation machine = getSshMachine(ImmutableList.of(machineLocation)); - SshEffectorTasks.SshEffectorTaskFactory<Integer> etf = SshEffectorTasks.ssh(machine, command); - - LOG.debug("{} Creating task to execute '{}' on location {}", new Object[] {this, command, machine}); - ProcessTaskWrapper<Integer> job = DynamicTasks.queue(etf); - DynamicTasks.waitForLast(); - return buildResult(job); - } - - - private <T> SimpleCommand.Result buildResult(final ProcessTaskWrapper<Integer> job) { - return new SimpleCommand.Result() { - - @Override - public int getExitCode() { - return job.get(); - } - - @Override - public String getStdout() { - return job.getStdout().trim(); - } - - @Override - public String getStderr() { - return job.getStderr().trim(); - } - }; - } - - private SshMachineLocation getSshMachine(Collection<? extends Location> hostLocations) { - Maybe<SshMachineLocation> host = Locations.findUniqueSshMachineLocation(hostLocations); - if (host.isAbsent()) { - throw new IllegalArgumentException("No SSH machine found to run command"); - } - return host.get(); - } - - private String calculateDestPath(String url, String directory) { - try { - URL asUrl = new URL(url); - Iterable<String> path = Splitter.on("/").split(asUrl.getPath()); - String scriptName = getLastPartOfPath(path, DEFAULT_NAME); - return Joiner.on("/").join(directory, "test-" + randomDir(), scriptName); - } catch (MalformedURLException e) { - throw illegal("Malformed URL:", url); - } - } - - private String randomDir() { - return Integer.valueOf(new Random(System.currentTimeMillis()).nextInt(100000)).toString(); - } - - private static String getLastPartOfPath(Iterable<String> path, String defaultName) { - MutableList<String> parts = MutableList.copyOf(path); - Collections.reverse(parts); - Iterator<String> it = parts.iterator(); - String scriptName = null; - - // strip any trailing "/" parts of URL - while (isBlank(scriptName) && it.hasNext()) { - scriptName = it.next(); - } - if (isBlank(scriptName)) { - scriptName = defaultName; - } - return scriptName; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandLifecycleEffectorTasks.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandLifecycleEffectorTasks.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandLifecycleEffectorTasks.java deleted file mode 100644 index ae318ed..0000000 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandLifecycleEffectorTasks.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 com.google.common.base.Supplier; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks; -import org.apache.http.util.Asserts; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.util.Collection; - -public class SimpleCommandLifecycleEffectorTasks extends MachineLifecycleEffectorTasks { - - private static final Logger LOG = LoggerFactory.getLogger(SimpleCommandLifecycleEffectorTasks.class); - - protected Location getLocation(@Nullable Collection<? extends Location> locations) { - return super.getLocation(entity().filterLocations(locations)); - } - - - @Override - protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) { - LOG.debug("Performing lifecycle startProcessesAtMachine on simple command"); - MachineLocation machineLocation = machineS.get(); - entity().execute(machineLocation); - return "Started simple command on " + machineLocation; - } - - @Override - protected String stopProcessesAtMachine() { - LOG.debug("No action needed on simple command stopped"); - return "Stopped"; - } - - protected SimpleCommandImpl entity() { - return (SimpleCommandImpl) super.entity(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTest.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTest.java deleted file mode 100644 index c962403..0000000 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 com.google.common.collect.Maps; -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 java.util.Map; - -/** - * Tests using a simple command execution. - */ -@ImplementedBy(SimpleCommandTestImpl.class) -public interface SimpleCommandTest extends SimpleCommand, BaseTest { - - /** - * Equals assertion on command result. - */ - String EQUALS = "equals"; - - /** - * String contains assertion on command result. - */ - String CONTAINS = "contains"; - - /** - * Regex match assertion on command result. - */ - String MATCHES = "matches"; - - /** - * Is-empty match assertion on command result. - */ - String IS_EMPTY = "isEmpty"; - - /** - * 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<Map> ASSERT_STATUS = ConfigKeys.newConfigKey(Map.class, "assert.status", - "Assertions on command exit code", Maps.newLinkedHashMap()); - - /** - * Assertions on the standard output of the command as a String. - */ - @SetFromFlag("assertOut") - ConfigKey<Map> ASSERT_OUT = ConfigKeys.newConfigKey(Map.class, "assert.out", - "Assertions on command standard output", Maps.newLinkedHashMap()); - - /** - * Assertions on the standard error of the command as a String. - */ - @SetFromFlag("assertErr") - ConfigKey<Map> ASSERT_ERR = ConfigKeys.newConfigKey(Map.class, "assert.err", - "Assertions on command standard error", Maps.newLinkedHashMap()); - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTestImpl.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTestImpl.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTestImpl.java deleted file mode 100644 index cf85b37..0000000 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTestImpl.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 com.google.common.base.Joiner; -import com.google.common.collect.ImmutableMap; -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.core.location.Locations; -import org.apache.brooklyn.test.Asserts; -import org.apache.brooklyn.util.groovy.GroovyJavaMethods; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.truth; -import static org.apache.commons.collections.MapUtils.isEmpty; - -public class SimpleCommandTestImpl extends SimpleCommandImpl implements SimpleCommandTest { - - public static final int SUCCESS = 0; - - @Override - public Entity resolveTarget() { - return AbstractTest.resolveTarget(getExecutionContext(), this); - } - - /** - * The test will choose the location of its target entity. - */ - public Collection<? extends Location> filterLocations(Collection<? extends Location> locations) { - Entity target = resolveTarget(); - return target.getLocations(); - } - - @Override - protected void handle(SimpleCommand.Result result) { - AssertionSupport support = new AssertionSupport(); - checkAssertions(support, exitCodeAssertions(), "exit code", result.getExitCode()); - checkAssertions(support, getConfig(ASSERT_OUT), "stdout", result.getStdout()); - checkAssertions(support, getConfig(ASSERT_ERR), "stderr", result.getStderr()); - support.validate(); - } - - private <T> void checkAssertions(AssertionSupport support, Map<?, ?> assertions, String target, T actual) { - if (null == assertions) { - return; - } - if (null == actual) { - support.fail(target, "no actual value", ""); - return; - } - for (Map.Entry<?, ?> assertion : assertions.entrySet()) { - String condition = assertion.getKey().toString(); - Object expected = assertion.getValue(); - switch (condition) { - case EQUALS : - if (!actual.equals(expected)) { - support.fail(target, EQUALS, expected); - } - break; - case CONTAINS : - if (!actual.toString().contains(expected.toString())) { - support.fail(target, CONTAINS, expected); - } - break; - case IS_EMPTY: - if (!actual.toString().isEmpty() && truth(expected)) { - support.fail(target, IS_EMPTY, expected); - } - break; - case MATCHES : - if (!actual.toString().matches(expected.toString())) { - support.fail(target, MATCHES, expected); - } - break; - default: - support.fail(target, "unknown condition", condition); - } - } - } - - private Map<?, ?> exitCodeAssertions() { - Map<?, ?> assertStatus = getConfig(ASSERT_STATUS); - if (isEmpty(assertStatus)) { - assertStatus = ImmutableMap.of(EQUALS, SUCCESS); - } - return assertStatus; - } - - public static class FailedAssertion { - String target; - String assertion; - String expected; - - public FailedAssertion(String target, String assertion, String expected) { - this.target = target; - this.assertion = assertion; - this.expected = expected; - } - public String description() { - return Joiner.on(' ').join(target, assertion, expected); - } - } - - /** - * A convenience to collect and validate any assertion failures. - */ - public static class AssertionSupport { - private List<FailedAssertion> failures = new ArrayList<>(); - - public void fail(String target, String assertion, Object expected) { - failures.add(new FailedAssertion(target, assertion, expected.toString())); - } - - /** - * @throws AssertionError if any failures were collected. - */ - public void validate() { - if (0 < failures.size()) { - StringBuilder summary = new StringBuilder(); - summary.append("Assertion Failures: \n"); - for (FailedAssertion fail : failures) { - summary.append(fail.description()).append("\n"); - } - Asserts.fail(summary.toString()); - } - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommand.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommand.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommand.java new file mode 100644 index 0000000..1f430a7 --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommand.java @@ -0,0 +1,75 @@ +/* + * 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 org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.ImplementedBy; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.entity.trait.Startable; +import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; +import org.apache.brooklyn.util.core.flags.SetFromFlag; + +import static org.apache.brooklyn.core.config.ConfigKeys.newConfigKey; + +/** + * Entity to invoke on a node a simple command that will immediately succeed or fail. + * + * Invokes the command in the start operation, and declares itself RUNNING. + */ +@ImplementedBy(SimpleShellCommandImpl.class) +public interface SimpleShellCommand extends Entity, Startable { + + String TMP_DEFAULT = "/tmp"; + + /** + * Result of a command invocation. + */ + interface Result { + int getExitCode(); + String getStdout(); + String getStderr(); + + } + + /** + * Supply the command to invoke directly. Cannot be used together with {@link #DOWNLOAD_URL}. + */ + @SetFromFlag(nullable = false) + ConfigKey<String> COMMAND = ConfigKeys.newConfigKey(String.class, "command", "Command to invoke"); + + /** + * Download a script to invoke. Cannot be used together with {@link #COMMAND}. + */ + @SetFromFlag("downloadUrl") + AttributeSensorAndConfigKey<String, String> DOWNLOAD_URL = SoftwareProcess.DOWNLOAD_URL; + + /** + * Where the script will be downloaded on the target machine. + */ + @SetFromFlag("scriptDir") + ConfigKey<String> SCRIPT_DIR = newConfigKey("script.dir", "directory where downloaded scripts should be put", TMP_DEFAULT); + + /** + * The working directory that the script will be run from on the target machine. + */ + @SetFromFlag("runDir") + ConfigKey<String> RUN_DIR = newConfigKey("run.dir", "directory where downloaded scripts should be put", TMP_DEFAULT); +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandImpl.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandImpl.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandImpl.java new file mode 100644 index 0000000..1ccce5e --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandImpl.java @@ -0,0 +1,252 @@ +/* + * 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 com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.api.mgmt.TaskFactory; +import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks; +import org.apache.brooklyn.core.entity.AbstractEntity; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.location.Locations; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.core.task.DynamicTasks; +import org.apache.brooklyn.util.core.task.ssh.SshTasks; +import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.guava.Maybe; +import org.apache.brooklyn.util.text.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Random; + +import static org.apache.brooklyn.core.entity.lifecycle.Lifecycle.*; +import static org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.setExpectedState; +import static org.apache.brooklyn.util.text.Strings.isBlank; +import static org.apache.brooklyn.util.text.Strings.isNonBlank; + +/** + * Implementation for {@link SimpleShellCommand}. + */ +public class SimpleShellCommandImpl extends AbstractEntity implements SimpleShellCommand { + + private static final Logger LOG = LoggerFactory.getLogger(SimpleShellCommandImpl.class); + private static final int A_LINE = 80; + public static final String DEFAULT_NAME = "download.sh"; + private static final String CD = "cd"; + private static final String SHELL_AND = "&&"; + + @Override + public void init() { + super.init(); + getLifecycleEffectorTasks().attachLifecycleEffectors(this); + } + + protected SimpleShellCommandLifecycleEffectorTasks getLifecycleEffectorTasks() { + return new SimpleShellCommandLifecycleEffectorTasks(); + } + + /** + * Gives the opportunity to sub-classes to do additional work based on the result of the command. + */ + protected void handle(SimpleShellCommand.Result result) { + LOG.debug("{}, Result is {}\nwith output [\n{}\n] and error [\n{}\n]", new Object[] { + this, result.getExitCode(), shorten(result.getStdout()), shorten(result.getStderr()) + }); + } + + private String shorten(String text) { + return Strings.maxlenWithEllipsis(text, A_LINE); + } + + /** + * Does nothing in this class but gives sub-classes the opportunity to filter locations according to some criterion. + */ + public Collection<? extends Location> filterLocations(Collection<? extends Location> locations) { + return locations; + } + + + @Override + public void start(Collection<? extends Location> locations) { + addLocations(locations); + setExpectedState(this, STARTING); + } + + @Override + public void stop() { + LOG.debug("{} Stopping simple command", this); + setUpAndRunState(false, STOPPED); + } + + @Override + public void restart() { + LOG.debug("{} Restarting simple command", this); + setUpAndRunState(true, RUNNING); + } + + private void setUpAndRunState(boolean up, Lifecycle status) { + sensors().set(SERVICE_UP, up); + setExpectedState(this, status); + } + + public void execute(MachineLocation machineLocation) { + try { + executeCommand(machineLocation); + setUpAndRunState(true, RUNNING); + } catch (Exception e) { + setUpAndRunState(false, ON_FIRE); + throw Exceptions.propagate(e); + } + } + + private void executeCommand(MachineLocation machineLocation) { + + SimpleShellCommand.Result result = null; + String downloadUrl = getConfig(DOWNLOAD_URL); + String command = getConfig(COMMAND); + + String downloadName = DOWNLOAD_URL.getName(); + String commandName = COMMAND.getName(); + + if (isNonBlank(downloadUrl) && isNonBlank(command)) { + throw illegal("Cannot specify both", downloadName, "and", commandName); + } + + if (isBlank(downloadUrl) && isBlank(commandName)) { + throw illegal("No", downloadName, "and no", commandName, "provided"); + } + + if (Strings.isNonBlank(downloadUrl)) { + String scriptDir = getConfig(SCRIPT_DIR); + String scriptPath = calculateDestPath(downloadUrl, scriptDir); + result = executeDownloadedScript(machineLocation, downloadUrl, scriptPath); + } + + if (Strings.isNonBlank(command)) { + result = executeShellCommand(machineLocation, command); + } + + handle(result); + } + + private IllegalArgumentException illegal(String ...messages) { + return new IllegalArgumentException(Joiner.on(' ').join(this.toString() + ":", messages)); + } + + private SimpleShellCommand.Result executeDownloadedScript(MachineLocation machineLocation, String url, String scriptPath) { + + SshMachineLocation machine = getSshMachine(ImmutableList.<Location>of(machineLocation)); + + TaskFactory<?> install = SshTasks.installFromUrl(ImmutableMap.<String, Object>of(), machine, url, scriptPath); + DynamicTasks.queue(install); + DynamicTasks.waitForLast(); + + machine.execCommands("make the script executable", ImmutableList.<String>of("chmod u+x " + scriptPath)); + + String runDir = getConfig(RUN_DIR); + String cdAndRun = Joiner.on(' ').join(CD, runDir, SHELL_AND, scriptPath); + + return executeShellCommand(machineLocation, cdAndRun); + } + + + private SimpleShellCommand.Result executeShellCommand(MachineLocation machineLocation, String command) { + + SshMachineLocation machine = getSshMachine(ImmutableList.of(machineLocation)); + SshEffectorTasks.SshEffectorTaskFactory<Integer> etf = SshEffectorTasks.ssh(machine, command); + + LOG.debug("{} Creating task to execute '{}' on location {}", new Object[] {this, command, machine}); + ProcessTaskWrapper<Integer> job = DynamicTasks.queue(etf); + DynamicTasks.waitForLast(); + return buildResult(job); + } + + + private <T> SimpleShellCommand.Result buildResult(final ProcessTaskWrapper<Integer> job) { + return new SimpleShellCommand.Result() { + + @Override + public int getExitCode() { + return job.get(); + } + + @Override + public String getStdout() { + return job.getStdout().trim(); + } + + @Override + public String getStderr() { + return job.getStderr().trim(); + } + }; + } + + private SshMachineLocation getSshMachine(Collection<? extends Location> hostLocations) { + Maybe<SshMachineLocation> host = Locations.findUniqueSshMachineLocation(hostLocations); + if (host.isAbsent()) { + throw new IllegalArgumentException("No SSH machine found to run command"); + } + return host.get(); + } + + private String calculateDestPath(String url, String directory) { + try { + URL asUrl = new URL(url); + Iterable<String> path = Splitter.on("/").split(asUrl.getPath()); + String scriptName = getLastPartOfPath(path, DEFAULT_NAME); + return Joiner.on("/").join(directory, "test-" + randomDir(), scriptName); + } catch (MalformedURLException e) { + throw illegal("Malformed URL:", url); + } + } + + private String randomDir() { + return Integer.valueOf(new Random(System.currentTimeMillis()).nextInt(100000)).toString(); + } + + private static String getLastPartOfPath(Iterable<String> path, String defaultName) { + MutableList<String> parts = MutableList.copyOf(path); + Collections.reverse(parts); + Iterator<String> it = parts.iterator(); + String scriptName = null; + + // strip any trailing "/" parts of URL + while (isBlank(scriptName) && it.hasNext()) { + scriptName = it.next(); + } + if (isBlank(scriptName)) { + scriptName = defaultName; + } + return scriptName; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandLifecycleEffectorTasks.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandLifecycleEffectorTasks.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandLifecycleEffectorTasks.java new file mode 100644 index 0000000..fdccd99 --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandLifecycleEffectorTasks.java @@ -0,0 +1,57 @@ +/* + * 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 com.google.common.base.Supplier; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.Collection; + +public class SimpleShellCommandLifecycleEffectorTasks extends MachineLifecycleEffectorTasks { + + private static final Logger LOG = LoggerFactory.getLogger(SimpleShellCommandLifecycleEffectorTasks.class); + + protected Location getLocation(@Nullable Collection<? extends Location> locations) { + return super.getLocation(entity().filterLocations(locations)); + } + + + @Override + protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) { + LOG.debug("Performing lifecycle startProcessesAtMachine on simple command"); + MachineLocation machineLocation = machineS.get(); + entity().execute(machineLocation); + return "Started simple command on " + machineLocation; + } + + @Override + protected String stopProcessesAtMachine() { + LOG.debug("No action needed on simple command stopped"); + return "Stopped"; + } + + protected SimpleShellCommandImpl entity() { + return (SimpleShellCommandImpl) super.entity(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandTest.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandTest.java new file mode 100644 index 0000000..17621d9 --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandTest.java @@ -0,0 +1,78 @@ +/* + * 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 com.google.common.collect.Maps; +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 java.util.Map; + +/** + * Tests using a simple command execution. + */ +@ImplementedBy(SimpleShellCommandTestImpl.class) +public interface SimpleShellCommandTest extends SimpleShellCommand, BaseTest { + + /** + * Equals assertion on command result. + */ + String EQUALS = "equals"; + + /** + * String contains assertion on command result. + */ + String CONTAINS = "contains"; + + /** + * Regex match assertion on command result. + */ + String MATCHES = "matches"; + + /** + * Is-empty match assertion on command result. + */ + String IS_EMPTY = "isEmpty"; + + /** + * 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<Map> ASSERT_STATUS = ConfigKeys.newConfigKey(Map.class, "assert.status", + "Assertions on command exit code", Maps.newLinkedHashMap()); + + /** + * Assertions on the standard output of the command as a String. + */ + @SetFromFlag("assertOut") + ConfigKey<Map> ASSERT_OUT = ConfigKeys.newConfigKey(Map.class, "assert.out", + "Assertions on command standard output", Maps.newLinkedHashMap()); + + /** + * Assertions on the standard error of the command as a String. + */ + @SetFromFlag("assertErr") + ConfigKey<Map> ASSERT_ERR = ConfigKeys.newConfigKey(Map.class, "assert.err", + "Assertions on command standard error", Maps.newLinkedHashMap()); + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandTestImpl.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandTestImpl.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandTestImpl.java new file mode 100644 index 0000000..9c9a15a --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleShellCommandTestImpl.java @@ -0,0 +1,147 @@ +/* + * 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 com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.test.Asserts; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.truth; +import static org.apache.commons.collections.MapUtils.isEmpty; + +public class SimpleShellCommandTestImpl extends SimpleShellCommandImpl implements SimpleShellCommandTest { + + public static final int SUCCESS = 0; + + @Override + public Entity resolveTarget() { + return AbstractTest.resolveTarget(getExecutionContext(), this); + } + + /** + * The test will choose the location of its target entity. + */ + public Collection<? extends Location> filterLocations(Collection<? extends Location> locations) { + Entity target = resolveTarget(); + return target.getLocations(); + } + + @Override + protected void handle(SimpleShellCommand.Result result) { + AssertionSupport support = new AssertionSupport(); + checkAssertions(support, exitCodeAssertions(), "exit code", result.getExitCode()); + checkAssertions(support, getConfig(ASSERT_OUT), "stdout", result.getStdout()); + checkAssertions(support, getConfig(ASSERT_ERR), "stderr", result.getStderr()); + support.validate(); + } + + private <T> void checkAssertions(AssertionSupport support, Map<?, ?> assertions, String target, T actual) { + if (null == assertions) { + return; + } + if (null == actual) { + support.fail(target, "no actual value", ""); + return; + } + for (Map.Entry<?, ?> assertion : assertions.entrySet()) { + String condition = assertion.getKey().toString(); + Object expected = assertion.getValue(); + switch (condition) { + case EQUALS : + if (!actual.equals(expected)) { + support.fail(target, EQUALS, expected); + } + break; + case CONTAINS : + if (!actual.toString().contains(expected.toString())) { + support.fail(target, CONTAINS, expected); + } + break; + case IS_EMPTY: + if (!actual.toString().isEmpty() && truth(expected)) { + support.fail(target, IS_EMPTY, expected); + } + break; + case MATCHES : + if (!actual.toString().matches(expected.toString())) { + support.fail(target, MATCHES, expected); + } + break; + default: + support.fail(target, "unknown condition", condition); + } + } + } + + private Map<?, ?> exitCodeAssertions() { + Map<?, ?> assertStatus = getConfig(ASSERT_STATUS); + if (isEmpty(assertStatus)) { + assertStatus = ImmutableMap.of(EQUALS, SUCCESS); + } + return assertStatus; + } + + public static class FailedAssertion { + String target; + String assertion; + String expected; + + public FailedAssertion(String target, String assertion, String expected) { + this.target = target; + this.assertion = assertion; + this.expected = expected; + } + public String description() { + return Joiner.on(' ').join(target, assertion, expected); + } + } + + /** + * A convenience to collect and validate any assertion failures. + */ + public static class AssertionSupport { + private List<FailedAssertion> failures = new ArrayList<>(); + + public void fail(String target, String assertion, Object expected) { + failures.add(new FailedAssertion(target, assertion, expected.toString())); + } + + /** + * @throws AssertionError if any failures were collected. + */ + public void validate() { + if (0 < failures.size()) { + StringBuilder summary = new StringBuilder(); + summary.append("Assertion Failures: \n"); + for (FailedAssertion fail : failures) { + summary.append(fail.description()).append("\n"); + } + Asserts.fail(summary.toString()); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandIntegrationTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandIntegrationTest.java b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandIntegrationTest.java deleted file mode 100644 index 0b7b121..0000000 --- a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandIntegrationTest.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import org.apache.brooklyn.api.entity.EntitySpec; -import org.apache.brooklyn.api.location.LocationSpec; -import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; -import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic; -import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; -import org.apache.brooklyn.core.test.entity.TestEntity; -import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Random; -import java.util.UUID; - -import static org.apache.brooklyn.test.framework.BaseTest.TARGET_ENTITY; -import static org.apache.brooklyn.test.framework.SimpleCommand.COMMAND; -import static org.apache.brooklyn.test.framework.SimpleCommandTest.*; -import static org.assertj.core.api.Assertions.assertThat; - -public class SimpleCommandIntegrationTest extends BrooklynAppUnitTestSupport { - private static final Logger LOG = LoggerFactory.getLogger(SimpleCommandIntegrationTest.class); - - private static final String UP = "up"; - private LocalhostMachineProvisioningLocation localhost; - private String testId; - - - protected void setUpApp() { - super.setUpApp(); - testId = UUID.randomUUID().toString(); - - localhost = app.getManagementContext().getLocationManager() - .createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class) - .configure("name", testId)); - } - - @Test(groups = "Integration") - public void shouldInvokeCommand() { - TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - - SimpleCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleCommandTest.class) - .configure(TARGET_ENTITY, testEntity) - .configure(COMMAND, "uptime") - .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 0)) - .configure(ASSERT_OUT, ImmutableMap.of(CONTAINS, UP))); - - app.start(ImmutableList.of(localhost)); - - assertThat(uptime.sensors().get(SERVICE_UP)).isTrue() - .withFailMessage("Service should be up"); - assertThat(ServiceStateLogic.getExpectedState(uptime)).isEqualTo(Lifecycle.RUNNING) - .withFailMessage("Service should be marked running"); - - } - - @Test(groups = "Integration") - public void shouldNotBeUpIfAssertionFails() { - TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - - SimpleCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleCommandTest.class) - .configure(TARGET_ENTITY, testEntity) - .configure(COMMAND, "uptime") - .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 1))); - - try { - app.start(ImmutableList.of(localhost)); - } catch (Exception e) { - assertThat(e.getCause().getMessage().contains("exit code equals 1")); - } - - assertThat(ServiceStateLogic.getExpectedState(uptime)).isEqualTo(Lifecycle.ON_FIRE) - .withFailMessage("Service should be marked on fire"); - - } - - @Test(groups = "Integration") - public void shouldInvokeScript() { - TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - - String text = "hello world"; - String testUrl = createTempScript("script.sh", "echo " + text); - - SimpleCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleCommandTest.class) - .configure(TARGET_ENTITY, testEntity) - .configure(DOWNLOAD_URL, testUrl) - .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 0)) - .configure(ASSERT_OUT, ImmutableMap.of(CONTAINS, text))); - - app.start(ImmutableList.of(localhost)); - - assertThat(uptime.sensors().get(SERVICE_UP)).isTrue() - .withFailMessage("Service should be up"); - assertThat(ServiceStateLogic.getExpectedState(uptime)).isEqualTo(Lifecycle.RUNNING) - .withFailMessage("Service should be marked running"); - } - - @Test - public void shouldExecuteInTheRightPlace() throws Exception { - TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - - String remoteTmp = randomName(); - SimpleCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleCommandTest.class) - .configure(TARGET_ENTITY, testEntity) - .configure(COMMAND, "mkdir " + remoteTmp) - .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 0))); - - String pwdUrl = createTempScript("pwd.sh", "pwd"); - - SimpleCommandTest pwd = app.createAndManageChild(EntitySpec.create(SimpleCommandTest.class) - .configure(TARGET_ENTITY, testEntity) - .configure(DOWNLOAD_URL, pwdUrl) - .configure(RUN_DIR, remoteTmp) - .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 0)) - .configure(ASSERT_OUT, ImmutableMap.of(CONTAINS, remoteTmp))); - - app.start(ImmutableList.of(localhost)); - - assertThat(uptime.sensors().get(SERVICE_UP)).isTrue() - .withFailMessage("Service should be up"); - assertThat(ServiceStateLogic.getExpectedState(uptime)).isEqualTo(Lifecycle.RUNNING) - .withFailMessage("Service should be marked running"); - } - - private String createTempScript(String filename, String contents) { - try { - Path tempDirectory = Files.createTempDirectory(randomName()); - tempDirectory.toFile().deleteOnExit(); - Path tempFile = Files.createFile(tempDirectory.resolve(filename)); - Files.write(tempFile, contents.getBytes()); - return "file:" + tempFile.toString(); - } catch (IOException e) { - throw Exceptions.propagate(e); - } - } - - private String randomName() { - return Integer.valueOf(new Random(System.currentTimeMillis()).nextInt(100000)).toString(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5f98079e/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleShellCommandIntegrationTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleShellCommandIntegrationTest.java b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleShellCommandIntegrationTest.java new file mode 100644 index 0000000..d6ff949 --- /dev/null +++ b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleShellCommandIntegrationTest.java @@ -0,0 +1,166 @@ +/* + * 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 com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Random; +import java.util.UUID; + +import static org.apache.brooklyn.test.framework.BaseTest.TARGET_ENTITY; +import static org.apache.brooklyn.test.framework.SimpleShellCommand.COMMAND; +import static org.apache.brooklyn.test.framework.SimpleShellCommandTest.*; +import static org.assertj.core.api.Assertions.assertThat; + +public class SimpleShellCommandIntegrationTest extends BrooklynAppUnitTestSupport { + private static final Logger LOG = LoggerFactory.getLogger(SimpleShellCommandIntegrationTest.class); + + private static final String UP = "up"; + private LocalhostMachineProvisioningLocation localhost; + private String testId; + + + protected void setUpApp() { + super.setUpApp(); + testId = UUID.randomUUID().toString(); + + localhost = app.getManagementContext().getLocationManager() + .createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class) + .configure("name", testId)); + } + + @Test(groups = "Integration") + public void shouldInvokeCommand() { + TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + SimpleShellCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleShellCommandTest.class) + .configure(TARGET_ENTITY, testEntity) + .configure(COMMAND, "uptime") + .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 0)) + .configure(ASSERT_OUT, ImmutableMap.of(CONTAINS, UP))); + + app.start(ImmutableList.of(localhost)); + + assertThat(uptime.sensors().get(SERVICE_UP)).isTrue() + .withFailMessage("Service should be up"); + assertThat(ServiceStateLogic.getExpectedState(uptime)).isEqualTo(Lifecycle.RUNNING) + .withFailMessage("Service should be marked running"); + + } + + @Test(groups = "Integration") + public void shouldNotBeUpIfAssertionFails() { + TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + SimpleShellCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleShellCommandTest.class) + .configure(TARGET_ENTITY, testEntity) + .configure(COMMAND, "uptime") + .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 1))); + + try { + app.start(ImmutableList.of(localhost)); + } catch (Exception e) { + assertThat(e.getCause().getMessage().contains("exit code equals 1")); + } + + assertThat(ServiceStateLogic.getExpectedState(uptime)).isEqualTo(Lifecycle.ON_FIRE) + .withFailMessage("Service should be marked on fire"); + + } + + @Test(groups = "Integration") + public void shouldInvokeScript() { + TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + String text = "hello world"; + String testUrl = createTempScript("script.sh", "echo " + text); + + SimpleShellCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleShellCommandTest.class) + .configure(TARGET_ENTITY, testEntity) + .configure(DOWNLOAD_URL, testUrl) + .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 0)) + .configure(ASSERT_OUT, ImmutableMap.of(CONTAINS, text))); + + app.start(ImmutableList.of(localhost)); + + assertThat(uptime.sensors().get(SERVICE_UP)).isTrue() + .withFailMessage("Service should be up"); + assertThat(ServiceStateLogic.getExpectedState(uptime)).isEqualTo(Lifecycle.RUNNING) + .withFailMessage("Service should be marked running"); + } + + @Test + public void shouldExecuteInTheRightPlace() throws Exception { + TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + String remoteTmp = randomName(); + SimpleShellCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleShellCommandTest.class) + .configure(TARGET_ENTITY, testEntity) + .configure(COMMAND, "mkdir " + remoteTmp) + .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 0))); + + String pwdUrl = createTempScript("pwd.sh", "pwd"); + + SimpleShellCommandTest pwd = app.createAndManageChild(EntitySpec.create(SimpleShellCommandTest.class) + .configure(TARGET_ENTITY, testEntity) + .configure(DOWNLOAD_URL, pwdUrl) + .configure(RUN_DIR, remoteTmp) + .configure(ASSERT_STATUS, ImmutableMap.of(EQUALS, 0)) + .configure(ASSERT_OUT, ImmutableMap.of(CONTAINS, remoteTmp))); + + app.start(ImmutableList.of(localhost)); + + assertThat(uptime.sensors().get(SERVICE_UP)).isTrue() + .withFailMessage("Service should be up"); + assertThat(ServiceStateLogic.getExpectedState(uptime)).isEqualTo(Lifecycle.RUNNING) + .withFailMessage("Service should be marked running"); + } + + private String createTempScript(String filename, String contents) { + try { + Path tempDirectory = Files.createTempDirectory(randomName()); + tempDirectory.toFile().deleteOnExit(); + Path tempFile = Files.createFile(tempDirectory.resolve(filename)); + Files.write(tempFile, contents.getBytes()); + return "file:" + tempFile.toString(); + } catch (IOException e) { + throw Exceptions.propagate(e); + } + } + + private String randomName() { + return Integer.valueOf(new Random(System.currentTimeMillis()).nextInt(100000)).toString(); + } + +}
