This is an automated email from the ASF dual-hosted git repository. heneveld pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git
commit 921d09e4202dbc3dee8d2e81d3706c475e75a27c Author: Duncan Grant <[email protected]> AuthorDate: Fri Jun 30 11:44:20 2023 +0100 Allow on fire in workflow expressions When calling attributeWhenReady using workflow syntax we should not abort when the entity is on fire. This is different from what we do using DSL expressions. --- .../camp/brooklyn/WorkflowExpressionsYamlTest.java | 60 ++++++++++++++++++++-- .../core/sensor/DependentConfiguration.java | 16 +++++- .../brooklyn/util/core/text/TemplateProcessor.java | 2 +- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowExpressionsYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowExpressionsYamlTest.java index d0fb7d02e2..3036d535b1 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowExpressionsYamlTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowExpressionsYamlTest.java @@ -22,23 +22,21 @@ import com.google.common.collect.Iterables; import com.google.common.reflect.TypeToken; import org.apache.brooklyn.api.effector.Effector; import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.entity.Attributes; import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils; import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.core.workflow.WorkflowBasicTest; import org.apache.brooklyn.core.workflow.WorkflowExecutionContext; import org.apache.brooklyn.core.workflow.steps.flow.LogWorkflowStep; -import org.apache.brooklyn.entity.stock.BasicApplication; import org.apache.brooklyn.entity.stock.BasicEntity; import org.apache.brooklyn.entity.stock.BasicEntityImpl; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.test.ClassLogWatcher; import org.apache.brooklyn.util.core.flags.TypeCoercions; -import org.apache.brooklyn.util.core.internal.TypeCoercionsTest; import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.guava.Maybe; @@ -50,6 +48,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; import java.util.function.Function; public class WorkflowExpressionsYamlTest extends AbstractYamlTest { @@ -82,6 +81,18 @@ public class WorkflowExpressionsYamlTest extends AbstractYamlTest { } } + private Entity createEntity() throws Exception { + Entity app = createAndStartApplication( + "services:", + "- type: " + BasicEntity.class.getName()); + waitForApplicationTasks(app); + + // Deploy the blueprint. + Entity entity = lastEntity = Iterables.getOnlyElement(app.getChildren()); + synchronized (this) { this.notifyAll(); } + return entity; + } + private Entity createEntityWithWorkflowEffector(String ...stepLines) throws Exception { // Declare workflow in a blueprint, add various log steps. Entity app = createAndStartApplication( @@ -214,6 +225,47 @@ public class WorkflowExpressionsYamlTest extends AbstractYamlTest { } } + @Test + public void testWorkflowExpressionAllowsOnFire() throws Exception { + Entity entity = createEntity(); + WorkflowExecutionContext workflow = invocationWorkflowOnLastEntity( + " - step: transform ${entity.attributeWhenReady.foo} | wait | set foo_in_workflow", + " timeout: 4s", + " - step: set-sensor new-foo = ${foo_in_workflow}" + ); + + Time.sleep(Duration.ONE_SECOND); + Asserts.assertFalse(workflow.getTask(false).get().isDone()); + + waitForLastEntity().sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + Time.sleep(Duration.of(2, TimeUnit.SECONDS)); + waitForLastEntity().sensors().set(Sensors.newIntegerSensor("foo"), 10); + + workflow.getTask(false).get().blockUntilEnded(Duration.TEN_SECONDS); + Asserts.assertFalse(workflow.getTask(true).get().isError()); + Integer fooInWorkflow = entity.sensors().get(Sensors.newIntegerSensor("new-foo")); + Asserts.assertEquals((Integer) 10, fooInWorkflow); + } + + @Test + public void testDSLExpressionAbortWhenOnFire() throws Exception { + createEntity(); + WorkflowExecutionContext workflow = invocationWorkflowOnLastEntity( + " - step: transform $brooklyn:attributeWhenReady(\"foo\") | wait | set foo_in_workflow", + " timeout: 3s" + ); + + Time.sleep(Duration.ONE_SECOND); + Asserts.assertFalse(workflow.getTask(false).get().isDone()); + + waitForLastEntity().sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + Time.sleep(Duration.of(2, TimeUnit.SECONDS)); + waitForLastEntity().sensors().set(Sensors.newIntegerSensor("foo"), 10); + + workflow.getTask(false).get().blockUntilEnded(Duration.TEN_SECONDS); + Asserts.assertTrue(workflow.getTask(true).get().isError()); + } + @Test public void testWorkflowExpressionMixingBrooklynDslAndExpressions() throws Exception { createEntityWithWorkflowEffector("- s: let x = $brooklyn:self()", " output: ${x}"); diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java b/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java index 731a0eff25..351677bcb0 100644 --- a/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java +++ b/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java @@ -121,7 +121,11 @@ public class DependentConfiguration { public static <T> Task<T> attributeWhenReady(Entity source, AttributeSensor<T> sensor) { return attributeWhenReady(source, sensor, JavaGroovyEquivalents.groovyTruthPredicate()); } - + + public static <T> Task<T> attributeWhenReadyAllowingOnFire(Entity source, AttributeSensor<T> sensor) { + return attributeWhenReadyAllowingOnFire(source, sensor, JavaGroovyEquivalents.groovyTruthPredicate()); + } + /** * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted. */ @@ -141,6 +145,16 @@ public class DependentConfiguration { } + /** returns an unsubmitted {@link Task} which blocks until the given sensor on the given source entity gives a value that satisfies ready, then returns that value; + * particular useful in Entity configuration where config will block until Tasks have a value + */ + public static <T> Task<T> attributeWhenReadyAllowingOnFire(final Entity source, final AttributeSensor<T> sensor, final Predicate<? super T> ready) { + Builder<T, T> builder = builder().attributeWhenReadyAllowingOnFire(source, sensor).timeoutIfStoppingOrDestroyed(Duration.ONE_MINUTE); + if (ready != null) builder.readiness(ready); + return builder.build(); + + } + /** * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted. */ diff --git a/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java b/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java index 815a9642c7..771fe150fb 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java @@ -626,7 +626,7 @@ public class TemplateProcessor { try { result = mode == SensorResolutionMode.ATTRIBUTE_WHEN_READY ? - ((EntityInternal)entity).getExecutionContext().get( DependentConfiguration.attributeWhenReady(entity, + ((EntityInternal)entity).getExecutionContext().get( DependentConfiguration.attributeWhenReadyAllowingOnFire(entity, Sensors.builder(Object.class, key).persistence(AttributeSensor.SensorPersistenceMode.NONE).build())) : mode == SensorResolutionMode.ATTRIBUTE_VALUE ? entity.sensors().get( Sensors.newSensor(Object.class, key) )
