/*
* 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.lifecycle;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.List;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.core.sensor.DependentConfiguration;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.core.test.entity.TestApplication;
import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
import org.apache.brooklyn.entity.software.base.SoftwareProcess;
import org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode;
import org.apache.brooklyn.entity.stock.BasicEntity;
import org.apache.brooklyn.entity.stock.BasicEntityImpl;
import org.apache.brooklyn.location.jclouds.BailOutJcloudsLocation;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.core.task.TaskInternal;
import org.apache.brooklyn.util.core.task.ValueResolver;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.time.Duration;
import org.apache.brooklyn.util.time.Time;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
public class MachineLifecycleEffectorTasksTest {
public static boolean canStop(StopMode stopMode, boolean isEntityStopped) {
BasicEntityImpl entity = new BasicEntityImpl();
Lifecycle state = isEntityStopped ? Lifecycle.STOPPED : Lifecycle.RUNNING;
entity.sensors().set(SoftwareProcess.SERVICE_STATE_ACTUAL, state);
return MachineLifecycleEffectorTasks.canStop(stopMode, entity);
}
@DataProvider(name = "canStopStates")
public Object[][] canStopStates() {
return new Object[][] {
{ StopMode.ALWAYS, true, true },
{ StopMode.ALWAYS, false, true },
{ StopMode.IF_NOT_STOPPED, true, false },
{ StopMode.IF_NOT_STOPPED, false, true },
{ StopMode.NEVER, true, false },
{ StopMode.NEVER, false, false },
};
}
@Test(dataProvider = "canStopStates")
public void testBasicSonftwareProcessCanStop(StopMode mode, boolean isEntityStopped, boolean expected) {
boolean canStop = canStop(mode, isEntityStopped);
assertEquals(canStop, expected);
}
@Test(groups="Integration")
public void testProvisionLatchObeyed() throws Exception {
AttributeSensor<Boolean> ready = Sensors.newBooleanSensor("readiness");
TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
BasicEntity triggerEntity = app.createAndManageChild(EntitySpec.create(BasicEntity.class));
EmptySoftwareProcess entity = app.createAndManageChild(EntitySpec.create(EmptySoftwareProcess.class)
.configure(BrooklynConfigKeys.PROVISION_LATCH, DependentConfiguration.attributeWhenReady(triggerEntity, ready)));
final Task<Void> task = Entities.invokeEffector(app, app, Startable.START, ImmutableMap.of(
"locations", ImmutableList.of(BailOutJcloudsLocation.newBailOutJcloudsLocation(app.getManagementContext()))));
Time.sleep(ValueResolver.PRETTY_QUICK_WAIT);
if (task.isDone()) throw new IllegalStateException("Task finished early with: "+task.get());
assertEffectorBlockingDetailsEventually(entity, "Waiting for config " + BrooklynConfigKeys.PROVISION_LATCH.getName());
Asserts.succeedsContinually(new Runnable() {
@Override
public void run() {
if (task.isDone()) throw new IllegalStateException("Task finished early with: "+task.getUnchecked());
}
});
try {
((EntityLocal) triggerEntity).sensors().set(ready, true);
task.get(Duration.THIRTY_SECONDS);
} catch (Throwable t) {
Exceptions.propagateIfFatal(t);
if ((t.toString().contains(BailOutJcloudsLocation.ERROR_MESSAGE))) {
// expected - BailOut location throws - just swallow
} else {
Exceptions.propagate(t);
}
} finally {
Entities.destroyAll(app.getManagementContext());
}
}
private void assertEffectorBlockingDetailsEventually(final Entity entity, final String blockingDetailsSnippet) {
Asserts.succeedsEventually(new Runnable() {
@Override public void run() {
Task<?> entityTask = Iterables.getOnlyElement(entity.getApplication().getManagementContext().getExecutionManager().getTasksWithAllTags(
ImmutableList.of(BrooklynTaskTags.EFFECTOR_TAG, BrooklynTaskTags.tagForContextEntity(entity))));
String blockingDetails = getBlockingDetails(entityTask);
assertTrue(blockingDetails.contains(blockingDetailsSnippet));
}});
}
private String getBlockingDetails(Task<?> task) {
List<TaskInternal<?>> taskChain = Lists.newArrayList();
TaskInternal<?> taskI = (TaskInternal<?>) task;
while (taskI != null) {
taskChain.add(taskI);
if (taskI.getBlockingDetails() != null) {
return taskI.getBlockingDetails();
}
taskI = (TaskInternal<?>) taskI.getBlockingTask();
}
throw new IllegalStateException("No blocking details for "+task+" (walked task chain "+taskChain+")");
}
}