Repository: incubator-brooklyn Updated Branches: refs/heads/master faf08c116 -> bbacc2b94
Initial draft of SimpleCommand. Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/b4e3ac7b Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/b4e3ac7b Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/b4e3ac7b Branch: refs/heads/master Commit: b4e3ac7b68ed26f3efa74be7285de013d1f8c02a Parents: ca89ed4 Author: Geoff Macartney <[email protected]> Authored: Thu Nov 5 15:06:45 2015 +0000 Committer: Geoff Macartney <[email protected]> Committed: Fri Nov 13 12:01:49 2015 +0000 ---------------------------------------------------------------------- .../brooklyn/util/core/task/ssh/SshTasks.java | 5 + usage/test-framework/pom.xml | 6 + .../brooklyn/test/framework/AbstractTest.java | 28 ++- .../brooklyn/test/framework/BaseTest.java | 2 + .../brooklyn/test/framework/SimpleCommand.java | 50 +++++ .../test/framework/SimpleCommandDriver.java | 70 +++++++ .../test/framework/SimpleCommandImpl.java | 177 +++++++++++++++++ .../SimpleCommandLifecycleEffectorTasks.java | 73 +++++++ .../test/framework/SimpleCommandSshDriver.java | 192 +++++++++++++++++++ .../test/framework/SimpleCommandTest.java | 78 ++++++++ .../test/framework/SimpleCommandTestImpl.java | 149 ++++++++++++++ .../brooklyn/test/framework/TestCase.java | 1 + .../brooklyn/test/framework/TestEffector.java | 1 + .../test/framework/TestHttpCallImpl.java | 4 +- .../brooklyn/test/framework/TestSensor.java | 1 + .../brooklyn/test/framework/TestSensorImpl.java | 2 +- .../test/framework/SimpleCommandImplTest.java | 117 +++++++++++ .../SimpleCommandScriptIntegrationTest.java | 134 +++++++++++++ .../test/framework/TestEffectorTest.java | 3 +- .../brooklyn/logback-appender-stdout.xml | 37 ++++ 20 files changed, 1117 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/core/src/main/java/org/apache/brooklyn/util/core/task/ssh/SshTasks.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/ssh/SshTasks.java b/core/src/main/java/org/apache/brooklyn/util/core/task/ssh/SshTasks.java index e939ca8..c9fd66b 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/task/ssh/SshTasks.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/task/ssh/SshTasks.java @@ -215,6 +215,11 @@ public class SshTasks { return installFromUrl(ResourceUtils.create(SshTasks.class), ImmutableMap.<String,Object>of(), location, url, destPath); } /** task to install a file given a url, where the url is resolved remotely first then locally */ + public static TaskFactory<?> installFromUrl(final Map<String, ?> props, final SshMachineLocation location, final String url, final String destPath) { + return installFromUrl(ResourceUtils.create(SshTasks.class), props, location, url, destPath); + } + + /** task to install a file given a url, where the url is resolved remotely first then locally */ public static TaskFactory<?> installFromUrl(final ResourceUtils utils, final Map<String, ?> props, final SshMachineLocation location, final String url, final String destPath) { return new TaskFactory<TaskAdaptable<?>>() { @Override http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/pom.xml ---------------------------------------------------------------------- diff --git a/usage/test-framework/pom.xml b/usage/test-framework/pom.xml index 006afdc..b4297a9 100644 --- a/usage/test-framework/pom.xml +++ b/usage/test-framework/pom.xml @@ -47,6 +47,12 @@ <!--TEST SCOPE :: START--> <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-logback-xml</artifactId> + <version>${brooklyn.version}</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>${testng.version}</version> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/AbstractTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/AbstractTest.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/AbstractTest.java index d97d09b..434be8e 100644 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/AbstractTest.java +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/AbstractTest.java @@ -19,6 +19,7 @@ package org.apache.brooklyn.test.framework; import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.mgmt.ExecutionContext; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent; import org.apache.brooklyn.core.entity.AbstractEntity; @@ -44,24 +45,33 @@ public abstract class AbstractTest extends AbstractEntity implements BaseTest { * @throws @RuntimeException if no target can be determined. */ public Entity resolveTarget() { - Entity entity = getConfig(TARGET_ENTITY); - if (null == entity) { - entity = getTargetById(); + return resolveTarget(getExecutionContext(), this); + } + + /** + * Find the target entity in the given execution context. + * + * @see {@link #resolveTarget()}. + */ + public static Entity resolveTarget(ExecutionContext executionContext, Entity entity) { + Entity target = entity.getConfig(TARGET_ENTITY); + if (null == target) { + target = getTargetById(executionContext, entity); } - return entity; + return target; } - private Entity getTargetById() { - String targetId = getConfig(TARGET_ID); + private static Entity getTargetById(ExecutionContext executionContext, Entity entity) { + String targetId = entity.getConfig(TARGET_ID); final Task<Entity> targetLookup = new DslComponent(targetId).newTask(); - Entity entity = null; + Entity target = null; try { - entity = Tasks.resolveValue(targetLookup, Entity.class, getExecutionContext(), "Finding entity " + targetId); + target = Tasks.resolveValue(targetLookup, Entity.class, executionContext, "Finding entity " + targetId); LOG.debug("Found target by id {}", targetId); } catch (final ExecutionException | InterruptedException e) { LOG.error("Error finding target {}", targetId); Exceptions.propagate(e); } - return entity; + return target; } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/BaseTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/BaseTest.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/BaseTest.java index 7fd70ef..4043bcc 100644 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/BaseTest.java +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/BaseTest.java @@ -20,6 +20,7 @@ package org.apache.brooklyn.test.framework; import com.google.common.collect.Maps; import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.mgmt.ExecutionContext; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.entity.trait.Startable; @@ -60,4 +61,5 @@ public interface BaseTest extends Entity, Startable { * @throws IllegalArgumentException if the target cannot be found. */ Entity resolveTarget(); + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/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 new file mode 100644 index 0000000..3332a04 --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommand.java @@ -0,0 +1,50 @@ +/* + * 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 org.apache.brooklyn.util.os.Os; + +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 { + + @SetFromFlag(nullable = false) + ConfigKey<String> DEFAULT_COMMAND = ConfigKeys.newConfigKey(String.class, "defaultCommand", + "Command to invoke if no script is provided via a downloadUrl"); + + @SetFromFlag("downloadUrl") + AttributeSensorAndConfigKey<String, String> DOWNLOAD_URL = SoftwareProcess.DOWNLOAD_URL; + + @SetFromFlag("scriptDir") + ConfigKey<String> SCRIPT_DIR = newConfigKey("scriptDir", "directory where downloaded scripts should be put", Os.tmp()); +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/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 new file mode 100644 index 0000000..d95d509 --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandDriver.java @@ -0,0 +1,70 @@ +/* + * 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/b4e3ac7b/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 new file mode 100644 index 0000000..8b3d27f --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandImpl.java @@ -0,0 +1,177 @@ +/* + * 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.drivers.DriverDependentEntity; +import org.apache.brooklyn.api.entity.drivers.EntityDriverManager; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.core.annotation.EffectorParam; +import org.apache.brooklyn.core.entity.AbstractEntity; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.text.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; + +import static org.apache.brooklyn.core.entity.lifecycle.Lifecycle.*; +import static org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.setExpectedState; + +/** + * Implementation for {@link SimpleCommand}. + */ +public class SimpleCommandImpl extends AbstractEntity + implements SimpleCommand, DriverDependentEntity<SimpleCommandDriver> { + + private static final Logger LOG = LoggerFactory.getLogger(SimpleCommandImpl.class); + private static final int A_LINE = 80; + private transient SimpleCommandDriver driver; + + private Collection<? extends Location> locations; + + @Override + public SimpleCommandDriver getDriver() { + return driver; + } + + @Override + public Class<SimpleCommandDriver> getDriverInterface() { + return SimpleCommandDriver.class; + } + + /** + * Gives the opportunity to sub-classes to do additional work based on the result of the command. + */ + protected void handle(SimpleCommandDriver.Result result) { + LOG.debug("Result is {}\nwith output [\n{}\n] and error [\n{}\n]", new Object[] { + 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 init() { + super.init(); + getLifecycleEffectorTasks().attachLifecycleEffectors(this); + } + + + protected void initDriver(MachineLocation machine) { + LOG.debug("Initializing simple command driver"); + SimpleCommandDriver newDriver = doInitDriver(machine); + if (newDriver == null) { + throw new UnsupportedOperationException("cannot start "+this+" on "+machine+": no driver available"); + } + driver = newDriver; + } + + protected SimpleCommandDriver doInitDriver(MachineLocation machine) { + if (driver!=null) { + if (machine.equals(driver.getLocation())) { + return driver; //just reuse + } else { + LOG.warn("driver/location change is untested for {} at {}; changing driver and continuing", this, machine); + return newDriver(machine); + } + } else { + return newDriver(machine); + } + } + + protected SimpleCommandDriver newDriver(MachineLocation machine) { + LOG.debug("Creating new simple command driver for {} from management context", machine); + EntityDriverManager entityDriverManager = getManagementContext().getEntityDriverManager(); + return entityDriverManager.build(this, machine); + } + + @Override + public void start(@EffectorParam(name = "locations") Collection<? extends Location> locations) { + this.locations = locations; + startOnLocations(); + } + + protected void startOnLocations() { + setExpectedState(this, STARTING); + int size = locations.size(); + LOG.debug("Starting simple command at {} locations{}", size, + size > 0 ? " beginning " + locations.iterator().next() : ""); + try { + execute(locations); + setUpAndRunState(true, RUNNING); + + } catch (final Exception e) { + setUpAndRunState(false, ON_FIRE); + throw Exceptions.propagate(e); + } + } + + private void execute(Collection<? extends Location> locations) { + SimpleCommandDriver.Result result = null; + String downloadUrl = getConfig(DOWNLOAD_URL); + if (Strings.isNonBlank(downloadUrl)) { + String scriptDir = getConfig(SCRIPT_DIR); + result = getDriver().executeDownloadedScript(locations, downloadUrl, scriptDir); + + } else { + String command = getConfig(DEFAULT_COMMAND); + if (Strings.isBlank(command)) { + throw new IllegalArgumentException("No default command and no downloadUrl provided"); + } + + result = getDriver().execute(locations, command); + } + handle(result); + } + + + @Override + public void stop() { + LOG.debug("Stopping simple command"); + setUpAndRunState(false, STOPPED); + } + + @Override + public void restart() { + LOG.debug("Restarting simple command"); + setUpAndRunState(true, RUNNING); + } + + private void setUpAndRunState(boolean up, Lifecycle status) { + sensors().set(SERVICE_UP, up); + setExpectedState(this, status); + } + + protected SimpleCommandLifecycleEffectorTasks getLifecycleEffectorTasks () { + return new SimpleCommandLifecycleEffectorTasks(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/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 new file mode 100644 index 0000000..d044212 --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandLifecycleEffectorTasks.java @@ -0,0 +1,73 @@ +/* + * 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); + private MachineLocation location; + + protected Location getLocation(@Nullable Collection<? extends Location> locations) { + return super.getLocation(entity().filterLocations(locations)); + } + + @Override + protected void preStartCustom(MachineLocation machine) { + location = machine; + super.preStartCustom(location); + LOG.debug("Performing lifecycle preStartCustom on simple command"); + entity().initDriver(location); + } + + @Override + protected void preRestartCustom() { + LOG.debug("Performing lifecycle preStartCustom on simple command"); + Asserts.notNull(location, "Cannot restart with no location"); + entity().initDriver(location); + } + + @Override + protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) { + LOG.debug("Performing lifecycle startProcessesAtMachine on simple command"); + entity().getDriver().start(); + return "Started with driver " + entity().getDriver(); + } + + @Override + protected String stopProcessesAtMachine() { + LOG.debug("Performing lifecycle stopProcessesAtMachine on simple command"); + entity().getDriver().stop(); + return "Stopped"; + } + + protected SimpleCommandImpl entity() { + return (SimpleCommandImpl) super.entity(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandSshDriver.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandSshDriver.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandSshDriver.java new file mode 100644 index 0000000..f386b1e --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandSshDriver.java @@ -0,0 +1,192 @@ +/* + * 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.entity.EntityLocal; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.mgmt.TaskAdaptable; +import org.apache.brooklyn.api.mgmt.TaskFactory; +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.ResourceUtils; +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.ProcessTaskFactory; +import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.exceptions.FatalRuntimeException; +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 com.google.common.base.Preconditions.checkNotNull; +import static org.apache.brooklyn.util.text.Strings.isBlank; + +/** + * Driver for {@link SimpleCommand}. + */ +public class SimpleCommandSshDriver implements SimpleCommandDriver { + + private static final Logger LOG = LoggerFactory.getLogger(SimpleCommandSshDriver.class); + public static final String DEFAULT_NAME = "download.sh"; + + protected final EntityLocal entity; + protected final ResourceUtils resource; + protected final Location location; + + public SimpleCommandSshDriver(EntityLocal entity, SshMachineLocation location) { + LOG.debug("Constructing SSH driver for simple command for {} at {}", entity, location); + this.entity = checkNotNull(entity, "entity"); + this.location = checkNotNull(location, "location"); + this.resource = ResourceUtils.create(entity); + } + + @Override + public EntityLocal getEntity() { + return entity; + } + + @Override + public void start() { + LOG.debug("Performing start in SSH driver for simple command"); + invoke(); + } + + private void invoke() { + SimpleCommand simpleCommand = (SimpleCommand) getEntity(); + simpleCommand.start(ImmutableList.of(location)); + } + + @Override + public void restart() { + LOG.debug("Performing restart in SSH driver for simple command"); + invoke(); + } + + @Override + public void stop() { + LOG.debug("Performing stop in SSH driver for simple command"); + } + + @Override + public Result execute(Collection<? extends Location> hostLocations, String command) { + + SshMachineLocation machine = getSshMachine(hostLocations); + ProcessTaskFactory<Integer> taskFactory = SshTasks.newSshExecTaskFactory(machine, command); + + LOG.debug("Creating task to execute '{}' on location {}", command, machine); + final ProcessTaskWrapper<Integer> job = DynamicTasks.queue(taskFactory); + DynamicTasks.waitForLast(); + return buildResult(job); + } + + private <T> Result buildResult(final ProcessTaskWrapper<Integer> job) { + return new Result() { + + @Override + public int getExitCode() { + return job.get(); + } + + @Override + public String getStdout() { + return job.getStdout().trim(); + } + + @Override + public String getStderr() { + return job.getStderr().trim(); + } + }; + } + + @Override + public Result executeDownloadedScript(Collection<? extends Location> hostLocations, + String url, String directory) { + + SshMachineLocation machine = getSshMachine(hostLocations); + String destPath = calculateDestPath(url, directory); + + TaskFactory<?> install = SshTasks.installFromUrl(ImmutableMap.<String, Object>of(), machine, url, destPath); + DynamicTasks.queue(install); + DynamicTasks.waitForLast(); + + machine.execCommands("make the script executable", ImmutableList.<String>of("chmod u+x " + destPath)); + + return execute(hostLocations, destPath); + } + + 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 Exceptions.propagate(new FatalRuntimeException("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; + } + + 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(); + } + + @Override + public Location getLocation() { + return location; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/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 new file mode 100644 index 0000000..c962403 --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTest.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(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/b4e3ac7b/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 new file mode 100644 index 0000000..261afe7 --- /dev/null +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/SimpleCommandTestImpl.java @@ -0,0 +1,149 @@ +/* + * 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(SimpleCommandDriver.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/b4e3ac7b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestCase.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestCase.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestCase.java index 78fd8d6..d05889d 100644 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestCase.java +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestCase.java @@ -19,6 +19,7 @@ package org.apache.brooklyn.test.framework; import org.apache.brooklyn.api.entity.ImplementedBy; +import org.apache.brooklyn.core.entity.trait.Startable; /** * Entity that logically groups other test entities http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java index 14c45f5..6639845 100644 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java @@ -23,6 +23,7 @@ import com.google.common.reflect.TypeToken; 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.util.core.flags.SetFromFlag; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCallImpl.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCallImpl.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCallImpl.java index 66e24da..a1d84bf 100644 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCallImpl.java +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCallImpl.java @@ -58,7 +58,7 @@ public class TestHttpCallImpl extends AbstractTest implements TestHttpCall { sensors().set(SERVICE_UP, true); ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); } catch (Throwable t) { - LOG.info("Url [{}] test failed", url); + LOG.debug("Url [{}] test failed", url); sensors().set(SERVICE_UP, false); ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); throw Exceptions.propagate(t); @@ -89,7 +89,7 @@ public class TestHttpCallImpl extends AbstractTest implements TestHttpCall { for (final Map.Entry<?, ?> entry : assertions.entrySet()) { if (Objects.equal(entry.getKey(), "regex")) { - LOG.info("Testing if url [{}] matches regex [{}]", + LOG.debug("Testing if url [{}] matches regex [{}]", new Object[]{url, entry.getValue()}); assertContentEventuallyMatches(flags, url, TypeCoercions.coerce(entry.getValue(), String.class)); } else if (Objects.equal(entry.getKey(), "bodyContains")) { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensor.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensor.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensor.java index ec070d1..3655501 100644 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensor.java +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensor.java @@ -21,6 +21,7 @@ package org.apache.brooklyn.test.framework; 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.util.core.flags.SetFromFlag; /** http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensorImpl.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensorImpl.java b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensorImpl.java index c2d6169..f8c96a0 100644 --- a/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensorImpl.java +++ b/usage/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestSensorImpl.java @@ -65,7 +65,7 @@ public class TestSensorImpl extends AbstractTest implements TestSensor { sensors().set(SERVICE_UP, true); ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); } catch (Throwable t) { - LOG.info("Sensor [{}] test failed", sensor); + LOG.debug("Sensor [{}] test failed", sensor); sensors().set(SERVICE_UP, false); ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE); throw Exceptions.propagate(t); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandImplTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandImplTest.java b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandImplTest.java new file mode 100644 index 0000000..fac54aa --- /dev/null +++ b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandImplTest.java @@ -0,0 +1,117 @@ +/* + * 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.Predicate; +import com.google.common.base.Strings; +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.api.mgmt.ManagementContext; +import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic; +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; +import org.apache.brooklyn.util.core.task.Tasks; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import javax.annotation.Nullable; +import java.util.UUID; + +import static org.apache.brooklyn.test.framework.BaseTest.TARGET_ENTITY; +import static org.apache.brooklyn.test.framework.SimpleCommand.DEFAULT_COMMAND; +import static org.apache.brooklyn.test.framework.SimpleCommandTest.*; +import static org.assertj.core.api.Assertions.assertThat; + +public class SimpleCommandImplTest { + private static final Logger LOG = LoggerFactory.getLogger(SimpleCommandImplTest.class); + + private static final String UP = "up"; + private TestApplication app; + private ManagementContext managementContext; + private LocalhostMachineProvisioningLocation localhost; + private String testId; + + + @BeforeMethod + public void setUp() { + + testId = UUID.randomUUID().toString(); + + app = TestApplication.Factory.newManagedInstanceForTests(); + managementContext = app.getManagementContext(); + + localhost = managementContext.getLocationManager() + .createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class) + .configure("name", testId)); + } + + @AfterMethod(alwaysRun = true) + public void tearDown() throws Exception { + if (app != null) Entities.destroyAll(app.getManagementContext()); + } + + @Test + public void shouldInvokeCommand() { + TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + SimpleCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleCommandTest.class) + .configure(TARGET_ENTITY, testEntity) + .configure(DEFAULT_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 + public void shouldNotBeUpIfAssertionFails() { + TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + SimpleCommandTest uptime = app.createAndManageChild(EntitySpec.create(SimpleCommandTest.class) + .configure(TARGET_ENTITY, testEntity) + .configure(DEFAULT_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"); + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandScriptIntegrationTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandScriptIntegrationTest.java b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandScriptIntegrationTest.java new file mode 100644 index 0000000..e081cef --- /dev/null +++ b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/SimpleCommandScriptIntegrationTest.java @@ -0,0 +1,134 @@ +/* + * 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.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic; +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; +import org.apache.brooklyn.test.http.TestHttpRequestHandler; +import org.apache.brooklyn.test.http.TestHttpServer; +import org.apache.brooklyn.util.core.task.Tasks; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.exceptions.FatalRuntimeException; +import org.apache.brooklyn.util.http.HttpAsserts; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.*; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Iterator; +import java.util.UUID; + +import static org.apache.brooklyn.test.framework.BaseTest.TARGET_ENTITY; +import static org.apache.brooklyn.test.framework.SimpleCommand.DEFAULT_COMMAND; +import static org.apache.brooklyn.test.framework.SimpleCommandTest.*; +import static org.assertj.core.api.Assertions.assertThat; + +@Test(groups = "Integration") +public class SimpleCommandScriptIntegrationTest { + private static final Logger LOG = LoggerFactory.getLogger(SimpleCommandScriptIntegrationTest.class); + + private static final String UP = "up"; + private static final String SCRIPT_NAME = "script.sh"; + private static final String TEXT = "hello world"; + private TestApplication app; + private ManagementContext managementContext; + private LocalhostMachineProvisioningLocation localhost; + private TestHttpServer server; + private String testId; + + + @BeforeClass + public void setUpTests() { + server = initializeServer(); + } + + @AfterClass + public void tearDownTests() { + if (null != server) { + server.stop(); + } + } + + @BeforeMethod + public void setUp() { + + testId = UUID.randomUUID().toString(); + + app = TestApplication.Factory.newManagedInstanceForTests(); + managementContext = app.getManagementContext(); + + localhost = managementContext.getLocationManager() + .createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class) + .configure("name", testId)); + } + + @AfterMethod(alwaysRun = true) + public void tearDown() throws Exception { + if (app != null) Entities.destroyAll(app.getManagementContext()); + } + + + private TestHttpServer initializeServerUnstarted() { + return new TestHttpServer() + .handler("/" + SCRIPT_NAME, + new TestHttpRequestHandler().response("#!/bin/sh\necho " + TEXT + "\n")); + } + private TestHttpServer initializeServer() { + return initializeServerUnstarted().start(); + } + + + + @Test + public void shouldInvokeScript() { + TestEntity testEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + String testUrl = server.getUrl() + "/" + SCRIPT_NAME; + HttpAsserts.assertContentContainsText(testUrl, 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"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java index ab01eea..b6f3c4a 100644 --- a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java +++ b/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java @@ -51,7 +51,8 @@ public class TestEffectorTest { app = TestApplication.Factory.newManagedInstanceForTests(); managementContext = app.getManagementContext(); - loc = managementContext.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class) + loc = managementContext.getLocationManager() + .createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class) .configure("name", testId)); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4e3ac7b/usage/test-framework/src/test/resources/brooklyn/logback-appender-stdout.xml ---------------------------------------------------------------------- diff --git a/usage/test-framework/src/test/resources/brooklyn/logback-appender-stdout.xml b/usage/test-framework/src/test/resources/brooklyn/logback-appender-stdout.xml new file mode 100644 index 0000000..f2515ad --- /dev/null +++ b/usage/test-framework/src/test/resources/brooklyn/logback-appender-stdout.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<included> + + <!-- change settings in this file for (temporary!) debug purposes, e.g. change filter level to DEBUG below --> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>%d %-5level %msg%n%xEx{0}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> + <level>INFO</level> + </filter> + </appender> + + <root> + <appender-ref ref="STDOUT" /> + </root> + +</included> \ No newline at end of file
