Skip to content
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

fix issue where agent closure with a label referencing a param variable causes test failures #252

Merged
merged 2 commits into from
Jul 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.lesfurets.jenkins.unit.declarative

import static com.lesfurets.jenkins.unit.declarative.DeclarativePipeline.createComponent
import static com.lesfurets.jenkins.unit.declarative.DeclarativePipeline.executeWith
import static com.lesfurets.jenkins.unit.declarative.ObjectUtils.printNonNullProperties
import static groovy.lang.Closure.*
Expand All @@ -16,6 +15,7 @@ class AgentDeclaration {
String dockerfileDir
Boolean reuseNode = null
String customWorkspace
def binding = null

def label(String label) {
this.label = label
Expand Down Expand Up @@ -53,6 +53,18 @@ class AgentDeclaration {
this.dockerfileDir = dir
}

def getCurrentBuild() {
return binding?.currentBuild
}

def getEnv() {
return binding?.env
}

def getParams() {
return binding?.params
}

def execute(Object delegate) {
def agentDesc = null
if (!label && !docker && dockerfile == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,18 @@ class DeclarativePipeline extends GenericPipelineDeclaration {
List<Closure> options = []

Closure triggers
Closure parameters

static <T> T createComponent(Class<T> componentType,
@DelegatesTo(strategy = DELEGATE_ONLY, value = T) Closure closure) {
def componentInstance = componentType.newInstance()
def rehydrate = closure.rehydrate(componentInstance, this, this)
rehydrate.resolveStrategy = DELEGATE_ONLY
rehydrate.call()
return componentInstance
}
ParametersDeclaration params = null

DeclarativePipeline() {
properties.put('any', 'any')
properties.put('none', 'none')
properties.put('scm', 'scm')
}

def getParams() {
return binding?.params
}

def propertyMissing(String name) {
if (properties.containsKey(name)) {
return properties.get(name)
Expand All @@ -45,8 +40,12 @@ class DeclarativePipeline extends GenericPipelineDeclaration {
this.triggers = closure
}

def parameters(@DelegatesTo(DeclarativePipeline) Closure closure) {
this.parameters = closure
def parameters(Object o) {
this.params = new ParametersDeclaration().with { it.label = o; it }
}

def parameters(@DelegatesTo(strategy=Closure.DELEGATE_ONLY, value=ParametersDeclaration) Closure closure) {
this.params = createComponent(ParametersDeclaration, closure)
}

def execute(Object delegate) {
Expand All @@ -55,7 +54,6 @@ class DeclarativePipeline extends GenericPipelineDeclaration {
executeOn(delegate, it)
}
this.agent?.execute(delegate)
executeOn(delegate, this.parameters)
executeOn(delegate, this.triggers)
this.stages.entrySet().forEach { e ->
e.value.execute(delegate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import com.lesfurets.jenkins.unit.BasePipelineTest
abstract class DeclarativePipelineTest extends BasePipelineTest {

def pipelineInterceptor = { Closure closure ->
DeclarativePipeline.createComponent(DeclarativePipeline, closure).execute(delegate)
GenericPipelineDeclaration.binding = delegate.binding
GenericPipelineDeclaration.createComponent(DeclarativePipeline, closure).execute(delegate)
}

def paramInterceptor = { Map desc ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ abstract class GenericPipelineDeclaration {
Closure tools
PostDeclaration post
Map<String, StageDeclaration> stages = [:]
static def binding = null

static <T> T createComponent(Class<T> componentType,
@DelegatesTo(strategy = DELEGATE_ONLY, value = T) Closure closure) {
def componentInstance = componentType.newInstance()
def rehydrate = closure.rehydrate(componentInstance, this, this)
rehydrate.resolveStrategy = DELEGATE_ONLY
if (binding && componentInstance.hasProperty('binding') && componentInstance.binding != binding) {
componentInstance.binding = binding
}
rehydrate.call()
return componentInstance
}

static <T> T executeOn(@DelegatesTo.Target Object delegate,
@DelegatesTo(strategy = DELEGATE_ONLY) Closure<T> closure) {
Expand All @@ -35,7 +48,7 @@ abstract class GenericPipelineDeclaration {
this.agent = new AgentDeclaration().with { it.label = o; it }
}

def agent(@DelegatesTo(strategy = DELEGATE_ONLY, value = AgentDeclaration) Closure closure) {
def agent(@DelegatesTo(strategy=Closure.DELEGATE_ONLY, value=AgentDeclaration) Closure closure) {
this.agent = createComponent(AgentDeclaration, closure)
}

Expand All @@ -60,6 +73,18 @@ abstract class GenericPipelineDeclaration {
this.stages.put(name, createComponent(StageDeclaration, closure).with { it.name = name; it })
}

def getCurrentBuild() {
return binding?.currentBuild
}

def getEnv() {
return binding?.env
}

def getParams() {
return binding?.params
}

def execute(Object delegate) {
// set environment
if (this.environment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ParallelDeclaration extends GenericPipelineDeclaration {

def stage(String name,
@DelegatesTo(strategy = DELEGATE_ONLY, value = StageDeclaration) Closure closure) {
this.stages.put(name, DeclarativePipeline.createComponent(StageDeclaration, closure).with{it.name = name;it} )
this.stages.put(name, createComponent(StageDeclaration, closure).with{it.name = name;it} )
}

def execute(Object delegate) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.lesfurets.jenkins.unit.declarative


class ParametersDeclaration {

def binding = null

Map getParams() {
return binding?.getVariable('params')
}

void setParams(String key, Object val) {
Map params = this.params
if (params != null && (! params.containsKey(key))) {
params[key] = val
}
}

// dereference 'parameters closure
def booleanParam(Map val) {
this.setParams( val.name, val.defaultValue )
}
def choice(Map val) {
this.setParams( val.name, val.choices[0] )
}
def password(Map val) {
this.setParams( val.name, val.defaultValue )
}
def string(Map val) {
this.setParams( val.name, val.defaultValue )
}
def text(Map val) {
this.setParams( val.name, val.defaultValue )
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ class StageDeclaration extends GenericPipelineDeclaration {
this.failFast = failFast
}

def getBinding_var() {
return binding?.var
}

def parallel(@DelegatesTo(strategy = DELEGATE_ONLY, value = ParallelDeclaration) Closure closure) {
this.parallel = DeclarativePipeline.createComponent(ParallelDeclaration, closure).with { it.failFast = failFast; it }
this.parallel = createComponent(ParallelDeclaration, closure).with { it.failFast = failFast; it }
}

def when(@DelegatesTo(strategy = DELEGATE_ONLY, value = WhenDeclaration) Closure closure) {
this.when = DeclarativePipeline.createComponent(WhenDeclaration, closure)
this.when = createComponent(WhenDeclaration, closure)
}

def options(@DelegatesTo(StageDeclaration) Closure closure) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.lesfurets.jenkins.unit.declarative

import org.junit.Before
import org.junit.Test
import org.junit.*

class TestDeclarativePipeline extends DeclarativePipelineTest {

Expand Down Expand Up @@ -193,4 +192,42 @@ class TestDeclarativePipeline extends DeclarativePipelineTest {
assertCallStack().contains('echo(SOMEVAR restored inside closure = SOMEVAL)')
assertCallStack().contains('echo(SOMEVAR outside closure = null)')
}

@Test void agent_with_param_label() throws Exception {
runScript('AgentParam_Jenkinsfile')
printCallStack()
assertCallStack().contains('aSlave')
assertJobStatusSuccess()
}

@Test void agent_with_mock_param_label() throws Exception {
def params = binding.getVariable('params')
params['SLAVE'] = 'mockSlave'
runScript('AgentParam_Jenkinsfile')
printCallStack()
assertCallStack().contains('mockSlave')
assertJobStatusSuccess()
}

@Test void should_agent_with_environment() throws Exception {
def env = binding.getVariable('env')
env['ENV_VAR'] = 'ENV VAR'
env['some_env_var'] = 'some env var'
runScript('Agent_env_Jenkinsfile')
printCallStack()
assertJobStatusSuccess()
}

@Ignore @Test void should_agent_with_bindings() throws Exception {
final def binding_var = 'a binding var'
binding.setVariable('binding_var', binding_var)

helper.registerAllowedMethod('getBinding_var', {
return binding_var
})

runScript('Agent_bindings_Jenkinsfile')
printCallStack()
assertJobStatusSuccess()
}
}
37 changes: 37 additions & 0 deletions src/test/jenkins/jenkinsfiles/AgentParam_Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
pipeline {
parameters {
booleanParam (
name: 'TOGGLE',
defaultValue: true,
description: 'Toggle this value'
)
choice (
name: 'CHOICE',
choices: ['One', 'Two', 'Three'],
description: 'Pick something'
)
password (
name: 'PASSWORD',
defaultValue: 'SECRET',
description: 'Enter a password'
)
string (
name: 'AGENT',
defaultValue: 'aSlave',
description: 'The slave to use for testing tested.'
)
text (
name: 'BIOGRAPHY',
defaultValue: 'they were born, they lived, they died',
description: 'Enter some information about the person'
)
}
agent { label params.AGENT }
stages {
stage('Example Build') {
steps {
echo "Hello from ${params.AGENT}"
}
}
}
}
19 changes: 19 additions & 0 deletions src/test/jenkins/jenkinsfiles/Agent_bindings_Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pipeline {
agent none
stages {
stage('Example Build') {
agent binding_var
steps {
echo 'Hello, Maven'
sh 'mvn --version'
}
}
stage('Example Test') {
agent { docker binding_var }
steps {
echo 'Hello, JDK'
sh 'java -version'
}
}
}
}
19 changes: 19 additions & 0 deletions src/test/jenkins/jenkinsfiles/Agent_env_Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pipeline {
agent none
stages {
stage('Example Build') {
agent env.ENV_VAR.toString()
steps {
echo 'Hello, Maven'
sh 'mvn --version'
}
}
stage('Example Test') {
agent { docker env.some_env_var }
steps {
echo 'Hello, JDK'
sh 'java -version'
}
}
}
}