From b8782c253f87f79d84c65c93f0391ec527729383 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 6 Nov 2017 17:54:28 +0000 Subject: [PATCH] BROOKLYN-547: fix VanillaSoftwareProcess.restart with no ssh-monitoring --- .../base/VanillaSoftwareProcessImpl.java | 11 +++ .../base/VanillaSoftwareProcessTest.java | 74 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessImpl.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessImpl.java index 5fa7b662cb..02a34f19c9 100644 --- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessImpl.java +++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessImpl.java @@ -18,7 +18,10 @@ */ package org.apache.brooklyn.entity.software.base; +import org.apache.brooklyn.api.sensor.Enricher; +import org.apache.brooklyn.core.entity.EntityAdjuncts; import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceNotUpLogic; +import org.apache.brooklyn.util.guava.Maybe; public class VanillaSoftwareProcessImpl extends SoftwareProcessImpl implements VanillaSoftwareProcess { @@ -35,6 +38,14 @@ protected void connectSensors() { } else { // See SoftwareProcessImpl.waitForEntityStart(). We will already have waited for driver.isRunning. // We will not poll for that again. + // + // Also disable the associated enricher - otherwise that would reset the not-up-indicator + // if serviceUp=false temporarily (e.g. if restart effector is called). + // See https://issues.apache.org/jira/browse/BROOKLYN-547 + Maybe enricher = EntityAdjuncts.tryFindWithUniqueTag(enrichers(), "service-process-is-running-updating-not-up"); + if (enricher.isPresent()) { + enrichers().remove(enricher.get()); + } ServiceNotUpLogic.clearNotUpIndicator(this, SERVICE_PROCESS_IS_RUNNING); } } diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessTest.java index fa8695f309..3c78b35f56 100644 --- a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessTest.java +++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessTest.java @@ -19,6 +19,8 @@ package org.apache.brooklyn.entity.software.base; import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecContains; +import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecHasAtLeastOnce; +import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecHasNever; import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecHasOnlyOnce; import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecSatisfies; import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecsContain; @@ -26,25 +28,38 @@ import static org.apache.brooklyn.util.core.internal.ssh.ExecCmdAsserts.assertExecsSatisfy; import java.util.Map; +import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.api.location.LocationSpec; import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.api.sensor.EnricherSpec; +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.sensor.Sensors; +import org.apache.brooklyn.core.sensor.function.FunctionSensor; import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.enricher.stock.UpdatingMap; import org.apache.brooklyn.entity.software.base.SoftwareProcess.ChildStartableMode; import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation; import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool; import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.CustomResponse; +import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.CustomResponseGenerator; import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.ExecCmdPredicates; import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.ExecParams; +import org.apache.brooklyn.util.time.Duration; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import com.google.common.base.Functions; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -294,4 +309,63 @@ public void testCmdNotInheritedFromParentOfSoftwareProcess() throws Exception { assertExecHasOnlyOnce(RecordingSshTool.getExecCmds(), "preInstallCommand"); assertExecHasOnlyOnce(RecordingSshTool.getExecCmds(), "launchCommand"); } + + @Test + public void testUseSshMonitoringDisabled() throws Exception { + // Setup a custom health-check that returns true after launch is called, + // and false after stop is called. + final AtomicBoolean customHealth = new AtomicBoolean(false); + + RecordingSshTool.setCustomResponse(".*launchCommand.*", new CustomResponseGenerator() { + @Override public CustomResponse generate(ExecParams execParams) throws Exception { + customHealth.set(true); + return new CustomResponse(0, "", ""); + }}); + RecordingSshTool.setCustomResponse(".*stopCommand.*", new CustomResponseGenerator() { + @Override public CustomResponse generate(ExecParams execParams) throws Exception { + customHealth.set(false); + return new CustomResponse(0, "", ""); + }}); + + // The entity polls for the custom-health; it populates the service-up-indicators using that. + VanillaSoftwareProcess entity = app.createAndManageChild(EntitySpec.create(VanillaSoftwareProcess.class) + .configure(VanillaSoftwareProcess.USE_SSH_MONITORING, false) + .configure(VanillaSoftwareProcess.SERVICE_PROCESS_IS_RUNNING_POLL_PERIOD, Duration.ONE_MILLISECOND) + .configure(VanillaSoftwareProcess.LAUNCH_COMMAND, "launchCommand") + .configure(VanillaSoftwareProcess.CHECK_RUNNING_COMMAND, "checkRunningCommand") + .configure(VanillaSoftwareProcess.STOP_COMMAND, "stopCommand") + .addInitializer(new FunctionSensor(ConfigBag.newInstance() + .configure(FunctionSensor.SENSOR_NAME, "myCustomHealth") + .configure(FunctionSensor.SUPPRESS_DUPLICATES, true) + .configure(FunctionSensor.SENSOR_PERIOD, Duration.ONE_MILLISECOND) + .configure(FunctionSensor.FUNCTION, new Callable() { + public Boolean call() { + return customHealth.get(); + }}) + )) + .enricher(EnricherSpec.create(UpdatingMap.class) + .configure(UpdatingMap.SOURCE_SENSOR, Sensors.newBooleanSensor("myCustomHealth")) + .configure(UpdatingMap.TARGET_SENSOR, Attributes.SERVICE_NOT_UP_INDICATORS) + .configure(UpdatingMap.COMPUTING, Functions.forMap( + MutableMap.of(true, null, false, "custom-health-is-false"), + "custom-health-unknown"))) + ); + + // Start the entity + app.start(ImmutableList.of(loc)); + EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true); + + // Expect to have ssh "checkRunningCommand" called once during startup, and not again + assertExecHasAtLeastOnce(RecordingSshTool.getExecCmds(), "checkRunningCommand"); + + RecordingSshTool.clearCmdHistory(); + Asserts.succeedsContinually(ImmutableMap.of("timeout", Duration.millis(100)), new Runnable() { + public void run() { + assertExecHasNever(RecordingSshTool.getExecCmds(), "checkRunningCommand"); + }}); + + // Restart (see https://issues.apache.org/jira/browse/BROOKLYN-547) + entity.restart(); + EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true); + } }