diff --git a/centaur/src/main/resources/standardTestCases/failures/nested_struct_bad_instantiation/nested_struct_bad_instantiation.inputs b/centaur/src/main/resources/standardTestCases/failures/nested_struct_bad_instantiation/nested_struct_bad_instantiation.inputs new file mode 100644 index 00000000000..6f8cb64990a --- /dev/null +++ b/centaur/src/main/resources/standardTestCases/failures/nested_struct_bad_instantiation/nested_struct_bad_instantiation.inputs @@ -0,0 +1,4 @@ +{ + "MinimalStructExample.test": "Hello World", + "MinimalStructExample.integer": 2 +} diff --git a/centaur/src/main/resources/standardTestCases/failures/nested_struct_bad_instantiation/nested_struct_bad_instantiation.wdl b/centaur/src/main/resources/standardTestCases/failures/nested_struct_bad_instantiation/nested_struct_bad_instantiation.wdl new file mode 100644 index 00000000000..dce55da0bae --- /dev/null +++ b/centaur/src/main/resources/standardTestCases/failures/nested_struct_bad_instantiation/nested_struct_bad_instantiation.wdl @@ -0,0 +1,27 @@ +version 1.0 + + +struct firstLayer { + String first + Int number +} + +struct secondLayer { + String second + firstLayer lowerLayer +} + + +workflow MinimalStructExample { + input { + String test + Int integer + } + + firstLayer example1 = {"first": test, "number": integer} + secondLayer example2 = {"second": test, "lowerLayer": example1} + + output { + String example3 = example2.second + } +} diff --git a/centaur/src/main/resources/standardTestCases/nested_struct_bad_instantiation.test b/centaur/src/main/resources/standardTestCases/nested_struct_bad_instantiation.test new file mode 100644 index 00000000000..a2608a0262c --- /dev/null +++ b/centaur/src/main/resources/standardTestCases/nested_struct_bad_instantiation.test @@ -0,0 +1,14 @@ +name: nested_struct_bad_instantiation +testFormat: workflowfailure + +files { + workflow: failures/nested_struct_bad_instantiation/nested_struct_bad_instantiation.wdl + inputs: failures/nested_struct_bad_instantiation/nested_struct_bad_instantiation.inputs +} + +metadata { + workflowName: MinimalStructExample + status: Failed + "failures.0.message": "Workflow failed" + "failures.0.causedBy.0.message": "Failed to evaluate 'example2' (reason 1 of 1): Evaluating { \"second\": test, \"lowerLayer\": example1 } failed: Cannot construct WomMapType(WomStringType,WomAnyType) with mixed types in map values: [WomString(Hello World), WomObject(Map(first -> WomString(Hello World), number -> WomInteger(2)),WomCompositeType(Map(first -> WomStringType, number -> WomIntegerType),Some(firstLayer)))]" +} diff --git a/engine/src/main/scala/cromwell/engine/workflow/lifecycle/execution/keys/ExpressionKey.scala b/engine/src/main/scala/cromwell/engine/workflow/lifecycle/execution/keys/ExpressionKey.scala index 5fd8e36cac5..51183af9f04 100644 --- a/engine/src/main/scala/cromwell/engine/workflow/lifecycle/execution/keys/ExpressionKey.scala +++ b/engine/src/main/scala/cromwell/engine/workflow/lifecycle/execution/keys/ExpressionKey.scala @@ -6,7 +6,7 @@ import common.validation.ErrorOr._ import common.validation.Validation._ import cromwell.core.ExecutionIndex._ import cromwell.core.{ExecutionStatus, JobKey} -import cromwell.engine.workflow.lifecycle.execution.WorkflowExecutionDiff +import cromwell.engine.workflow.lifecycle.execution.{WdlRuntimeException, WorkflowExecutionDiff} import cromwell.engine.workflow.lifecycle.execution.keys.ExpressionKey.{ ExpressionEvaluationFailedResponse, ExpressionEvaluationSucceededResponse @@ -32,7 +32,7 @@ final case class ExpressionKey(node: ExpressionNodeLike, index: ExecutionIndex) .contextualizeErrors(s"evaluate '${node.fullyQualifiedName}'") match { case Right(result) => workflowExecutionActor ! ExpressionEvaluationSucceededResponse(this, result) case Left(f) => - workflowExecutionActor ! ExpressionEvaluationFailedResponse(this, new RuntimeException(f.toList.mkString(", "))) + workflowExecutionActor ! ExpressionEvaluationFailedResponse(this, WdlRuntimeException(f.toList.mkString(", "))) } WorkflowExecutionDiff(Map(this -> ExecutionStatus.Running)).validNel diff --git a/engine/src/main/scala/cromwell/engine/workflow/lifecycle/execution/package.scala b/engine/src/main/scala/cromwell/engine/workflow/lifecycle/execution/package.scala index 52fbe868f1e..e1695e1d355 100644 --- a/engine/src/main/scala/cromwell/engine/workflow/lifecycle/execution/package.scala +++ b/engine/src/main/scala/cromwell/engine/workflow/lifecycle/execution/package.scala @@ -5,6 +5,16 @@ package execution { import cromwell.core.CallKey import wom.values.WomEvaluatedCallInputs + import scala.util.control.NoStackTrace + final case class JobRunning(key: CallKey, inputs: WomEvaluatedCallInputs) final case class JobStarting(callKey: CallKey) + + /** + * An exception specific to conditions inside the executing WDL, as opposed to one that is "Cromwell's fault" + * @param message Description suitable for user display + */ + final case class WdlRuntimeException(message: String) extends RuntimeException with NoStackTrace { + override def getMessage: String = message + } } diff --git a/server/src/test/scala/cromwell/SimpleWorkflowActorSpec.scala b/server/src/test/scala/cromwell/SimpleWorkflowActorSpec.scala index 4e67ac984a1..266e56060d9 100644 --- a/server/src/test/scala/cromwell/SimpleWorkflowActorSpec.scala +++ b/server/src/test/scala/cromwell/SimpleWorkflowActorSpec.scala @@ -192,7 +192,7 @@ class SimpleWorkflowActorSpec extends CromwellTestKitWordSpec with BeforeAndAfte workflowActor ! StartWorkflowCommand } Await.result(promise.future, TestExecutionTimeout) - probe.expectTerminated(workflowActor, 2.seconds) + probe.expectTerminated(workflowActor, 15.seconds) supervisor.expectMsgPF(AwaitAlmostNothing, "parent should get a failed response") { case x: WorkflowFailedResponse => x.workflowId should be(workflowId) diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/values/LiteralEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/values/LiteralEvaluators.scala index 4831c34e76b..e981b103688 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/values/LiteralEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/values/LiteralEvaluators.scala @@ -4,7 +4,9 @@ import cats.syntax.apply._ import cats.syntax.traverse._ import cats.syntax.validated._ import cats.instances.list._ +import common.validation.ErrorOr import common.validation.ErrorOr.ErrorOr +import common.validation.ErrorOr.NestedErrorOr import wdl.model.draft3.elements.ExpressionElement import wdl.model.draft3.elements.ExpressionElement._ import wdl.model.draft3.graph.expression.{EvaluatedValue, ForCommandInstantiationOptions, ValueEvaluator} @@ -84,11 +86,11 @@ object LiteralEvaluators { ) mapN { (key, value) => key -> value } } - evaluated map { kvps => + (evaluated map { kvps => val value = kvps.map(entry => entry._1.value -> entry._2.value).toMap val sideEffectFiles = kvps.flatMap(entry => entry._1.sideEffectFiles ++ entry._2.sideEffectFiles) - EvaluatedValue(WomMap(value.toMap), sideEffectFiles) - } + ErrorOr(EvaluatedValue(WomMap.apply(value.toMap), sideEffectFiles)) + }).flatten } } @@ -103,11 +105,11 @@ object LiteralEvaluators { entry.evaluateValue(inputs, ioFunctionSet, forCommandInstantiationOptions) } - evaluated map { evaluateds => + (evaluated map { evaluateds => val value = evaluateds.map(_.value) val sideEffectFiles = evaluateds.flatMap(_.sideEffectFiles) - EvaluatedValue(WomArray(value), sideEffectFiles) - } + ErrorOr(EvaluatedValue(WomArray.apply(value), sideEffectFiles)) + }).flatten } }