BROOKLYN-264: renames + updates live test Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/e889df53 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/e889df53 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/e889df53
Branch: refs/heads/master Commit: e889df538ca3b6acbfbf3c534412edbd336295bb Parents: 8f3c09f Author: Aled Sage <aled.s...@gmail.com> Authored: Tue Jul 19 11:57:15 2016 +0100 Committer: Aled Sage <aled.s...@gmail.com> Committed: Tue Jul 19 11:57:15 2016 +0100 ---------------------------------------------------------------------- .../MachineLifecycleEffectorTasks.java | 8 +- .../ExpungingJcloudsLocationLiveTest.java | 186 --------------- ...eProcessStopsDuringStartJcloudsLiveTest.java | 231 +++++++++++++++++++ .../SoftwareProcessStopsDuringStartTest.java | 47 ++++ 4 files changed, 282 insertions(+), 190 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e889df53/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java index 2e11120..75f7a06 100644 --- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java +++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java @@ -145,15 +145,15 @@ public abstract class MachineLifecycleEffectorTasks { @Beta public static final AttributeSensor<ProvisioningTaskState> PROVISIONING_TASK_STATE = new BasicAttributeSensor<ProvisioningTaskState>( TypeToken.of(ProvisioningTaskState.class), - "provisioning.task.state", - "Internal transient sensor for tracking the provisioning of a machine (to better handle aborting)", + "internal.provisioning.task.state", + "Internal transient sensor (do not use) for tracking the provisioning of a machine (to better handle aborting)", AttributeSensor.SensorPersistenceMode.NONE); @Beta public static final AttributeSensor<MachineLocation> PROVISIONED_MACHINE = new BasicAttributeSensor<MachineLocation>( TypeToken.of(MachineLocation.class), - "provisioning.task.machine", - "Internal transient sensor for tracking the machine being provisioned (to better handle aborting)", + "internal.provisioning.task.machine", + "Internal transient sensor (do not use) for tracking the machine being provisioned (to better handle aborting)", AttributeSensor.SensorPersistenceMode.NONE); /** http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e889df53/software/base/src/test/java/org/apache/brooklyn/entity/ExpungingJcloudsLocationLiveTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/ExpungingJcloudsLocationLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/ExpungingJcloudsLocationLiveTest.java deleted file mode 100644 index 658a36f..0000000 --- a/software/base/src/test/java/org/apache/brooklyn/entity/ExpungingJcloudsLocationLiveTest.java +++ /dev/null @@ -1,186 +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.entity; - -import com.google.common.base.Predicate; -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.core.entity.Attributes; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.EntityAsserts; -import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; -import org.apache.brooklyn.core.internal.BrooklynProperties; -import org.apache.brooklyn.core.location.LocationConfigKeys; -import org.apache.brooklyn.core.location.cloud.CloudLocationConfig; -import org.apache.brooklyn.core.mgmt.EntityManagementUtils; -import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport; -import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; -import org.apache.brooklyn.core.test.entity.TestApplication; -import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess; -import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess; -import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks; -import org.apache.brooklyn.location.jclouds.JcloudsLocation; -import org.apache.brooklyn.util.collections.MutableMap; -import org.jclouds.aws.ec2.compute.AWSEC2ComputeService; -import org.jclouds.compute.domain.ComputeMetadata; -import org.jclouds.compute.domain.NodeMetadata; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import javax.annotation.Nullable; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; - -import static org.apache.brooklyn.test.Asserts.*; - -public class ExpungingJcloudsLocationLiveTest extends BrooklynAppLiveTestSupport { - private static final Logger LOG = LoggerFactory.getLogger(ExpungingJcloudsLocationLiveTest.class); - - protected BrooklynProperties brooklynProperties; - - @BeforeMethod(alwaysRun=true) - @Override - public void setUp() throws Exception { - // Don't let any defaults from brooklyn.properties (except credentials) interfere with test - brooklynProperties = BrooklynProperties.Factory.newDefault(); - - // Also removes scriptHeader (e.g. if doing `. ~/.bashrc` and `. ~/.profile`, then that can cause "stdin: is not a tty") - brooklynProperties.remove("brooklyn.ssh.config.scriptHeader"); - - mgmt = new LocalManagementContextForTests(brooklynProperties); - super.setUp(); - } - - @Test - public void verifyExpungingMockedEntityIsQuick() throws Exception { - final EmptySoftwareProcess emptySoftwareProcess = app.createAndManageChild(EntitySpec.create(EmptySoftwareProcess.class)); - executeInLimitedTime(new Callable<Void>() { - public Void call() { - app.start(ImmutableList.of(mgmt.getLocationManager().createLocation(TestApplication.LOCALHOST_PROVISIONER_SPEC))); - return null; - } - }, 2, TimeUnit.SECONDS); - EntityAsserts.assertEntityHealthy(emptySoftwareProcess); - assertEquals(emptySoftwareProcess.getAttribute(MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE), MachineLifecycleEffectorTasks.ProvisioningTaskState.DONE); - - executeInLimitedTime(new Callable<Void>() { - public Void call() { - Entities.destroy(app); - return null; - } - }, 1, TimeUnit.SECONDS); - assertEquals(app.getAttribute(Attributes.SERVICE_STATE_ACTUAL), Lifecycle.STOPPED); - assertEquals(app.getAttribute(Attributes.SERVICE_STATE_EXPECTED).getState(), Lifecycle.STOPPED); - } - - @Test(groups = "Integration") - public void verifyExpungingByonLocationIsQuick() throws Exception { - final VanillaSoftwareProcess entity = app.addChild(EntitySpec.create(VanillaSoftwareProcess.class) - .configure(VanillaSoftwareProcess.INSTALL_COMMAND, "echo install") - .configure(VanillaSoftwareProcess.LAUNCH_COMMAND, "echo launch") - .configure(VanillaSoftwareProcess.CHECK_RUNNING_COMMAND, "echo running")); - app.addLocations(ImmutableList.of(mgmt.getLocationFactory().createLocation(TestApplication.LOCALHOST_PROVISIONER_SPEC))); - - EntityManagementUtils.start(app); - - succeedsEventually(ImmutableMap.of("timeout", "4s"), new Callable<Boolean>() { - public Boolean call() { - assertTrue(entity.sensors().get(Attributes.SERVICE_UP)); - assertEquals(entity.getAttribute(MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE), MachineLifecycleEffectorTasks.ProvisioningTaskState.DONE); - return entity.sensors().get(Attributes.SERVICE_UP); - } - }); - - executeInLimitedTime(new Callable<Void>() { - public Void call() { - Entities.destroy(app); - return null; - } - }, 2, TimeUnit.SECONDS); - // Make sure that the entity will be stopped fast. Two seconds at most. - assertEquals(app.getAttribute(Attributes.SERVICE_STATE_ACTUAL), Lifecycle.STOPPED); - assertEquals(app.getAttribute(Attributes.SERVICE_STATE_EXPECTED).getState(), Lifecycle.STOPPED); - } - - public static final String PROVIDER = "aws-ec2"; - public static final String REGION_NAME = "us-west-2"; - public static final String LOCATION_SPEC = PROVIDER + (REGION_NAME == null ? "" : ":" + REGION_NAME); - - /** - * Verifies the behavior described in - * <a href="https://issues.apache.org/jira/browse/BROOKLYN-264">BROOKLYN-264 Stop app while VM still being provisioned: vm is left running when app is expunged</a> - * <ul> - * <li>ApplicationResource.launch</li> - * <li>wait a few seconds and EntityResponse.expunge</li> - * <li>assert the image is on the cloud</li> - * </ul> - */ - @Test(groups = {"Live"}) - public void verifyJclousMachineIsExpungedWhenStoppedImmediatelyAfterStart() { - Map<String,String> flags = ImmutableMap.of("imageId", "us-west-2/ami-cd715dfd", LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS.getName(), ""); - Map<String,?> allFlags = MutableMap.<String,Object>builder() - .put("tags", ImmutableList.of(getClass().getName())) - .putAll(flags) - .build(); - JcloudsLocation jcloudsLocation = (JcloudsLocation)mgmt.getLocationRegistry().getLocationManaged(LOCATION_SPEC, allFlags); - - final EmptySoftwareProcess emptySoftwareProcess = app.createAndManageChild(EntitySpec.create(EmptySoftwareProcess.class) - .configure(EmptySoftwareProcess.PROVISIONING_PROPERTIES.subKey(CloudLocationConfig.INBOUND_PORTS.getName()), ImmutableList.of(22))); - - app.addLocations(ImmutableList.of(jcloudsLocation)); - - EntityManagementUtils.start(app); - - succeedsEventually(ImmutableMap.of("timeout", "16s"), new Callable<MachineLifecycleEffectorTasks.ProvisioningTaskState>() { - public MachineLifecycleEffectorTasks.ProvisioningTaskState call() { - assertEquals(emptySoftwareProcess.getAttribute(MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE), MachineLifecycleEffectorTasks.ProvisioningTaskState.RUNNING); - return emptySoftwareProcess.getAttribute(MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE); - } - }); - - long beginTime = System.currentTimeMillis(); - Entities.destroyCatching(app); - LOG.info("Time for expunging: {}", System.currentTimeMillis() - beginTime); - - NodeMetadata nodeMetadata = Iterables.getFirst(((AWSEC2ComputeService) jcloudsLocation.getComputeService()).listNodesDetailsMatching(new Predicate<ComputeMetadata>() { - @Override public boolean apply(@Nullable ComputeMetadata computeMetadata) { - return ((NodeMetadata)computeMetadata).getGroup() == null ? false - : Pattern.matches( - "brooklyn-.*" + System.getProperty("user.name") + ".*emptysoftware.*"+emptySoftwareProcess.getId().substring(0, 4), - ((NodeMetadata)computeMetadata).getGroup() - ); - }}), - null); - LOG.info("nodeMetadata found after app was created: {}", nodeMetadata); - assertTrue(nodeMetadata.getStatus().equals(NodeMetadata.Status.TERMINATED), "The application should be destroyed after stop effector was called."); - } - - private <T> T executeInLimitedTime(Callable<T> callable, long timeout, TimeUnit timeUnit) throws Exception { - Future<T> future = Executors.newCachedThreadPool().submit(callable); - return future.get(timeout, timeUnit); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e889df53/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartJcloudsLiveTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartJcloudsLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartJcloudsLiveTest.java new file mode 100644 index 0000000..c43f739 --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartJcloudsLiveTest.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.entity.software.base; + +import static org.apache.brooklyn.test.Asserts.assertEquals; +import static org.apache.brooklyn.test.Asserts.assertNotNull; +import static org.apache.brooklyn.test.Asserts.assertTrue; +import static org.apache.brooklyn.test.Asserts.fail; + +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.api.location.ProvisioningLocation; +import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.core.entity.Attributes; +import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.EntityAsserts; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.entity.trait.Startable; +import org.apache.brooklyn.core.internal.BrooklynProperties; +import org.apache.brooklyn.core.location.LocationConfigKeys; +import org.apache.brooklyn.core.location.Machines; +import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.entity.AbstractEc2LiveTest; +import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks; +import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation; +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool; +import org.apache.brooklyn.util.time.Duration; +import org.jclouds.aws.ec2.compute.AWSEC2ComputeService; +import org.jclouds.compute.domain.ComputeMetadata; +import org.jclouds.compute.domain.NodeMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Predicate; +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +public class SoftwareProcessStopsDuringStartJcloudsLiveTest extends BrooklynAppLiveTestSupport { + private static final Logger LOG = LoggerFactory.getLogger(SoftwareProcessStopsDuringStartJcloudsLiveTest.class); + + // same image as in AbstractEc2LiveTest.test_CentOS_6_3 + // Image: {id=us-east-1/ami-a96b01c0, providerId=ami-a96b01c0, name=CentOS-6.3-x86_64-GA-EBS-02-85586466-5b6c-4495-b580-14f72b4bcf51-ami-bb9af1d2.1, location={scope=REGION, id=us-east-1, description=us-east-1, parent=aws-ec2, iso3166Codes=[US-VA]}, os={family=centos, arch=paravirtual, version=6.3, description=aws-marketplace/CentOS-6.3-x86_64-GA-EBS-02-85586466-5b6c-4495-b580-14f72b4bcf51-ami-bb9af1d2.1, is64Bit=true}, description=CentOS-6.3-x86_64-GA-EBS-02 on EBS x86_64 20130527:1219, version=bb9af1d2.1, status=AVAILABLE[available], loginUser=root, userMetadata={owner=679593333241, rootDeviceType=ebs, virtualizationType=paravirtual, hypervisor=xen}}) + public static final String PROVIDER = "aws-ec2"; + public static final String REGION_NAME = "us-east-1"; + public static final String IMAGE_ID = "us-east-1/ami-a96b01c0"; + public static final String HARDWARE_ID = AbstractEc2LiveTest.SMALL_HARDWARE_ID; + public static final String LOCATION_SPEC = PROVIDER + (REGION_NAME == null ? "" : ":" + REGION_NAME); + + protected BrooklynProperties brooklynProperties; + + protected ExecutorService executor; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + // Don't let any defaults from brooklyn.properties (except credentials) interfere with test + brooklynProperties = BrooklynProperties.Factory.newDefault(); + + // Also removes scriptHeader (e.g. if doing `. ~/.bashrc` and `. ~/.profile`, then that can cause "stdin: is not a tty") + brooklynProperties.remove("brooklyn.ssh.config.scriptHeader"); + + mgmt = new LocalManagementContextForTests(brooklynProperties); + super.setUp(); + + executor = Executors.newCachedThreadPool(); + } + + @Override + public void tearDown() throws Exception { + if (executor != null) { + executor.shutdownNow(); + } + super.tearDown(); + } + + // Integration because takes approx 1 seconds + @Test(groups = "Integration") + public void testStartStopSequentiallyIsQuickInLocalhost() throws Exception { + LocalhostMachineProvisioningLocation localLoc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class) + .configure(SshMachineLocation.SSH_TOOL_CLASS, RecordingSshTool.class.getName())); + runStartStopSequentiallyIsQuick(localLoc); + } + + // Integration because takes approx 1 seconds + @Test(groups = "Integration") + public void testStartStopSequentiallyIsQuickInByon() throws Exception { + FixedListMachineProvisioningLocation<?> byonLoc = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class) + .configure(FixedListMachineProvisioningLocation.MACHINE_SPECS, ImmutableList.<LocationSpec<? extends MachineLocation>>of( + LocationSpec.create(SshMachineLocation.class) + .configure("address", "1.2.3.4") + .configure(SshMachineLocation.SSH_TOOL_CLASS, RecordingSshTool.class.getName())))); + runStartStopSequentiallyIsQuick(byonLoc); + } + + protected void runStartStopSequentiallyIsQuick(final ProvisioningLocation<?> loc) throws Exception { + final EmptySoftwareProcess entity = app.createAndManageChild(EntitySpec.create(EmptySoftwareProcess.class) + .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true)); + + executeInLimitedTime(new Callable<Void>() { + public Void call() { + app.start(ImmutableList.of(loc)); + return null; + } + }, Asserts.DEFAULT_LONG_TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS); + EntityAsserts.assertEntityHealthy(entity); + assertEquals(entity.getAttribute(MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE), MachineLifecycleEffectorTasks.ProvisioningTaskState.DONE); + assertEquals(entity.getAttribute(MachineLifecycleEffectorTasks.PROVISIONED_MACHINE), Machines.findUniqueMachineLocation(entity.getLocations(), SshMachineLocation.class).get()); + + executeInLimitedTime(new Callable<Void>() { + public Void call() { + Entities.destroy(app); + return null; + } + }, Asserts.DEFAULT_LONG_TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS); + assertEquals(app.getAttribute(Attributes.SERVICE_STATE_ACTUAL), Lifecycle.STOPPED); + assertEquals(app.getAttribute(Attributes.SERVICE_STATE_EXPECTED).getState(), Lifecycle.STOPPED); + assertEquals(entity.getAttribute(MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE), null); + assertEquals(entity.getAttribute(MachineLifecycleEffectorTasks.PROVISIONED_MACHINE), null); + } + + /** + * Verifies the behavior described in + * <a href="https://issues.apache.org/jira/browse/BROOKLYN-264">BROOKLYN-264 Stop app while VM still being provisioned: vm is left running when app is expunged</a> + * <ul> + * <li>Launch the app + * <li>wait a few seconds (for entity internal state to indicate the provisioning is happening) + * <li>Expunge the app (thus calling stop on the entity) + * <li>assert the image is terminated (using jclouds directly to query the cloud api) + * </ul> + */ + @Test(groups = {"Live"}) + public void testJclousMachineIsExpungedWhenStoppedDuringStart() throws Exception { + Map<String,?> allFlags = ImmutableMap.<String,Object>builder() + .put("tags", ImmutableList.of(getClass().getName())) + .put(JcloudsLocation.IMAGE_ID.getName(), IMAGE_ID) + .put(JcloudsLocation.HARDWARE_ID.getName(), HARDWARE_ID) + .put(LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS.getName(), "") + .put(JcloudsLocation.MACHINE_CREATE_ATTEMPTS.getName(), 1) + .put(JcloudsLocation.OPEN_IPTABLES.getName(), true) + .build(); + JcloudsLocation jcloudsLocation = (JcloudsLocation)mgmt.getLocationRegistry().getLocationManaged(LOCATION_SPEC, allFlags); + + final VanillaSoftwareProcess entity = app.createAndManageChild(EntitySpec.create(VanillaSoftwareProcess.class) + .configure(VanillaSoftwareProcess.INSTALL_COMMAND, "echo install") + .configure(VanillaSoftwareProcess.LAUNCH_COMMAND, "echo launch") + .configure(VanillaSoftwareProcess.CHECK_RUNNING_COMMAND, "echo running")); + + app.addLocations(ImmutableList.of(jcloudsLocation)); + + // Invoke async + @SuppressWarnings("unused") + Task<Void> startTask = Entities.invokeEffector(app, app, Startable.START, ImmutableMap.of("locations", MutableList.of())); + EntityAsserts.assertAttributeEqualsEventually(entity, MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE, MachineLifecycleEffectorTasks.ProvisioningTaskState.RUNNING); + + Stopwatch stopwatch = Stopwatch.createStarted(); + Entities.destroyCatching(app); + LOG.info("Time for expunging: {}", Duration.of(stopwatch)); + + NodeMetadata nodeMetadata = Iterables.getFirst(((AWSEC2ComputeService) jcloudsLocation.getComputeService()).listNodesDetailsMatching(new Predicate<ComputeMetadata>() { + @Override public boolean apply(@Nullable ComputeMetadata computeMetadata) { + return ((NodeMetadata)computeMetadata).getGroup() == null + ? false + : Pattern.matches( + "brooklyn-.*" + System.getProperty("user.name") + ".*vanillasoftware.*"+entity.getId().substring(0, 4), + ((NodeMetadata)computeMetadata).getGroup() + ); + }}), + null); + assertNotNull(nodeMetadata, "node matching node found"); + LOG.info("nodeMetadata found after app was created: {}", nodeMetadata); + + // If pending (e.g. "status=PENDING[shutting-down]"), wait for it to transition + Stopwatch pendingStopwatch = Stopwatch.createStarted(); + Duration maxPendingWait = Duration.FIVE_MINUTES; + while (maxPendingWait.isLongerThan(Duration.of(pendingStopwatch)) && nodeMetadata.getStatus() == NodeMetadata.Status.PENDING) { + Thread.sleep(1000); + nodeMetadata = ((AWSEC2ComputeService) jcloudsLocation.getComputeService()).getNodeMetadata(nodeMetadata.getId()); + } + + if (nodeMetadata.getStatus() != NodeMetadata.Status.TERMINATED) { + // Try to terminate the VM - don't want test to leave it behind! + String errMsg = "The application should be destroyed after stop effector was called: status="+nodeMetadata.getStatus()+"; node="+nodeMetadata; + LOG.error(errMsg); + jcloudsLocation.getComputeService().destroyNode(nodeMetadata.getId()); + fail(errMsg); + } + } + + private <T> T executeInLimitedTime(Callable<T> callable, long timeout, TimeUnit timeUnit) throws Exception { + Future<T> future = executor.submit(callable); + return future.get(timeout, timeUnit); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e889df53/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartTest.java index 8772253..49acdd2 100644 --- a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartTest.java +++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/SoftwareProcessStopsDuringStartTest.java @@ -96,7 +96,10 @@ public class SoftwareProcessStopsDuringStartTest extends BrooklynAppUnitTestSupp EntityAsserts.assertAttributeEquals(entity, MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE, MachineLifecycleEffectorTasks.ProvisioningTaskState.DONE); EntityAsserts.assertAttributeEquals(entity, MachineLifecycleEffectorTasks.PROVISIONED_MACHINE, machine); + Stopwatch stopwatch = Stopwatch.createStarted(); entity.stop(); + Duration stopDuration = Duration.of(stopwatch); + assertTrue(Asserts.DEFAULT_LONG_TIMEOUT.isLongerThan(stopDuration), "stop took "+stopDuration); EntityAsserts.assertAttributeEquals(entity, MachineLifecycleEffectorTasks.PROVISIONING_TASK_STATE, null); EntityAsserts.assertAttributeEquals(entity, MachineLifecycleEffectorTasks.PROVISIONED_MACHINE, null); @@ -200,13 +203,53 @@ public class SoftwareProcessStopsDuringStartTest extends BrooklynAppUnitTestSupp assertEquals(loc.getCalls(), ImmutableList.of("obtain")); } + @Test + public void testStopWhenProvisionFails() throws Exception { + loc.setObtainToFail(0); + + executor.submit(new Runnable() { + public void run() { + entity.start(ImmutableList.<Location>of(loc)); + }}); + loc.getObtainCalledLatch(0).await(); + + // Calling stop - it should block + // TODO Nicer way of ensuring that stop is really waiting? We wait for the log message! + Future<?> stopFuture; + LogWatcher watcher = new LogWatcher( + MachineLifecycleEffectorTasks.class.getName(), + ch.qos.logback.classic.Level.INFO, + EventPredicates.containsMessage("for the machine to finish provisioning, before terminating it") ); + watcher.start(); + try { + stopFuture = executor.submit(new Runnable() { + public void run() { + entity.stop(); + }}); + watcher.assertHasEventEventually(); + } finally { + watcher.close(); + } + assertFalse(stopFuture.isDone()); + + // When the loc.obtain() call throws exception, that will allow stop() to complete. + // It must not wait for the full 10 minutes. + loc.getObtainResumeLatch(0).countDown(); + stopFuture.get(Asserts.DEFAULT_LONG_TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS); // should be successful + } + public static class DelayedProvisioningLocation extends AbstractLocation implements MachineProvisioningLocation<SshMachineLocation> { + public List<Integer> obtainsToFail = MutableList.of(); public List<CountDownLatch> obtainCalledLatches = MutableList.of(new CountDownLatch(1)); public List<CountDownLatch> obtainResumeLatches = MutableList.of(new CountDownLatch(1)); private Set<SshMachineLocation> obtainedMachines = Sets.newConcurrentHashSet(); private final List<String> calls = Lists.newCopyOnWriteArrayList(); private final AtomicInteger obtainCount = new AtomicInteger(); + public void setObtainToFail(int index) { + this.obtainsToFail.add(index); + } + public void setObtainResumeLatches(List<CountDownLatch> latches) { this.obtainResumeLatches = latches; } @@ -235,6 +278,10 @@ public class SoftwareProcessStopsDuringStartTest extends BrooklynAppUnitTestSupp getObtainCalledLatch(count).countDown(); getObtainResumeLatch(count).await(); + if (obtainsToFail.contains(count)) { + throw new RuntimeException("Simulate failure in obtain"); + } + SshMachineLocation result = getManagementContext().getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class) .parent(this) .configure(SshMachineLocation.SSH_TOOL_CLASS, RecordingSshTool.class.getName())