Skip to content
Permalink
Browse files

Merge pull request #19 from abayer/jenkins-38331

[JENKINS-38331] Per-stage configuration for agent
  • Loading branch information
abayer committed Oct 14, 2016
2 parents 263a17a + 8cf6fd2 commit b7a3ec68779e2b8edc4da667cf9d42551d067fc6
Showing with 984 additions and 337 deletions.
  1. +37 −23 SYNTAX.md
  2. +16 −4 src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTStage.groovy
  3. +37 −0 src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/ClosureContentsChecker.groovy
  4. +32 −4 src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/Stage.groovy
  5. +6 −0 src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/JSONParser.groovy
  6. +53 −15 src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/ModelParser.groovy
  7. +3 −0 src/main/resources/ast-schema.json
  8. +16 −19 src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ClosureModelTranslator.groovy
  9. +12 −10 src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy
  10. +5 −0 src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/AbstractModelDefTest.java
  11. +21 −18 src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/AgentTest.java
  12. +11 −12 src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/BasicModelDefTest.java
  13. +12 −5 src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/EnvironmentTest.java
  14. +11 −4 src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/ScriptStepTest.java
  15. +11 −4 src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/ToolsTest.java
  16. +76 −49 src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/ValidatorTest.java
  17. +11 −4 src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/parser/ExecuteConvertedTest.java
  18. +4 −2 src/test/resources/abortedNotification.groovy
  19. +3 −1 src/test/resources/agentAny.groovy
  20. +4 −2 src/test/resources/agentDocker.groovy
  21. +4 −2 src/test/resources/agentDockerWithEmptyDockerArgs.groovy
  22. +4 −2 src/test/resources/agentDockerWithNullDockerArgs.groovy
  23. +3 −1 src/test/resources/agentLabel.groovy
  24. +4 −2 src/test/resources/agentNone.groovy
  25. +4 −2 src/test/resources/agentNoneWithNode.groovy
  26. +6 −2 src/test/resources/allStagesExist.groovy
  27. +7 −5 src/test/resources/buildPluginParentPOM.groovy
  28. +4 −2 src/test/resources/dockerGlobalVariable.groovy
  29. +5 −3 src/test/resources/dockerGlobalVariableInScript.groovy
  30. +3 −1 src/test/resources/errors/blockInJobProperties.groovy
  31. +3 −1 src/test/resources/errors/blockInParameters.groovy
  32. +3 −1 src/test/resources/errors/blockInTriggers.groovy
  33. +3 −1 src/test/resources/errors/closureAsMethodCallArg.groovy
  34. +3 −1 src/test/resources/errors/duplicateEnvironment.groovy
  35. +3 −1 src/test/resources/errors/duplicateNotificationConditions.groovy
  36. +3 −1 src/test/resources/errors/duplicatePostBuildConditions.groovy
  37. +6 −2 src/test/resources/errors/duplicateStageNames.groovy
  38. +4 −2 src/test/resources/errors/duplicateStepParameter.groovy
  39. +3 −1 src/test/resources/errors/emptyAgent.groovy
  40. +3 −1 src/test/resources/errors/emptyEnvironment.groovy
  41. +3 −1 src/test/resources/errors/emptyJobProperties.groovy
  42. +3 −1 src/test/resources/errors/emptyNotifications.groovy
  43. +3 −1 src/test/resources/errors/emptyParallel.groovy
  44. +3 −1 src/test/resources/errors/emptyParameters.groovy
  45. +3 −1 src/test/resources/errors/emptyPostBuild.groovy
  46. +3 −1 src/test/resources/errors/emptyTriggers.groovy
  47. +4 −2 src/test/resources/errors/globalLibraryNonStepBody.groovy
  48. +4 −2 src/test/resources/errors/globalLibraryObjectMethodCall.groovy
  49. +3 −1 src/test/resources/errors/importAndFunctionShouldNotSkipParsing.groovy
  50. +3 −1 src/test/resources/errors/invalidBuildCondition.groovy
  51. +5 −3 src/test/resources/errors/invalidMetaStepSyntax.groovy
  52. +3 −1 src/test/resources/errors/invalidParameterTypeMethodCall.groovy
  53. +4 −2 src/test/resources/errors/invalidStepParameterType.groovy
  54. +3 −1 src/test/resources/errors/missingAgent.groovy
  55. +4 −2 src/test/resources/errors/missingRequiredStepParameters.groovy
  56. +3 −1 src/test/resources/errors/mixedMethodArgs.groovy
  57. +3 −1 src/test/resources/errors/notInstalledToolType.groovy
  58. +3 −1 src/test/resources/errors/notInstalledToolVersion.groovy
  59. +3 −1 src/test/resources/errors/packageShouldNotSkipParsing.groovy
  60. +38 −0 src/test/resources/errors/perStageConfigEmptyAgent.groovy
  61. +37 −0 src/test/resources/errors/perStageConfigEmptySteps.groovy
  62. +35 −0 src/test/resources/errors/perStageConfigMissingSteps.groovy
  63. +41 −0 src/test/resources/errors/perStageConfigUnknownSection.groovy
  64. +3 −1 src/test/resources/errors/rejectMapsForTriggerDefinition.groovy
  65. +3 −1 src/test/resources/errors/rejectParallelInNotifications.groovy
  66. +9 −7 src/test/resources/errors/rejectParallelMixedInSteps.groovy
  67. +3 −1 src/test/resources/errors/rejectPropertiesStepInMethodCall.groovy
  68. +4 −2 src/test/resources/errors/rejectStageInSteps.groovy
  69. +3 −1 src/test/resources/errors/stageWithoutName.groovy
  70. +3 −1 src/test/resources/errors/tooFewMethodCallArgs.groovy
  71. +4 −2 src/test/resources/errors/unknownStepParameter.groovy
  72. +3 −1 src/test/resources/errors/unlistedToolType.groovy
  73. +3 −1 src/test/resources/errors/wrongParameterNameMethodCall.groovy
  74. +13 −11 src/test/resources/executionModelAction.groovy
  75. +3 −1 src/test/resources/failingNotifications.groovy
  76. +3 −1 src/test/resources/failingPipeline.groovy
  77. +3 −1 src/test/resources/failingPostBuild.groovy
  78. +22 −20 src/test/resources/globalLibrarySuccess.groovy
  79. +10 −8 src/test/resources/globalLibrarySuccessInScript.groovy
  80. +20 −0 src/test/resources/json/errors/perStageConfigEmptySteps.json
  81. +19 −0 src/test/resources/json/errors/perStageConfigMissingSteps.json
  82. +26 −0 src/test/resources/json/errors/perStageConfigUnknownSection.json
  83. +26 −0 src/test/resources/json/perStageConfigAgent.json
  84. +5 −3 src/test/resources/legacyMetaStepSyntax.groovy
  85. +5 −3 src/test/resources/metaStepSyntax.groovy
  86. +3 −1 src/test/resources/multipleProperties.groovy
  87. +3 −1 src/test/resources/noCheckoutScmInWrongContext.groovy
  88. +7 −5 src/test/resources/nonLiteralEnvironment.groovy
  89. +3 −1 src/test/resources/notificationOnChangeChanged.groovy
  90. +3 −1 src/test/resources/notificationOnChangeFailed.groovy
  91. +8 −6 src/test/resources/parallelPipeline.groovy
  92. +38 −0 src/test/resources/perStageConfigAgent.groovy
  93. +3 −1 src/test/resources/postBuildAndNotifications.groovy
  94. +3 −1 src/test/resources/shInNotification.groovy
  95. +3 −1 src/test/resources/simpleEnvironment.groovy
  96. +3 −1 src/test/resources/simpleJobProperties.groovy
  97. +3 −1 src/test/resources/simpleNotification.groovy
  98. +3 −1 src/test/resources/simpleParameters.groovy
  99. +3 −1 src/test/resources/simplePipeline.groovy
  100. +3 −1 src/test/resources/simplePostBuild.groovy
  101. +4 −2 src/test/resources/simpleScript.groovy
  102. +3 −1 src/test/resources/simpleTools.groovy
  103. +3 −1 src/test/resources/simpleTriggers.groovy
  104. +4 −2 src/test/resources/stringsNeedingEscapeLogic.groovy
  105. +6 −2 src/test/resources/twoStagePipeline.groovy
  106. +5 −3 src/test/resources/unstableNotification.groovy
  107. +4 −2 src/test/resources/validStepParameters.groovy
