allow workflow to have no steps if it has output
diff --git a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExecutionContext.java b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExecutionContext.java
index 0abb57c..deef3d0 100644
--- a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExecutionContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExecutionContext.java
@@ -917,7 +917,7 @@
@JsonIgnore
List<WorkflowStepDefinition> getStepsResolved() {
if (stepsResolved ==null) {
- stepsResolved = MutableList.copyOf(WorkflowStepResolution.resolveSteps(getManagementContext(), WorkflowExecutionContext.this.stepsDefinition));
+ stepsResolved = MutableList.copyOf(WorkflowStepResolution.resolveSteps(getManagementContext(), WorkflowExecutionContext.this.stepsDefinition, outputDefinition));
}
return stepsResolved;
}
diff --git a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowStepResolution.java b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowStepResolution.java
index 515153a..102843d 100644
--- a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowStepResolution.java
+++ b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowStepResolution.java
@@ -45,8 +45,15 @@
public class WorkflowStepResolution {
public static List<WorkflowStepDefinition> resolveSteps(ManagementContext mgmt, List<Object> steps) {
+ return resolveSteps(mgmt, steps, null);
+ }
+ public static List<WorkflowStepDefinition> resolveSteps(ManagementContext mgmt, List<Object> steps, Object outputDefinition) {
List<WorkflowStepDefinition> result = MutableList.of();
- if (steps==null || steps.isEmpty()) throw new IllegalStateException("No steps defined in workflow");
+ if (steps==null || steps.isEmpty()) {
+ if (outputDefinition==null) throw new IllegalStateException("No steps defined in workflow and no output set");
+ // if there is output, an empty workflow makes sense
+ return result;
+ }
for (int i=0; i<steps.size(); i++) {
try {
result.add(resolveStep(mgmt, steps.get(i)));
@@ -172,7 +179,7 @@
if (!hasCondition && entityOrAdjunctWhereRunningIfKnown!=null) {
// ideally try to resolve the steps at entity init time; except if a condition is required we skip that so you can have steps that only resolve late,
// and if entity isn't available then we don't need that either
- WorkflowStepResolution.resolveSteps( ((BrooklynObjectInternal)entityOrAdjunctWhereRunningIfKnown).getManagementContext(), steps);
+ WorkflowStepResolution.resolveSteps( ((BrooklynObjectInternal)entityOrAdjunctWhereRunningIfKnown).getManagementContext(), steps, params.containsKey(WorkflowCommonConfig.OUTPUT.getName()) ? "has_output" : null);
}
}
diff --git a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java
index d1925d2..e56d38e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java
+++ b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java
@@ -58,6 +58,7 @@
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -163,8 +164,10 @@
public void validateStep(@Nullable ManagementContext mgmt, @Nullable WorkflowExecutionContext workflow) {
super.validateStep(mgmt, workflow);
- if (steps instanceof List) WorkflowStepResolution.resolveSteps(mgmt, (List<Object>) steps);
- else if (steps!=null) throw new IllegalArgumentException("Workflow `steps` must be a list");
+ if (steps instanceof List) {
+ if (((List)steps).isEmpty()) throw new IllegalArgumentException("Workflow `steps` must be supplied for a custom or nested workflow");
+ WorkflowStepResolution.resolveSteps(mgmt, (List<Object>) steps, null);
+ } else if (steps!=null) throw new IllegalArgumentException("Workflow `steps` must be a list");
else if (target!=null) throw new IllegalArgumentException("Workflow cannot take a `target` without `steps`");
}
@@ -561,7 +564,10 @@
return newWorkflowExecution(entity, name, extraConfig, null);
}
public WorkflowExecutionContext newWorkflowExecution(Entity entity, String name, ConfigBag extraConfig, Map extraTaskFlags) {
- if (steps==null) throw new IllegalArgumentException("Cannot make new workflow with no steps");
+ if (steps==null) {
+ if (target!=null) throw new IllegalArgumentException("Steps are required for a workflow with a target");
+ else steps = MutableList.of();
+ }
if (target==null) {
// copy everything as we are going to run it "flat"
@@ -580,12 +586,15 @@
}
}
- private ConfigBag getConfigForSubWorkflow(boolean includeInput) {
+ private ConfigBag getConfigForSubWorkflow(boolean isFlattened) {
+ if (isFlattened && (output!=null && workflowOutput!=null)) {
+ if (!Objects.equals(output, workflowOutput)) throw new IllegalArgumentException("Setting both 'output' and 'workflowOutput' is not supported for custom steps without target");
+ }
ConfigBag result = ConfigBag.newInstance()
.configure(WorkflowCommonConfig.PARAMETER_DEFS, parameters)
.configure(WorkflowCommonConfig.STEPS, steps)
- .configure(WorkflowCommonConfig.INPUT, includeInput ? input : null) // input is resolved in outer workflow so it can reference outer workflow vars
- .configure(WorkflowCommonConfig.OUTPUT, workflowOutput)
+ .configure(WorkflowCommonConfig.INPUT, isFlattened ? input : null) // input is resolved in outer workflow so it can reference outer workflow vars
+ .configure(WorkflowCommonConfig.OUTPUT, isFlattened && workflowOutput==null ? output : workflowOutput)
.configure(WorkflowCommonConfig.RETENTION, retention)
.configure(WorkflowCommonConfig.REPLAYABLE, replayable)
.configure(WorkflowCommonConfig.IDEMPOTENT, idempotent)
diff --git a/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowBasicTest.java b/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowBasicTest.java
index 8d0ce20..9287cfb 100644
--- a/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowBasicTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowBasicTest.java
@@ -216,7 +216,7 @@
"log test message"
);
- List<WorkflowStepDefinition> steps = WorkflowStepResolution.resolveSteps(mgmt, stepsDefinition);
+ List<WorkflowStepDefinition> steps = WorkflowStepResolution.resolveSteps(mgmt, stepsDefinition, null);
Asserts.assertSize(steps, 4);
}
@@ -229,7 +229,7 @@
Asserts.assertInstanceOf(wf, CustomWorkflowStep.class);
Asserts.assertSize(((CustomWorkflowStep) wf).peekSteps(), 1);
Asserts.assertInstanceOf(
- WorkflowStepResolution.resolveSteps( mgmt, ((CustomWorkflowStep) wf).peekSteps() ).get(0), LogWorkflowStep.class);
+ WorkflowStepResolution.resolveSteps( mgmt, ((CustomWorkflowStep) wf).peekSteps(), null ).get(0), LogWorkflowStep.class);
};
test.accept( BeanWithTypeUtils.convert(mgmt,
@@ -480,5 +480,17 @@
w1.getTask(false).get().getUnchecked(),
MutableList.of("a=b", "b=c"));
}
+
+ @Test
+ public void testOutputOnlyWorkflow() {
+ loadTypes();
+ BasicApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(BasicApplication.class));
+ WorkflowExecutionContext w1 = WorkflowBasicTest.runWorkflow(app, Strings.lines(
+ "output: 42"
+ ), null);
+ Asserts.assertEquals(
+ w1.getTask(false).get().getUnchecked(),
+ 42);
+ }
}