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

Allow nesting of templates for inheritance. #94

Merged
merged 1 commit into from
Nov 30, 2016
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
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,54 @@ Field `inheritFrom` may refer a single podTemplate or multiple separated by spac
In any case if the referenced template is not found it will be ignored.


#### Nesting Pod templates

Field `inheritFrom` provides an easy way to compose podTemplates that have been pre-configured. In many cases it would be useful to define and compose podTemplates directly in the pipeline using groovy.
This is made possible via nesting. You can nest multiple pod templates together in order to compose a single one.

The example below composes two different podTemplates in order to create one with maven and docker capabilities.

podTemplate(label: 'docker', containers: [containerTemplate(image: 'docker)]) {
podTemplate(label: 'maven', containers: [containerTemplate(image: 'maven)]) {
// do stuff
}
}

This feature is extra useful, pipeline library developers as it allows you to wrap podTemplates into functions and let users, nest those functions according to their needs.

For example one could create a function for a maven template, say `mavenTemplate.groovy`:

#!/usr/bin/groovy
def call() {
podTemplate(label: label,
containers: [containerTemplate(name: 'maven', image: 'maven', command: 'cat', ttyEnabled: true)],
volumes: [secretVolume(secretName: 'maven-settings', mountPath: '/root/.m2'),
persistentVolumeClaim(claimName: 'maven-local-repo', mountPath: '/root/.m2nrepo')]) {
body()
}

and also a function for a docker template, say `dockerTemplate.groovy`:

#!/usr/bin/groovy
def call() {
podTemplate(label: label,
containers: [containerTemplate(name: 'docker', image: 'docker', command: 'cat', ttyEnabled: true)],
volumes: [hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock')]) {
body()
}

Then consumers of the library could just express the need for a maven pod with docker capabilities by combining the two:

dockerTemplate {
mavenTemplate {
ssh """
mvn clean install
docker build -t myimage ./target/docker/
"""
}
}


## Container Configuration
When configuring a container in a pipeline podTemplate the following options are available:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.csanchez.jenkins.plugins.kubernetes.pipeline;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import hudson.BulkChange;
import hudson.model.InvisibleAction;
import hudson.model.Run;

public class PodTemplateAction extends InvisibleAction {

private final Stack<String> names = new Stack<>();
private final Run run;


PodTemplateAction(Run run) {
this.run = run;
}

public void push(String template) throws IOException {
synchronized (run) {
BulkChange bc = new BulkChange(run);
try {
PodTemplateAction action = run.getAction(PodTemplateAction.class);
if (action == null) {
action = new PodTemplateAction(run);
run.addAction(action);
}
action.names.push(template);
bc.commit();
} finally {
bc.abort();
}
}
}

public String pop() throws IOException {
synchronized (run) {
BulkChange bc = new BulkChange(run);
try {
PodTemplateAction action = run.getAction(PodTemplateAction.class);
if (action == null) {
action = new PodTemplateAction(run);
run.addAction(action);
}
String template = action.names.pop();
bc.commit();
return template;
} finally {
bc.abort();
return null;
}
}
}

public List<String> getParentTemplateList() {
synchronized (run) {
PodTemplateAction action = run.getAction(PodTemplateAction.class);
if (action == null) {
action = new PodTemplateAction(run);
run.addAction(action);
}
return new ArrayList<>(action.names);
}
}

public String getParentTemplates() {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String template : getParentTemplateList()) {
if (first) {
first = false;
} else {
sb.append(" ");
}
sb.append(template);

}
return sb.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import org.csanchez.jenkins.plugins.kubernetes.volumes.PodVolume;
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
import org.jenkinsci.plugins.workflow.steps.StepContextParameter;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import hudson.Extension;
import hudson.model.Run;

public class PodTemplateStep extends AbstractStepImpl implements Serializable {

Expand All @@ -33,6 +35,13 @@ public class PodTemplateStep extends AbstractStepImpl implements Serializable {
private String nodeSelector;
private String workingDir = ContainerTemplate.DEFAULT_WORKING_DIR;

@StepContextParameter
private transient Run run;

public Run getRun() {
return run;
}

@DataBoundConstructor
public PodTemplateStep(String label, String name) {
this.label = label;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.csanchez.jenkins.plugins.kubernetes.pipeline;

import com.google.common.base.Strings;

import hudson.slaves.Cloud;
import jenkins.model.Jenkins;
import org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud;
Expand All @@ -26,12 +28,13 @@ public boolean start() throws Exception {
Cloud cloud = Jenkins.getActiveInstance().getCloud(step.getCloud());
if (cloud instanceof KubernetesCloud) {
KubernetesCloud kubernetesCloud = (KubernetesCloud) cloud;

String name = String.format(NAME_FORMAT, UUID.randomUUID().toString().replaceAll("-", ""));

PodTemplateAction action = new PodTemplateAction(step.getRun());

PodTemplate newTemplate = new PodTemplate();
newTemplate.setName(name);
newTemplate.setInheritFrom(step.getInheritFrom());
newTemplate.setInheritFrom(!Strings.isNullOrEmpty( action.getParentTemplates()) ? action.getParentTemplates() : step.getInheritFrom());
newTemplate.setLabel(step.getLabel());
newTemplate.setVolumes(step.getVolumes());
newTemplate.setContainers(step.getContainers());
Expand All @@ -42,6 +45,9 @@ public boolean start() throws Exception {
getContext().newBodyInvoker()
.withCallback(new PodTemplateCallback(newTemplate))
.start();


action.push(step.getLabel());
return false;
} else {
getContext().onFailure(new IllegalStateException("Could not find cloud with name:[" + step.getCloud() + "]."));
Expand All @@ -51,6 +57,7 @@ public boolean start() throws Exception {

@Override
public void stop(Throwable cause) throws Exception {
new PodTemplateAction(step.getRun()).pop();
}

private class PodTemplateCallback extends BodyExecutionCallback.TailCall {
Expand Down