Skip to content
Permalink
Browse files
[JENKINS-40866] First work on reusing nodes for stage agents
More tests still needed.
  • Loading branch information
abayer committed Jan 12, 2017
1 parent 71764d9 commit a262c3e9fa90372fadb9d2b0a64b27658f3b2d81
Showing 8 changed files with 258 additions and 50 deletions.
@@ -0,0 +1,85 @@
/*
* The MIT License
*
* Copyright (c) 2017, 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.agent;

import org.kohsuke.stapler.DataBoundSetter;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

public abstract class AbstractDockerAgent<D extends AbstractDockerAgent<D>> extends DeclarativeAgent<D> {
protected String label;
protected String args = "";
protected String registryUrl;
protected String registryCredentialsId;
protected boolean reuseNode;

public @Nullable
String getRegistryUrl() {
return registryUrl;
}

@DataBoundSetter
public void setRegistryUrl(String registryUrl) {
this.registryUrl = registryUrl;
}

public @Nullable String getRegistryCredentialsId() {
return registryCredentialsId;
}

@DataBoundSetter
public void setRegistryCredentialsId(String registryCredentialsId) {
this.registryCredentialsId = registryCredentialsId;
}

public boolean getReuseNode() {
return reuseNode;
}

@DataBoundSetter
public void setReuseNode(boolean reuseNode) {
this.reuseNode = reuseNode;
}

public @CheckForNull
String getLabel() {
return label;
}

@DataBoundSetter
public void setLabel(String label) {
this.label = label;
}

public @CheckForNull String getArgs() {
return args;
}

@DataBoundSetter
public void setArgs(String args) {
this.args = args;
}
}
@@ -40,6 +40,7 @@ public class DockerPipeline extends DeclarativeAgent<DockerPipeline> {
private String args = "";
private String registryUrl;
private String registryCredentialsId;
private boolean reuseNode;

@DataBoundConstructor
public DockerPipeline(@Nonnull String image) {
@@ -64,6 +65,14 @@ public void setRegistryCredentialsId(String registryCredentialsId) {
this.registryCredentialsId = registryCredentialsId;
}

public boolean getReuseNode() {
return reuseNode;
}

@DataBoundSetter
public void setReuseNode(boolean reuseNode) {
this.reuseNode = reuseNode;
}

public @Nullable String getLabel() {
return label;
@@ -26,6 +26,7 @@

import hudson.Extension;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.AbstractDockerAgent;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgent;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;
@@ -35,53 +36,13 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class DockerPipelineFromDockerfile extends DeclarativeAgent<DockerPipelineFromDockerfile> {
private String label;
public class DockerPipelineFromDockerfile extends AbstractDockerAgent<DockerPipelineFromDockerfile> {
private String filename;
private String args = "";
private String registryUrl;
private String registryCredentialsId;

@DataBoundConstructor
public DockerPipelineFromDockerfile() {
}

public @Nullable String getRegistryUrl() {
return registryUrl;
}

@DataBoundSetter
public void setRegistryUrl(String registryUrl) {
this.registryUrl = registryUrl;
}

public @Nullable String getRegistryCredentialsId() {
return registryCredentialsId;
}

@DataBoundSetter
public void setRegistryCredentialsId(String registryCredentialsId) {
this.registryCredentialsId = registryCredentialsId;
}

public @CheckForNull String getLabel() {
return label;
}

@DataBoundSetter
public void setLabel(String label) {
this.label = label;
}

public @CheckForNull String getArgs() {
return args;
}

@DataBoundSetter
public void setArgs(String args) {
this.args = args;
}

public @Nonnull Object getFilename() {
return filename;
}
@@ -25,6 +25,7 @@

package org.jenkinsci.plugins.pipeline.modeldefinition.agent.impl

import hudson.FilePath
import hudson.model.Result
import org.jenkinsci.plugins.pipeline.modeldefinition.SyntheticStageNames
import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
@@ -40,10 +41,22 @@ public class DockerPipelineFromDockerfileScript extends DeclarativeAgentScript<D

@Override
public Closure run(Closure body) {
String targetLabel = script.declarativeProps(property: DeclarativePropsStep.Property.LABEL,
override: describable.label)
LabelScript labelScript = (LabelScript) Label.DescriptorImpl.instanceForName("label", [label: targetLabel]).getScript(script)
return labelScript.run {
if (describable.reuseNode && script.getContext(FilePath.class) != null) {
return {
configureRegistry(body).call()
}
} else {
String targetLabel = script.declarativeProps(property: DeclarativePropsStep.Property.LABEL,
override: describable.label)
LabelScript labelScript = (LabelScript) Label.DescriptorImpl.instanceForName("label", [label: targetLabel]).getScript(script)
return labelScript.run {
configureRegistry(body).call()
}
}
}

private Closure configureRegistry(Closure body) {
return {
String registryUrl = script.declarativeProps(property: DeclarativePropsStep.Property.REGISTRY_URL,
override: describable.registryUrl)
String registryCreds = script.declarativeProps(property: DeclarativePropsStep.Property.REGISTRY_CREDENTIALS,
@@ -55,6 +68,7 @@ public class DockerPipelineFromDockerfileScript extends DeclarativeAgentScript<D
} else {
runImage(body).call()
}

}
}

@@ -25,10 +25,10 @@

package org.jenkinsci.plugins.pipeline.modeldefinition.agent.impl

import hudson.FilePath
import hudson.model.Result
import org.jenkinsci.plugins.pipeline.modeldefinition.SyntheticStageNames
import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgent
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentScript
import org.jenkinsci.plugins.pipeline.modeldefinition.steps.DeclarativePropsStep
import org.jenkinsci.plugins.workflow.cps.CpsScript
@@ -41,10 +41,22 @@ public class DockerPipelineScript extends DeclarativeAgentScript<DockerPipeline>

@Override
public Closure run(Closure body) {
String targetLabel = script.declarativeProps(property: DeclarativePropsStep.Property.LABEL,
override: describable.label)
LabelScript labelScript = (LabelScript) Label.DescriptorImpl.instanceForName("label", [label: targetLabel]).getScript(script)
return labelScript.run {
if (describable.reuseNode && script.getContext(FilePath.class) != null) {
return {
configureRegistry(body).call()
}
} else {
String targetLabel = script.declarativeProps(property: DeclarativePropsStep.Property.LABEL,
override: describable.label)
LabelScript labelScript = (LabelScript) Label.DescriptorImpl.instanceForName("label", [label: targetLabel]).getScript(script)
return labelScript.run {
configureRegistry(body).call()
}
}
}

private Closure configureRegistry(Closure body) {
return {
String registryUrl = script.declarativeProps(property: DeclarativePropsStep.Property.REGISTRY_URL,
override: describable.registryUrl)
String registryCreds = script.declarativeProps(property: DeclarativePropsStep.Property.REGISTRY_CREDENTIALS,
@@ -78,6 +78,23 @@ public void agentDocker() throws Exception {
agentDocker("agentDocker", "-v /tmp:/tmp -p 80:80");
}

@Test
public void agentDockerReuseNode() throws Exception {
agentDocker("agentDockerReuseNode");
}

@Test
public void agentDockerDontReuseNode() throws Exception {
assumeDocker();
// Bind mounting /var on OS X doesn't work at the moment
onAllowedOS(PossibleOS.LINUX);

expect(Result.FAILURE, "agentDockerDontReuseNode")
.logContains("The answer is 42")
.go();

}

@Test
public void agentDockerWithNullDockerArgs() throws Exception {
agentDocker("agentDockerWithNullDockerArgs");
@@ -0,0 +1,56 @@
/*
* The MIT License
*
* Copyright (c) 2017, 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.
*/

pipeline {
agent none

stages {
stage("foo") {
agent {
label "docker"
}
steps {
sh 'ls -la'
sh 'echo "The answer is 42"'
sh 'echo "${NODE_NAME}" > tmp.txt'
}
}
stage("bar") {
agent {
docker {
label "docker"
image "httpd:2.4.12"
reuseNode true
}
}
steps {
sh 'test -f Jenkinsfile'
}

}
}
}



@@ -0,0 +1,54 @@
/*
* The MIT License
*
* Copyright (c) 2017, 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.
*/

pipeline {
agent {
label "docker"
}
stages {
stage("foo") {
steps {
sh 'ls -la'
sh 'echo "The answer is 42"'
sh 'echo "${NODE_NAME}" > tmp.txt'
}
}
stage("bar") {
agent {
docker {
image "httpd:2.4.12"
reuseNode true
}
}
steps {
sh 'test -f Jenkinsfile'
sh 'test -f tmp.txt'
}

}
}
}



0 comments on commit a262c3e

Please sign in to comment.