Skip to content

Commit

Permalink
Merge pull request #38 from abayer/jenkins-39216
Browse files Browse the repository at this point in the history
[JENKINS-39216] Add "dockerfile" agent backend
  • Loading branch information
abayer committed Nov 4, 2016
2 parents eb197a9 + e1c84f2 commit 3e0001a
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 2 deletions.
Expand Up @@ -32,6 +32,7 @@ import hudson.ExtensionList
import hudson.model.Describable
import hudson.model.Descriptor
import hudson.triggers.TriggerDescriptor
import org.apache.commons.codec.digest.DigestUtils
import org.jenkinsci.plugins.pipeline.modeldefinition.actions.ExecutionModelAction
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTPipelineDef
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTStages
Expand Down Expand Up @@ -198,6 +199,11 @@ public class Utils {
r.addAction(new ExecutionModelAction(stages))
}

@Whitelisted
public static String stringToSHA1(String s) {
return DigestUtils.sha1Hex(s)
}

/**
* Creates and sets the loading for a cache of {@link Describable}s descending from the given descriptor type.
*
Expand Down
@@ -0,0 +1,80 @@
/*
* 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.agent.impl;

import hudson.Extension;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgent;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

public class DockerPipelineFromDockerfile extends DeclarativeAgent {
private String label;
private Object dockerfile;
private String dockerArgs = "";

@DataBoundConstructor
public DockerPipelineFromDockerfile(@Nonnull Object dockerfile) {
this.dockerfile = dockerfile;
}

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

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

public @CheckForNull String getDockerArgs() {
return dockerArgs;
}

@DataBoundSetter
public void setDockerArgs(String dockerArgs) {
this.dockerArgs = dockerArgs;
}

public @Nonnull Object getDockerfile() {
return dockerfile;
}

public String getDockerfileAsString() {
if (dockerfile instanceof String) {
return (String)dockerfile;
} else {
return "Dockerfile";
}
}

@Extension(ordinal = 999) @Symbol("dockerfile")
public static class DescriptorImpl extends DeclarativeAgentDescriptor {
}
}
@@ -0,0 +1,60 @@
/*
* 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.agent.impl

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.workflow.cps.CpsScript

public class DockerPipelineFromDockerfileScript extends DeclarativeAgentScript {

public DockerPipelineFromDockerfileScript(CpsScript s, DeclarativeAgent a) {
super(s, a)
}

@Override
public Closure run(Closure body) {
String targetLabel = declarativeAgent.label
if (targetLabel == null) {
targetLabel = script.dockerLabel()?.trim()
}
LabelScript labelScript = (LabelScript) Label.DescriptorImpl.instanceForName("label", [label: targetLabel]).getScript(script)
return labelScript.run {
script.checkout script.scm
try {
def hash = Utils.stringToSHA1(script.readFile(declarativeAgent.getDockerfileAsString()))
def imgName = "${hash}"
def img = script.getProperty("docker").build(imgName, "-f ${declarativeAgent.getDockerfileAsString()} .")
img.inside(declarativeAgent.dockerArgs, {
body.call()
})
} catch (FileNotFoundException f) {
script.error("No Dockerfile found at root of repository - failing.")
}
}
}
}
Expand Up @@ -158,6 +158,7 @@ public static Iterable<Object[]> configsWithErrors() {
result.add(new Object[]{"perStageConfigMissingSteps", "At /pipeline/stages/0/branches/0: Missing one or more required properties: 'steps'"});
result.add(new Object[]{"perStageConfigUnknownSection", "At /pipeline/stages/0: additional properties are not allowed"});

result.add(new Object[]{"unknownAgentType", "No agent type specified. Must contain one of [otherField, docker, dockerfile, label, any, none]"});
result.add(new Object[]{"invalidWrapperType", "Invalid wrapper type 'echo'. Valid wrapper types: [retry, script, timeout, withEnv]"});
result.add(new Object[]{"unknownAgentType", "No agent type specified. Must contain one of [otherField, docker, label, any, none]"});
result.add(new Object[]{"unknownBareAgentType", "Invalid argument for agent - 'foo' - must be map of config options or bare [any, none]."});
Expand Down
Expand Up @@ -139,13 +139,50 @@ public void agentTypeOrdering() throws Exception {
@Test
public void agentAnyInStage() throws Exception {
prepRepoWithJenkinsfile("agentAnyInStage");

WorkflowRun b = getAndStartBuild();
j.assertBuildStatusSuccess(j.waitForCompletion(b));
j.assertLogContains("[Pipeline] { (foo)", b);
j.assertLogContains("THIS WORKS", b);
}

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

prepRepoWithJenkinsfile("fromDockerfile");
sampleRepo.write("Dockerfile", "FROM ubuntu:14.04\n\nRUN echo 'HI THERE' > /hi-there\n\n");
sampleRepo.git("add", "Dockerfile");
sampleRepo.git("commit", "--message=Dockerfile");

WorkflowRun b = getAndStartBuild();
j.assertBuildStatusSuccess(j.waitForCompletion(b));
j.assertLogContains("[Pipeline] { (foo)", b);
j.assertLogContains("The answer is 42", b);
j.assertLogContains("-v /tmp:/tmp -p 8000:8000", b);
j.assertLogContains("HI THERE", b);
}

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

prepRepoWithJenkinsfile("fromAlternateDockerfile");
sampleRepo.write("Dockerfile.alternate", "FROM ubuntu:14.04\n\nRUN echo 'HI THERE' > /hi-there\n\n");
sampleRepo.git("add", "Dockerfile.alternate");
sampleRepo.git("commit", "--message=Dockerfile");

WorkflowRun b = getAndStartBuild();
j.assertBuildStatusSuccess(j.waitForCompletion(b));
j.assertLogContains("[Pipeline] { (foo)", b);
j.assertLogContains("The answer is 42", b);
j.assertLogContains("-v /tmp:/tmp -p 8000:8000", b);
j.assertLogContains("HI THERE", b);
}

private WorkflowRun agentDocker(final String jenkinsfile) throws Exception {
assumeDocker();
// Bind mounting /var on OS X doesn't work at the moment
Expand Down
Expand Up @@ -382,7 +382,7 @@ public void globalLibraryObjectMethodCall() throws Exception {
public void unknownAgentType() throws Exception {
prepRepoWithJenkinsfile("errors", "unknownAgentType");

assertFailWithError("No agent type specified. Must contain one of [otherField, docker, label, any, none]");
assertFailWithError("No agent type specified. Must contain one of [otherField, docker, dockerfile, label, any, none]");
}

@Test
Expand Down
@@ -0,0 +1,38 @@
/*
* 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.
*/

pipeline {
agent dockerfile:"Dockerfile.alternate", dockerArgs:"-v /tmp:/tmp -p 8000:8000"
stages {
stage("foo") {
steps {
sh 'cat /hi-there'
sh 'echo "The answer is 42"'
}
}
}
}



38 changes: 38 additions & 0 deletions pipeline-model-definition/src/test/resources/fromDockerfile.groovy
@@ -0,0 +1,38 @@
/*
* 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.
*/

pipeline {
agent dockerfile:true, dockerArgs:"-v /tmp:/tmp -p 8000:8000"
stages {
stage("foo") {
steps {
sh 'cat /hi-there'
sh 'echo "The answer is 42"'
}
}
}
}



0 comments on commit 3e0001a

Please sign in to comment.