Permalink
Browse files

Release 23 (#1735)

  • Loading branch information...
1 parent 0099816 commit f7f068cf59a928471330ae5f37f8bb4401872cb3 @geoffjentry geoffjentry committed on GitHub Dec 3, 2016
Showing 334 changed files with 8,400 additions and 4,793 deletions.
View
@@ -6,14 +6,18 @@ scala:
jdk:
- oraclejdk8
env:
- # Setting this variable twice will cause the 'script' section to run twice with the respective env var invoked
- - BUILD_TYPE=sbt
- - BUILD_TYPE=centaurJes
- - BUILD_TYPE=centaurLocal
+ global:
+ - CENTAUR_BRANCH=develop
+ matrix:
+ # Setting this variable twice will cause the 'script' section to run twice with the respective env var invoked
+ - BUILD_TYPE=sbt
+ - BUILD_TYPE=checkPublish
+ - BUILD_TYPE=centaurJes
+ - BUILD_TYPE=centaurLocal
script:
- src/bin/travis/test.sh
after_success:
- - src/bin/travis/publishSnapshot.sh
+ - src/bin/travis/afterSuccess.sh
deploy:
provider: script
script: src/bin/travis/publishRelease.sh
View
@@ -1,5 +1,124 @@
# Cromwell Change Log
+## 23
+
+* The `meta` and `parameter_meta` blocks are now valid within `workflow` blocks, not just `task`
+* The JES backend configuration now has an option `genomics-api-queries-per-100-seconds` to help tune the rate of batch polling against the JES servers. Users with quotas larger than default should make sure to set this value.
+* Added an option `call-caching.invalidate-bad-cache-results` (default: `true`). If true, Cromwell will invalidate cached results which have failed to copy as part of a cache hit.
+* Timing diagrams and metadata now receive more fine grained workflow states between submission and Running.
+* Support for the Pair WDL type (e.g. `Pair[Int, File] floo = (3, "gs://blar/blaz/qlux.txt")`)
+* Added support for new WDL functions:
+ * `zip: (Array[X], Array[Y]) => Array[Pair[X, Y]]` - align items in the two arrays by index and return them as WDL pairs
+ * `cross: (Array[X], Array[Y]) => Array[Pair[X, Y]]` - create every possible pair from the two input arrays and return them all as WDL pairs
+ * `transpose: (Array[Array[X]]) => Array[Array[X]]` compute the matrix transpose for a 2D array. Assumes each inner array has the same length.
+* By default, `system.abort-jobs-on-terminate` is false when running `java -jar cromwell.jar server`, and true when running `java -jar cromwell.jar run <wdl> <inputs>`.
+* Enable WDL imports when running in Single Workflow Runner Mode.
+* Support for sub workflows (see [Annex A](#annex-a---workflow-outputs))
+* Enable WDL imports when running in Single Workflow Runner Mode as well as Server Mode
+* Support for WDL imports through an additional imports.zip parameter
+* Support for sub workflows
+* Corrected file globbing in JES to correctly report all generated files. Additionally, file globbing in JES now uses bash-style glob syntax instead of python style glob syntax
+* Support declarations as graph nodes
+* Added the ability to override the default service account that the compute VM is started with via the configuration option `JES.config.genomics.compute-service-account` or through the workflow options parameter `google_compute_service_account`. More details can be found in the README.md
+* Fix bugs related to the behavior of Cromwell in Single Workflow Runner Mode. Cromwell will now exit once a workflow completes in Single Workflow Runner Mode. Additionally, when restarting Cromwell in Single Workflow Runner Mode, Cromwell will no longer restart incomplete workflows from a previous session.
+
+### Annex A - Workflow outputs
+
+The WDL specification has changed regarding [workflow outputs](https://github.com/broadinstitute/wdl/blob/develop/SPEC.md#outputs) to accommodate sub workflows.
+This change is backward compatible in terms of runnable WDLs (WDL files using the deprecated workflow outputs syntax will still run the same).
+The only visible change lies in the metadata (as well as the console output in single workflow mode, when workflow outputs are printed out at the end of a successful workflow).
+
+TL;DR Unless you are parsing or manipulating the "key" by which workflow outputs are referenced in the metadata (and/or the console output for single workflow mode), you can skip the following explanation.
+
+*Metadata Response*
+```
+{
+ ...
+ outputs {
+ "task_output_1": "hello",
+ "task_output_2": "world"
+ ^
+ If you don't manipulate this part of the metadata, then skip this section
+ }
+}
+```
+
+In order to maintain backward compatibility, workflow outputs expressed with the deprecated syntax are "expanded" to the new syntax. Here is an example:
+
+```
+task t {
+ command {
+ #do something
+ }
+ output {
+ String out1 = "hello"
+ String out2 = "world"
+ }
+}
+```
+
+```
+ workflow old_syntax {
+ call t
+ output {
+ t.*
+ }
+ }
+```
+
+```
+ workflow new_syntax {
+ call t
+ output {
+ String wf_out1 = t.out1
+ String wf_out2 = t.out2
+ }
+ }
+```
+
+The new syntax allows for type checking of the outputs as well as expressions. It also allows for explicitly naming to the outputs.
+The old syntax doesn't give the ability to name workflow outputs. For consistency reasons, Cromwell will generate a "new syntax" workflow output for each task output, and name them.
+Their name will be generated using their FQN, which would give
+
+```
+output {
+ String w.t.out1 = t.out1
+ String w.t.out2 = t.out2
+}
+```
+
+However as the FQN separator is `.`, the name itself cannot contain any `.`.
+For that reason, `.` are replaced with `_` :
+
+*Old syntax expanded to new syntax*
+```
+output {
+ String w_t_out1 = t.out1
+ String w_t_out2 = t.out2
+}
+```
+
+The consequence is that the workflow outputs section of the metadata for `old_syntax` would previously look like
+
+ ```
+ outputs {
+ "w.t.out1": "hello",
+ "w.t.out2": "hello"
+ }
+ ```
+
+but it will now look like
+
+```
+ outputs {
+ "w_t_out1": "hello",
+ "w_t_out2": "hello"
+ }
+```
+
+The same applies for the console output of a workflow run in single workflow mode.
+
+
## 0.22
* Improved retries for Call Caching and general bug fixes.
View
@@ -0,0 +1,197 @@
+# Making a backend
+
+## Part 0: Introduction
+
+- These notes were added while making a new AWS backend for Amazon AWS.
+
+## Part 1 (October 13 2016): The skeleton:
+
+To start with, I just need to create a bunch of boilerplate which will eventually be filled in with all of the lovely AWS details!
+
+### Defining the awsBackend project:
+
+- Added entries to `project/Settings.scala`, `project/Dependencies.scala` and `build.sbt`
+- This was mainly just a copy/paste from existing backend projects. I made a few typos renaming everything and linking the dependencies properly though!
+- E.g. In my first commit I forgot to update the libraryDependencies name for my AWS backend project:
+```
+ val awsBackendSettings = List(
+ name := "cromwell-aws-backend",
+ libraryDependencies ++= awsBackendDependencies
+ ) ++ commonSettings
+```
+- I guessed that I'd need the AWS SDK so I included that immediately in Dependencies.scala:
+```
+ val awsBackendDependencies = List(
+ "com.amazonaws" % "aws-java-sdk" % "1.11.41"
+ )
+```
+- In build.scala I had to also edit the `lazy val root` to include a new `.aggregate(awsBackend)` and a new `.dependsOn(awsBackend)`
+
+### Directory structure:
+
+- This is probably going to be autogenerated for you in the directories specified in the above files. I'd already added my own directory structure and sbt managed to pick it up correctly in `supportedBackends/aws`.
+
+### AWS Job Execution Actor:
+- To run a job, Cromwell needs to instantiate a Job Execution actor. I'll fill in the details later but for now, I'll just add the constructor, props, and an unimplemented method definition for `execute`:
+```
+class AwsJobExecutionActor(override val jobDescriptor: BackendJobDescriptor,
+ override val configurationDescriptor: BackendConfigurationDescriptor) extends BackendJobExecutionActor {
+
+ override def execute: Future[BackendJobExecutionResponse] = ???
+}
+
+object AwsJobExecutionActor {
+ def props(jobDescriptor: BackendJobDescriptor,
+ configurationDescriptor: BackendConfigurationDescriptor): Props = Props(new AwsJobExecutionActor(jobDescriptor, configurationDescriptor))
+}
+```
+
+### Actor factory:
+- This is the class which tells Cromwell which classes represent job execution actors, initialization actors and so on. I'm just adding a skeleton for now, with a constructor of the form the Cromwell expects:
+```
+case class AwsBackendActorFactory(name: String, configurationDescriptor: BackendConfigurationDescriptor) extends BackendLifecycleActorFactory {
+
+ override def jobExecutionActorProps(jobDescriptor: BackendJobDescriptor,
+ initializationData: Option[BackendInitializationData],
+ serviceRegistryActor: ActorRef,
+ backendSingletonActor: Option[ActorRef]): Props = AwsJobExecutionActor.props(jobDescriptor, configurationDescriptor)
+}
+```
+- There are a few other actor definitions that can be added to this file over time. But the only one that Cromwell *requires* to work is the job execution actor.
+
+### Reference conf:
+
+- Reference.conf is a set of reference options which shows people how to enable the backends that they want. So I'll add the initial config which people would add if they wanted the AWS backend (commented out in the reference so it's not enabled by default). This goes below all the other backend references:
+```
+ #AWS {
+ # actor-factory = "cromwell.backend.impl.aws.AwsBackendActorFactory"
+ # config {
+ #
+ # }
+ #}
+```
+
+### Application.conf
+
+- OK so I've now told people how to add this backend... Now I actually add it to my own personal configuration file so I can try it out!
+```
+backend {
+ default = "AWS"
+ providers {
+ AWS {
+ actor-factory = "cromwell.backend.impl.aws.AwsBackendActorFactory"
+ config {
+
+ }
+ }
+ }
+}
+```
+
+### Trying it out
+So we now have a backend skeleton! What happens when we run it? Well hopefully Cromwell will instantiate the backend far enough to reach the unimplemented execute method and then fall over. Let's give it a go!
+- I fire up cromwell in server mode with my modified application.conf.
+- I create a sample WDL that would sleep for 20 seconds if it actually worked:
+The input WDL:
+```
+task sleep {
+ command { sleep 20 }
+}
+workflow main {
+ call sleep
+}
+```
+- I submit the WDL to the swagger endpoint (http://localhost:8000/swagger/index.html?url=/swagger/cromwell.yaml) and watch the server logs...
+- And as expected:
+```
+2016-10-13 13:14:29,017 cromwell-system-akka.dispatchers.engine-dispatcher-39 INFO - MaterializeWorkflowDescriptorActor [UUID(ddd827ba)]: Call-to-Backend assignments: main.sleep -> AWS
+2016-10-13 13:14:30,167 cromwell-system-akka.dispatchers.engine-dispatcher-39 INFO - WorkflowExecutionActor-ddd827ba-091f-4c6f-b98f-cc9825717007 [UUID(ddd827ba)]: Starting calls: main.sleep:NA:1
+2016-10-13 13:14:30,983 cromwell-system-akka.actor.default-dispatcher-5 ERROR - guardian failed, shutting down system
+scala.NotImplementedError: an implementation is missing
+ at scala.Predef$.$qmark$qmark$qmark(Predef.scala:230)
+ at cromwell.backend.impl.aws.AwsJobExecutionActor.execute(AwsJobExecutionActor.scala:12)
+```
+- OK, so now I just need to implement `execute(): Future[JobExecutionResult]` and Cromwell can interface with AWS. How hard can it be!
+
+## Part 2 (October 13 2016): Using Amazon to sleep 20 seconds
+
+### Starting point
+- This was a learning experience after using the Google pipelines service to submit jobs!
+- To get myself started, I've manually created an ECS cluster which I've called `ecs-t2micro-cluster` via the ECS web console.
+
+### Trial and Error
+
+- I see in the aws sdk docs that there's an AmazonECSAsyncClient class. That sounds promising! Luckily I already added the dependency on AWS SDK in Part 1 so I guess I can just write something basic in my AwsJobExecutionActor class and see what happens:
+
+- I ended up having to add some credentials options to the configuration file. The new `reference.conf` now looks like:
+```
+ #AWS {
+ # actor-factory = "cromwell.backend.impl.aws.AwsBackendActorFactory"
+ # config {
+ # ## These two settings are required to authenticate with the ECS service:
+ # accessKeyId = "..."
+ # secretKey = "..."
+ # }
+ #}
+```
+
+- After a little bit of experimentation with the ECS API, I was able to come up with a backend that works but is very limited... It is entirely synchronous in the `execute` method. That's certainly not a final answer but it works OK for running a single task. And we can now run that single `sleep` command successfully on the Amazon EC2 Container Service!
+ - The synchronous `execute` method:
+```
+class AwsJobExecutionActor(override val jobDescriptor: BackendJobDescriptor,
+ override val configurationDescriptor: BackendConfigurationDescriptor) extends BackendJobExecutionActor {
+
+ val awsAccessKeyId = configurationDescriptor.backendConfig.as[String]("accessKeyId")
+ val awsSecretKey = configurationDescriptor.backendConfig.as[String]("secretKey")
+
+ val clusterName = "ecs-t2micro-cluster"
+
+ val credentials = new AWSCredentials {
+ override def getAWSAccessKeyId: String = awsAccessKeyId
+ override def getAWSSecretKey: String = awsSecretKey
+ }
+ val ecsAsyncClient = new AmazonECSAsyncClient(credentials)
+
+ override def execute: Future[BackendJobExecutionResponse] = {
+
+ val commandOverride = new ContainerOverride().withName("simple-app").withCommand(jobDescriptor.call.instantiateCommandLine(Map.empty, OnlyPureFunctions, identity).get)
+
+ val runRequest: RunTaskRequest = new RunTaskRequest()
+ .withCluster(clusterName)
+ .withCount(1)
+ .withTaskDefinition("ubuntuTask:1")
+ .withOverrides(new TaskOverride().withContainerOverrides(commandOverride))
+
+ val submitResultHandler = new AwsSdkAsyncHandler[RunTaskRequest, RunTaskResult]()
+ val _ = ecsAsyncClient.runTaskAsync(runRequest, submitResultHandler)
+
+ submitResultHandler.future map {
+ case AwsSdkAsyncResult(_, result) =>
+ log.info("AWS submission completed:\n{}", result.toString)
+ val taskArn= result.getTasks.asScala.head.getTaskArn
+ val taskDescription = waitUntilDone(taskArn)
+
+ log.info("AWS task completed!\n{}", taskDescription.toString)
+ SucceededResponse(jobDescriptor.key, Option(0), Map.empty, None, Seq.empty)
+ }
+ }
+
+ private def waitUntilDone(taskArn: String): Task = {
+ val describeTasksRequest = new DescribeTasksRequest().withCluster(clusterName).withTasks(List(taskArn).asJava)
+
+ val resultHandler = new AwsSdkAsyncHandler[DescribeTasksRequest, DescribeTasksResult]()
+ val _ = ecsAsyncClient.describeTasksAsync(describeTasksRequest, resultHandler)
+
+ val desribedTasks = Await.result(resultHandler.future, Duration.Inf)
+ val taskDescription = desribedTasks.result.getTasks.asScala.head
+ if (taskDescription.getLastStatus == DesiredStatus.STOPPED.toString) {
+ taskDescription
+ } else {
+ Thread.sleep(200)
+ waitUntilDone(taskArn)
+ }
+ }
+}
+```
+
+
Oops, something went wrong.

0 comments on commit f7f068c

Please sign in to comment.