package com.nirima.jenkins.plugins.docker;
import com.nirima.jenkins.plugins.docker.launcher.DockerComputerLauncher;
import com.nirima.jenkins.plugins.docker.strategy.DockerOnceRetentionStrategy;
import hudson.Extension;
import hudson.Util;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.labels.LabelAtom;
import hudson.slaves.RetentionStrategy;
import hudson.util.FormValidation;
import jenkins.model.Jenkins;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import shaded.com.google.common.base.MoreObjects;
import shaded.com.google.common.base.Strings;
import javax.annotation.CheckForNull;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DockerTemplate extends DockerTemplateBackwardCompatibility implements Describable<DockerTemplate> {
private static final Logger LOGGER = Logger.getLogger(DockerTemplate.class.getName());
private int configVersion = 2;
private final String labelString;
private DockerComputerLauncher launcher;
public final String remoteFsMapping;
public String remoteFs = "/home/jenkins";
public final int instanceCap;
private Node.Mode mode = Node.Mode.NORMAL;
private RetentionStrategy retentionStrategy = new DockerOnceRetentionStrategy(10);
private int numExecutors = 1;
private DockerTemplateBase dockerTemplateBase;
private boolean removeVolumes;
private transient /*almost final*/ Set<LabelAtom> labelSet;
private @CheckForNull DockerImagePullStrategy pullStrategy = DockerImagePullStrategy.PULL_LATEST;
/**
* fully default
*/
public DockerTemplate() {
this.labelString = "";
this.remoteFsMapping = Jenkins.getInstance().getRootDir().getAbsolutePath();
this.instanceCap = 1;
}
@DataBoundConstructor
public DockerTemplate(DockerTemplateBase dockerTemplateBase,
String labelString,
String remoteFs,
String remoteFsMapping,
String instanceCapStr
) {
this.dockerTemplateBase = dockerTemplateBase;
this.labelString = Util.fixNull(labelString);
this.remoteFs = Strings.isNullOrEmpty(remoteFs) ? "/home/jenkins" : remoteFs;
this.remoteFsMapping = remoteFsMapping;
if (instanceCapStr.equals("")) {
this.instanceCap = Integer.MAX_VALUE;
} else {
this.instanceCap = Integer.parseInt(instanceCapStr);
}
labelSet = Label.parse(labelString);
}
/**
* Contains all available arguments
*/
@Restricted(value = NoExternalUse.class)
public DockerTemplate(DockerTemplateBase dockerTemplateBase,
String labelString,
String remoteFs,
String remoteFsMapping,
String instanceCapStr,
Node.Mode mode,
int numExecutors,
DockerComputerLauncher launcher,
RetentionStrategy retentionStrategy,
boolean removeVolumes,
DockerImagePullStrategy pullStrategy) {
this(dockerTemplateBase,
labelString,
remoteFs,
remoteFsMapping,
instanceCapStr);
setMode(mode);
setNumExecutors(numExecutors);
setLauncher(launcher);
setRetentionStrategy(retentionStrategy);
setRemoveVolumes(removeVolumes);
setPullStrategy(pullStrategy);
}
public DockerTemplateBase getDockerTemplateBase() {
return dockerTemplateBase;
}
public void setDockerTemplateBase(DockerTemplateBase dockerTemplateBase) {
this.dockerTemplateBase = dockerTemplateBase;
}
public boolean isRemoveVolumes() {
return removeVolumes;
}
@DataBoundSetter
public void setRemoveVolumes(boolean removeVolumes) {
this.removeVolumes = removeVolumes;
}
public String getLabelString() {
return labelString;
}
@DataBoundSetter
public void setMode(Node.Mode mode) {
this.mode = mode;
}
public Node.Mode getMode() {
return mode;
}
/**
* Experimental option allows set number of executors
*/
@DataBoundSetter
public void setNumExecutors(int numExecutors) {
this.numExecutors = numExecutors;
}
public int getNumExecutors() {
if (getRetentionStrategy() instanceof DockerOnceRetentionStrategy) {
return 1; // works only with one executor!
}
return numExecutors;
}
@DataBoundSetter
public void setRetentionStrategy(RetentionStrategy retentionStrategy) {
this.retentionStrategy = retentionStrategy;
}
public RetentionStrategy getRetentionStrategy() {
return retentionStrategy;
}
/**
* tmp fix for terminating boolean caching
*/
public RetentionStrategy getRetentionStrategyCopy() {
if (retentionStrategy instanceof DockerOnceRetentionStrategy) {
DockerOnceRetentionStrategy onceRetention = (DockerOnceRetentionStrategy) retentionStrategy;
return new DockerOnceRetentionStrategy(onceRetention.getIdleMinutes());
}
return retentionStrategy;
}
@DataBoundSetter
public void setLauncher(DockerComputerLauncher launcher) {
this.launcher = launcher;
}
public DockerComputerLauncher getLauncher() {
return launcher;
}
public String getRemoteFs() {
return remoteFs;
}
public String getInstanceCapStr() {
if (instanceCap == Integer.MAX_VALUE) {
return "";
} else {
return String.valueOf(instanceCap);
}
}
public int getInstanceCap() {
return instanceCap;
}
public String getRemoteFsMapping() {
return remoteFsMapping;
}
public Set<LabelAtom> getLabelSet() {
return labelSet;
}
public DockerImagePullStrategy getPullStrategy() {
return pullStrategy;
}
@DataBoundSetter
public void setPullStrategy(DockerImagePullStrategy pullStrategy) {
this.pullStrategy = pullStrategy;
}
/**
* Initializes data structure that we don't persist.
*/
public Object readResolve() {
try {
if (configVersion < 1) {
convert1();
configVersion = 1;
}
// https://github.com/jenkinsci/docker-plugin/issues/270
if (configVersion < 2) {
if (retentionStrategy instanceof DockerOnceRetentionStrategy) {
DockerOnceRetentionStrategy tmpStrategy = (DockerOnceRetentionStrategy) retentionStrategy;
if (tmpStrategy.getIdleMinutes() == 0) {
setRetentionStrategy(new DockerOnceRetentionStrategy(10));
}
}
configVersion = 2;
} else {
// Xstream ignores default field values, so set them explicitly
if (mode == null) {
mode = Node.Mode.NORMAL;
}
if (retentionStrategy == null) {
retentionStrategy = new DockerOnceRetentionStrategy(10);
}
if (pullStrategy == null) {
pullStrategy = DockerImagePullStrategy.PULL_LATEST;
}
}
} catch (Throwable t) {
LOGGER.log(Level.SEVERE, "Can't convert old values to new (double conversion?): ", t);
}
try {
labelSet = Label.parse(labelString); // fails sometimes under debugger
} catch (Throwable t) {
LOGGER.log(Level.SEVERE, "Can't parse labels: ", t);
}
return this;
}
@Override
public String toString() {
return "DockerTemplate{" +
"configVersion=" + configVersion +
", labelString='" + labelString + '\'' +
", launcher=" + launcher +
", remoteFsMapping='" + remoteFsMapping + '\'' +
", remoteFs='" + remoteFs + '\'' +
", instanceCap=" + instanceCap +
", mode=" + mode +
", retentionStrategy=" + retentionStrategy +
", numExecutors=" + numExecutors +
", dockerTemplateBase=" + dockerTemplateBase +
", removeVolumes=" + removeVolumes +
", pullStrategy=" + pullStrategy +
'}';
}
public String getShortDescription() {
return MoreObjects.toStringHelper(this)
.add("image", dockerTemplateBase.getImage())
.toString();
}
public Descriptor<DockerTemplate> getDescriptor() {
return (DescriptorImpl) Jenkins.getInstance().getDescriptor(getClass());
}
@Extension
public static final class DescriptorImpl extends Descriptor<DockerTemplate> {
public FormValidation doCheckNumExecutors(@QueryParameter int numExecutors) {
if (numExecutors > 1) {
return FormValidation.warning("Experimental, see help");
} else if (numExecutors < 1) {
return FormValidation.error("Must be > 0");
}
return FormValidation.ok();
}
@Override
public String getDisplayName() {
return "Docker Template";
}
public Class getDockerTemplateBase() {
return DockerTemplateBase.class;
}
}
}