@@ -10,8 +10,9 @@
These are sections that are specified directly within the `pipeline` argument closure.

### agent
* *Description*: Specifies where the build will run.
* *Required*: Yes
* *Description*: Specifies where the build or stage will run.
* *Required*: Yes for the top-level `pipeline` closure, optional for individual `stage` closures.
* *Allowed In*: Top-level `pipeline` closure and individual `stage` closures.
* *Parameters*: Either a `Map` of one or more arguments or one of two constants - `none` or `any`.
* *Map Keys*:
* Note that this will be an `ExtensionPoint`, so plugins will be able to add to the available image providers.
@@ -41,6 +42,7 @@ These are sections that are specified directly within the `pipeline` argument cl
* *Description*: A sequence of `key = value` pairs, which will be passed to the `withEnv` step the build will be
executed within.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: One or more lines with `foo = 'bar'` variable name/value pairs.
@@ -59,6 +61,7 @@ environment {
### stages
* *Description*: A sequence of one or more Pipeline `stage`s, each of which consist of a sequence of steps.
* *Required*: Yes
* *Allowed In*: Top-level `pipeline` closure only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: one or more `stage` blocks, as described below.
@@ -69,27 +72,35 @@ to-be-released block-scoped `stage` syntax in base Pipeline.
* *Required*: At least one is required.
* *Parameters*: A single `String`, the name for the `stage`.
* *Takes a Closure*: Yes
* *Closure Contents*: One or more Pipeline steps, including block-scoped steps and the special `script` block described below.
* *Closure Contents*: A `steps` block containing one or more Pipeline steps, including block-scoped steps and the
special `script` block described below, and optionally, certain configuration sections that allow being set on a
per-stage basis.
* *NOTE*: Currently only the `agent` section can be configured per-stage.
* *NOTE*: Only the "declarative subset" of Groovy is allowed by default. See below for details on that subset.
* *NOTE*: The `parallel` step is a special case - it can only be used if it's the sole step in the `stage`.
* *Examples*:

```
stages {
stage('foo') {
echo 'bar'
steps {
echo 'bar'
}
}
}
stages {
stage('first') {
timeout(time:5, unit:'MINUTES') {
sh "mvn clean install -DskipTests"
steps {
timeout(time:5, unit:'MINUTES') {
sh "mvn clean install -DskipTests"
}
}
}
stage('second') {
node('some-node') {
agent label:'some-node'
steps {
checkout scm
sh "mvn clean install"
}
@@ -98,14 +109,16 @@ stages {
stages {
stage('parallel-stage') {
parallel(
firstBlock: {
echo "First block of the parallel"
},
secondBlock: {
echo "Second block of the parallel"
}
)
steps {
parallel(
firstBlock: {
echo "First block of the parallel"
},
secondBlock: {
echo "Second block of the parallel"
}
)
}
}
}
```
@@ -136,6 +149,7 @@ stages {
* *Description*: A top-level section defining tools to auto-install and put on the PATH. This is ignored if `image none`
is specified.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: Names and versions of tools configured in Jenkins to install.
@@ -155,6 +169,7 @@ tools {
### notifications
* *Description*: Defines notifications to be sent after build completion, assuming build status conditions are met.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: A sequence of one or more build conditions containing Pipeline steps to run. See below for
@@ -164,6 +179,7 @@ definition of build conditions and their contents.
* *Description*: Defines post build actions to be run after build completion, assuming build status conditions are met.
Note that `postBuild` steps are run *before* `notifications`.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: A sequence of one or more build conditions containing Pipeline steps to run. See below for
@@ -205,8 +221,9 @@ postBuild {
```

### Triggers
* *Description*:
* *Description*: Triggers for this job, as used in other Jenkins jobs.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: A sequence of one or more trigger configurations, using `@Symbol` names for constructors.
@@ -221,8 +238,9 @@ triggers {
```

### Build Parameters
* *Description*:
* *Description*: Build parameters that will be prompted for at build time.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: A sequence of one or more parameter definition configurations, using `@Symbol` names for constructors.
@@ -237,8 +255,9 @@ parameters {
```

### Job Properties
* *Description*:
* *Description*: Other job properties, such as build discarding, limiting concurrent builds, and more.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: A sequence of one or more job property configurations, using `@Symbol` names for constructors.
@@ -272,8 +291,3 @@ jobProperties {
* Method calls where the left hand side is a variable reference or a sequence of property references: `x.y.z(...)`
* Method calls (including `@Symbol` constructors like used above in job properties, triggers and build parameters) where there is no left hand side.
* Closure without parameters: `{ ... }`

## Script mode
* *Description*: A flag which, when set, allows usage of standard non-declarative-subset Pipeline code throughout the Jenkinsfile.
* *Usage*: Set by putting `use script` at the beginning of the Jenkinsfile
* *Examples*: (hard to figure out a good example here since we've moved to the `pipeline` step?)
@@ -20,6 +20,7 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidator
@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
public final class ModelASTStage extends ModelASTElement {
String name
ModelASTAgent agent
List<ModelASTBranch> branches = []

public ModelASTStage(Object sourceLocation) {
@@ -32,9 +33,13 @@ public final class ModelASTStage extends ModelASTElement {
branches.each { br ->
a.add(br.toJSON())
}
return new JSONObject()
.accumulate("name",name)
.accumulate("branches",a)
JSONObject o = new JSONObject()
o.accumulate("name",name)
o.accumulate("branches",a)
if (agent != null) {
o.accumulate("agent", agent.toJSON())
}
return o
}

@Override
@@ -43,13 +48,17 @@ public final class ModelASTStage extends ModelASTElement {
branches.each { b ->
b?.validate(validator)
}
agent?.validate(validator)
}

@Override
public String toGroovy() {
StringBuilder retString = new StringBuilder()
retString.append("stage(\"${name}\") {\n")

if (agent != null) {
retString.append(agent.toGroovy())
}
retString.append("steps {\n")
if (branches.size() > 1) {
retString.append("parallel(\n")
List<String> branchStrings = branches.collect { b ->
@@ -63,6 +72,8 @@ public final class ModelASTStage extends ModelASTElement {

retString.append("}\n")

retString.append("}\n")

return retString.toString()
}

@@ -72,5 +83,6 @@ public final class ModelASTStage extends ModelASTElement {
branches.each { b ->
b.removeSourceLocation()
}
agent?.removeSourceLocation()
}
}
@@ -0,0 +1,37 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


package org.jenkinsci.plugins.pipeline.modeldefinition.model

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings

/**
* Special wrapper for checking the contents of a {@link Closure}.
*
* @author Andrew Bayer
*/
@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
public class ClosureContentsChecker extends MappedClosure<Object,ClosureContentsChecker> {
}
@@ -36,19 +36,47 @@ import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted
@ToString
@EqualsAndHashCode
@SuppressFBWarnings(value="SE_NO_SERIALVERSIONID")
public class Stage implements StepBlockWithOtherArgs, Serializable {
public class Stage implements NestedModel, Serializable {

@Whitelisted
String name

@Whitelisted
StepsBlock closureWrapper
StepsBlock steps

@Whitelisted
Agent agent

@Whitelisted
public Stage(String n, StepsBlock w) {
Stage name(String n) {
this.name = n
this.closureWrapper = w
return this
}

@Whitelisted
Stage agent(Agent a) {
this.agent = a
return this
}

@Whitelisted
Stage agent(Map<String,String> args) {
this.agent = new Agent(args)
return this
}

@Whitelisted
Stage steps(StepsBlock s) {
this.steps = s
return this
}

@Override
@Whitelisted
public void modelFromMap(Map<String,Object> m) {
m.each { k, v ->
this."${k}"(v)
}
}

}
@@ -167,6 +167,10 @@ class JSONParser {

stage.name = j.getString("name")

if (j.has("agent")) {
stage.agent = parseAgent(j.get("agent"))
}

j.getJSONArray("branches").each { b ->
JSONObject o = (JSONObject)b
stage.branches.add(parseBranch(o))
@@ -175,6 +179,8 @@ class JSONParser {

}



public @CheckForNull ModelASTBranch parseBranch(JSONObject j) {
ModelASTBranch branch = new ModelASTBranch(j)
branch.name = j.getString("name")

0 comments on commit b7a3ec6

Please sign in to comment.
You can’t perform that action at this time.