-
Notifications
You must be signed in to change notification settings - Fork 374
AN-736 Add support for container
attribute in WDL 1.1
#7809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
9646039
c6bb830
1cd1d94
c7adf3d
4819d35
3abf512
7b4e86c
5501acc
359f062
46904f3
f5e8268
f30ede3
392fb84
6a1df7c
79cf0db
1f65b17
7de7b8f
df24bd4
c86938d
51312c2
ab99f09
0bebe0b
c36116b
000cff2
2ae7bfc
e233231
a6f6a59
335ee10
2871e45
5db3c3c
5ebc93a
ca0315d
6953da4
b2c2ac4
e9c62c0
b5d2db8
eea51d2
5777357
603d3d1
9a5d294
e4d0b62
c3beb19
e6f0deb
7866c36
58a9154
d32d53e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package cromwell.backend.validation | ||
|
||
import cats.implicits.catsSyntaxValidatedId | ||
import common.validation.ErrorOr.ErrorOr | ||
import wdl.draft2.model.LocallyQualifiedName | ||
import wom.RuntimeAttributesKeys | ||
import wom.types.{WomStringType, WomType} | ||
import wom.values.{WomArray, WomString, WomValue} | ||
|
||
// Wrapper type for the list of containers that can be provided as 'docker' or 'container' runtime attributes. | ||
// In WDL, this value can be either a single string or an array of strings, but in the backend we always | ||
// want to deal with it as a list of strings. | ||
// | ||
// Previous to WDL 1.1, only 'docker' was supported. From WDL 1.1 onwards, they are aliases of each other, with | ||
// `container` being preferred and `docker` deprecated. Only one of they two may be provided in runtime attrs. | ||
// Note that we strip `container` out of pre-1.1 WDL files during parsing, so at this stage we only see `docker` | ||
// in those cases. | ||
case class Containers(values: List[String]) { | ||
override def toString: String = values.mkString(", ") | ||
} | ||
|
||
object Containers { | ||
val validWdlTypes: Set[wom.types.WomType] = | ||
Set(wom.types.WomStringType, wom.types.WomArrayType(wom.types.WomStringType)) | ||
|
||
val runtimeAttrKeys = List(RuntimeAttributesKeys.ContainerKey, RuntimeAttributesKeys.DockerKey) | ||
|
||
def apply(value: String): Containers = Containers(List(value)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Elegant |
||
|
||
def extractContainer(validatedRuntimeAttributes: ValidatedRuntimeAttributes): String = | ||
extractContainerOption(validatedRuntimeAttributes).getOrElse { | ||
throw new RuntimeException("No container image found in either 'container' or 'docker' runtime attributes.") | ||
} | ||
|
||
def extractContainerOption(validatedRuntimeAttributes: ValidatedRuntimeAttributes): Option[String] = { | ||
val dockerContainer = RuntimeAttributesValidation | ||
.extractOption[Containers](RuntimeAttributesKeys.DockerKey, validatedRuntimeAttributes) | ||
.flatMap(_.values.headOption) | ||
val containerContainer = RuntimeAttributesValidation | ||
.extractOption[Containers](RuntimeAttributesKeys.ContainerKey, validatedRuntimeAttributes) | ||
.flatMap(_.values.headOption) | ||
|
||
containerContainer.orElse(dockerContainer) | ||
} | ||
|
||
def extractContainerFromPreValidationAttrs(attributes: Map[LocallyQualifiedName, WomValue]): Option[String] = { | ||
val containerContainer = attributes.get(RuntimeAttributesKeys.ContainerKey) match { | ||
case Some(WomArray(_, values)) => | ||
values.headOption.map(_.valueString) | ||
case Some(WomString(value)) => Some(value) | ||
case _ => None | ||
} | ||
|
||
val dockerContainer = attributes.get(RuntimeAttributesKeys.DockerKey) match { | ||
case Some(WomArray(_, values)) => | ||
values.headOption.map(_.valueString) | ||
case Some(WomString(value)) => Some(value) | ||
case _ => None | ||
} | ||
|
||
// TODO enhance to select the best container from the list if multiple are provided. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to link the relevant story here, I found its number: AN-734 |
||
// Currently we always choose the first, should prefer one that matches our platform. | ||
// https://broadworkbench.atlassian.net/browse/AN-734 | ||
|
||
containerContainer.orElse(dockerContainer) | ||
} | ||
} | ||
|
||
/** | ||
* Trait to handle validation of both 'docker' and 'container' runtime attributes, which are mutually exclusive | ||
* ways of specifying the container image to use for a task. | ||
*/ | ||
trait ContainersValidation extends OptionalRuntimeAttributesValidation[Containers] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be optional because |
||
override def coercion: Set[WomType] = Containers.validWdlTypes | ||
|
||
override def usedInCallCaching: Boolean = true | ||
|
||
override protected def missingValueMessage: String = s"Can't find an attribute value for key ${key}" | ||
|
||
override protected def invalidValueMessage(value: WomValue): String = super.missingValueMessage | ||
|
||
override protected def validateOption: PartialFunction[WomValue, ErrorOr[Containers]] = { | ||
case WomString(value) => value.validNel.map(v => Containers(v)) | ||
case WomArray(womType, values) if womType.memberType == WomStringType => | ||
Containers(values.map(_.valueString).toList).validNel | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,23 @@ | ||
package cromwell.backend.validation | ||
|
||
import cats.syntax.validated._ | ||
import common.validation.ErrorOr.ErrorOr | ||
import wom.RuntimeAttributesKeys | ||
import wom.values._ | ||
|
||
/** | ||
* Validates the "docker" runtime attribute as a String, returning it as `String`. | ||
* | ||
* `instance` returns an validation that errors when no attribute is specified. | ||
* | ||
* There is no default, however `optional` can be used return the validated value as an `Option`, wrapped in a `Some`, | ||
* if present, or `None` if not found. | ||
* Different WDL versions support different names for the runtime attribute that specifies the container image to use. | ||
* WDL 1.0 supports only `docker`, WDL 1.1 and later support `container` (preferred) and `docker` (deprecated). | ||
*/ | ||
object DockerValidation { | ||
lazy val instance: RuntimeAttributesValidation[String] = new DockerValidation | ||
lazy val optional: OptionalRuntimeAttributesValidation[String] = instance.optional | ||
lazy val instance: OptionalRuntimeAttributesValidation[Containers] = new DockerValidation | ||
} | ||
|
||
class DockerValidation extends StringRuntimeAttributesValidation(RuntimeAttributesKeys.DockerKey) { | ||
override def usedInCallCaching: Boolean = true | ||
|
||
override protected def missingValueMessage: String = "Can't find an attribute value for key docker" | ||
class DockerValidation extends ContainersValidation { | ||
override val key: String = RuntimeAttributesKeys.DockerKey | ||
} | ||
|
||
override protected def invalidValueMessage(value: WomValue): String = super.missingValueMessage | ||
object ContainerValidation { | ||
lazy val instance: OptionalRuntimeAttributesValidation[Containers] = new ContainerValidation | ||
} | ||
|
||
// NOTE: Docker's current test specs don't like WdlInteger, etc. auto converted to WdlString. | ||
override protected def validateValue: PartialFunction[WomValue, ErrorOr[String]] = { case WomString(value) => | ||
value.validNel | ||
} | ||
class ContainerValidation extends ContainersValidation { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make these not the same name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, I did actually intend this naming - note container vs containers |
||
override val key: String = RuntimeAttributesKeys.ContainerKey | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
name: container_attr_wdl10 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a reminder, the search for |
||
testFormat: workflowsuccess | ||
|
||
files { | ||
workflow: wdl/container_attr_wdl10.wdl | ||
} | ||
|
||
metadata { | ||
"calls.container_attr_wdl10.dockerOnly.dockerImageUsed": "ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9", | ||
"calls.container_attr_wdl10.dockerAndContainer.dockerImageUsed": "ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
name: container_attr_wdl11 | ||
testFormat: workflowsuccess | ||
|
||
files { | ||
workflow: wdl/container_attr_wdl11.wdl | ||
} | ||
|
||
metadata { | ||
"calls.container_attr_wdl11.dockerSingle.dockerImageUsed": "ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9", | ||
"calls.container_attr_wdl11.containerSingle.dockerImageUsed": "debian@sha256:9f67f90b1574ea7263a16eb64756897d3fa42a8e43cce61065b8a1f0f9367526", | ||
"calls.container_attr_wdl11.dockerList.dockerImageUsed": "ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9", | ||
"calls.container_attr_wdl11.containerList.dockerImageUsed": "debian@sha256:9f67f90b1574ea7263a16eb64756897d3fa42a8e43cce61065b8a1f0f9367526", | ||
"calls.container_attr_wdl11.dockerAndContainer.dockerImageUsed": "debian@sha256:9f67f90b1574ea7263a16eb64756897d3fa42a8e43cce61065b8a1f0f9367526", | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
name: container_attr_wdldraft2 | ||
testFormat: workflowsuccess | ||
|
||
files { | ||
workflow: wdl/container_attr_wdldraft2.wdl | ||
} | ||
|
||
metadata { | ||
"calls.container_attr_wdldraft2.dockerOnly.dockerImageUsed": "ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9", | ||
"calls.container_attr_wdldraft2.dockerAndContainer.dockerImageUsed": "ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
version 1.0 | ||
|
||
task dockerOnly { | ||
command <<< | ||
echo "Run with WDL 1.0 on ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9" | ||
>>> | ||
output { | ||
File out = stdout() | ||
} | ||
runtime { | ||
docker: "ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9" | ||
} | ||
} | ||
|
||
task dockerAndContainer { | ||
command <<< | ||
echo "Run with WDL 1.0 on ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9" | ||
>>> | ||
output { | ||
File out = stdout() | ||
} | ||
runtime { | ||
docker: "ubuntu@sha256:d0afa9fbcf16134b776fbba4a04c31d476eece2d080c66c887fdd2608e4219a9" | ||
container: "debian@sha256:9f67f90b1574ea7263a16eb64756897d3fa42a8e43cce61065b8a1f0f9367526" | ||
} | ||
} | ||
|
||
workflow container_attr_wdl10 { | ||
call dockerOnly | ||
call dockerAndContainer | ||
|
||
output { | ||
String out1 = read_string(dockerOnly.out) | ||
String out2 = read_string(dockerAndContainer.out) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
YES