diff --git a/examples/dind.groovy b/examples/dind.groovy
index e5fba67d16..27a81e92ea 100644
--- a/examples/dind.groovy
+++ b/examples/dind.groovy
@@ -10,6 +10,9 @@ podTemplate(yaml: '''
apiVersion: v1
kind: Pod
spec:
+ volumes:
+ - name: docker-socket
+ emptyDir: {}
containers:
- name: docker
image: docker:19.03.1
@@ -17,19 +20,19 @@ spec:
- sleep
args:
- 99d
- env:
- - name: DOCKER_HOST
- value: tcp://localhost:2375
+ volumeMounts:
+ - name: docker-socket
+ mountPath: /var/run
- name: docker-daemon
image: docker:19.03.1-dind
securityContext:
privileged: true
- env:
- - name: DOCKER_TLS_CERTDIR
- value: ""
+ volumeMounts:
+ - name: docker-socket
+ mountPath: /var/run
''') {
node(POD_LABEL) {
- git 'https://github.com/jenkinsci/docker-jnlp-slave.git'
+ writeFile file: 'Dockerfile', text: 'FROM scratch'
container('docker') {
sh 'docker version && DOCKER_BUILDKIT=1 docker build --progress plain -t testing .'
}
diff --git a/pom.xml b/pom.xml
index 4adcfd1306..2906b9dca6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
org.jenkins-ci.pluginsplugin
- 4.3
+ 4.7
@@ -39,7 +39,7 @@
- 1.26.5
+ 1.27.1-SNAPSHOT
@@ -49,7 +49,8 @@
82.222.4false
- 1.6.0
+ 1.7.2
+ 2.81true
@@ -62,7 +63,7 @@
org.jenkins-ci.pluginsjackson2-api
- 2.11.0
+ 2.11.2org.jenkins-ci.plugins
@@ -114,9 +115,17 @@
${pipeline-model-definition.version}true
+
+ org.jenkinsci.plugins
+ pipeline-model-definition
+ ${pipeline-model-definition.version}
+ tests
+ test
+ org.jenkins-ci.plugins.workflowworkflow-cps
+ ${workflow-cps.version}true
@@ -151,6 +160,7 @@
org.jenkins-ci.plugins.workflowworkflow-cps
+ ${workflow-cps.version}teststest
@@ -180,7 +190,7 @@
org.jenkins-ci.pluginsssh-agent
- 1.19
+ 1.20test
@@ -223,6 +233,12 @@
test-harnesstest
+
+ org.jenkins-ci.plugins
+ docker-workflow
+ 1.23
+ test
+ org.jenkins-ci.plugins
@@ -269,17 +285,10 @@
0500.85
+ ${jenkins.host.address}
-
- org.codehaus.mojo
- versions-maven-plugin
- 2.7
-
- file://${basedir}/src/test/resources/rules.xml
-
-
diff --git a/src/main/java/io/jenkins/plugins/kubernetes/NoDelayProvisionerStrategy.java b/src/main/java/io/jenkins/plugins/kubernetes/NoDelayProvisionerStrategy.java
index f7a055dd12..32d8d33720 100644
--- a/src/main/java/io/jenkins/plugins/kubernetes/NoDelayProvisionerStrategy.java
+++ b/src/main/java/io/jenkins/plugins/kubernetes/NoDelayProvisionerStrategy.java
@@ -6,12 +6,14 @@
import hudson.slaves.CloudProvisioningListener;
import hudson.slaves.NodeProvisioner;
import jenkins.model.Jenkins;
+import jenkins.util.Timer;
import org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -28,12 +30,12 @@
public class NoDelayProvisionerStrategy extends NodeProvisioner.Strategy {
private static final Logger LOGGER = Logger.getLogger(NoDelayProvisionerStrategy.class.getName());
- private static final boolean DISABLE_NODELAY_PROVISING = Boolean.valueOf(
+ private static final boolean DISABLE_NO_DELAY_PROVISIONING = Boolean.parseBoolean(
System.getProperty("io.jenkins.plugins.kubernetes.disableNoDelayProvisioning"));
@Override
public NodeProvisioner.StrategyDecision apply(NodeProvisioner.StrategyState strategyState) {
- if (DISABLE_NODELAY_PROVISING) {
+ if (DISABLE_NO_DELAY_PROVISIONING) {
LOGGER.log(Level.FINE, "Provisioning not complete, NoDelayProvisionerStrategy is disabled");
return NodeProvisioner.StrategyDecision.CONSULT_REMAINING_STRATEGIES;
}
@@ -49,6 +51,8 @@ public NodeProvisioner.StrategyDecision apply(NodeProvisioner.StrategyState stra
int currentDemand = snapshot.getQueueLength();
LOGGER.log(Level.FINE, "Available capacity={0}, currentDemand={1}",
new Object[]{availableCapacity, currentDemand});
+ int totalPlannedNodes = 0;
+ boolean canProvision = false;
if (availableCapacity < currentDemand) {
List jenkinsClouds = new ArrayList<>(Jenkins.get().clouds);
Collections.shuffle(jenkinsClouds);
@@ -61,22 +65,29 @@ public NodeProvisioner.StrategyDecision apply(NodeProvisioner.StrategyState stra
continue;
}
}
+ canProvision = true;
Collection plannedNodes = cloud.provision(label, workloadToProvision);
LOGGER.log(Level.FINE, "Planned {0} new nodes", plannedNodes.size());
fireOnStarted(cloud, strategyState.getLabel(), plannedNodes);
strategyState.recordPendingLaunches(plannedNodes);
availableCapacity += plannedNodes.size();
+ totalPlannedNodes += plannedNodes.size();
LOGGER.log(Level.FINE, "After provisioning, available capacity={0}, currentDemand={1}", new Object[]{availableCapacity, currentDemand});
break;
}
}
- if (availableCapacity >= currentDemand) {
- LOGGER.log(Level.FINE, "Provisioning completed");
- return NodeProvisioner.StrategyDecision.PROVISIONING_COMPLETED;
+ if (currentDemand - availableCapacity <= 0) {
+ LOGGER.log(Level.FINE, String.format("Provisioning completed for label: [%s]", label));
} else {
- LOGGER.log(Level.FINE, "Provisioning not complete, consulting remaining strategies");
- return NodeProvisioner.StrategyDecision.CONSULT_REMAINING_STRATEGIES;
+ if (!canProvision) {
+ return NodeProvisioner.StrategyDecision.CONSULT_REMAINING_STRATEGIES;
+ }
+ if (totalPlannedNodes > 0 && label != null) {
+ LOGGER.log(Level.FINE, "Suggesting NodeProvisioner review");
+ Timer.get().schedule(label.nodeProvisioner::suggestReviewNow, 1L, TimeUnit.SECONDS);
+ }
}
+ return NodeProvisioner.StrategyDecision.PROVISIONING_COMPLETED;
}
private static void fireOnStarted(final Cloud cloud, final Label label,
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java
index afa29cb86b..6d6d5bca38 100644
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java
@@ -19,15 +19,14 @@
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
+import hudson.Main;
import hudson.model.ItemGroup;
import hudson.util.XStream2;
-import io.fabric8.openshift.client.OpenShiftClient;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.csanchez.jenkins.plugins.kubernetes.pipeline.PodTemplateMap;
@@ -63,7 +62,6 @@
import hudson.slaves.NodeProvisioner;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
-import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodList;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
@@ -141,6 +139,7 @@ public class KubernetesCloud extends Cloud {
@DataBoundConstructor
public KubernetesCloud(String name) {
super(name);
+ setMaxRequestsPerHost(DEFAULT_MAX_REQUESTS_PER_HOST);
}
/**
@@ -540,24 +539,25 @@ public synchronized Collection provision(@CheckForN
int toBeProvisioned = Math.max(0, excessWorkload - allInProvisioning.size());
LOGGER.log(Level.INFO, "Excess workload after pending Kubernetes agents: {0}", toBeProvisioned);
- List r = new ArrayList();
+ List plannedNodes = new ArrayList<>();
- for (PodTemplate t: getTemplatesFor(label)) {
- LOGGER.log(Level.INFO, "Template for label {0}: {1}", new Object[] { label, t.getName() });
+ for (PodTemplate podTemplate: getTemplatesFor(label)) {
+ LOGGER.log(Level.INFO, "Template for label {0}: {1}", new Object[] { label, podTemplate.getName() });
for (int i = 0; i < toBeProvisioned; i++) {
- if (!addProvisionedSlave(t, label, i)) {
+ // Check concurrency limits
+ if (!addProvisionedSlave(podTemplate, label, i + allInProvisioning.size())) {
break;
}
- r.add(PlannedNodeBuilderFactory.createInstance().cloud(this).template(t).label(label).build());
+ plannedNodes.add(PlannedNodeBuilderFactory.createInstance().cloud(this).template(podTemplate).label(label).build());
}
LOGGER.log(Level.FINEST, "Planned Kubernetes agents for template \"{0}\": {1}",
- new Object[] { t.getName(), r.size() });
- if (r.size() > 0) {
- // Already found a matching template
- return r;
+ new Object[] { podTemplate.getName(), plannedNodes.size() });
+ if (plannedNodes.size() > 0) {
+ // Return early when a matching template was found and nodes were planned
+ return plannedNodes;
}
}
- return r;
+ return plannedNodes;
} catch (KubernetesClientException e) {
Throwable cause = e.getCause();
if (cause instanceof SocketTimeoutException || cause instanceof ConnectException || cause instanceof UnknownHostException) {
@@ -579,9 +579,9 @@ public synchronized Collection provision(@CheckForN
* Check not too many already running.
*
*/
- private boolean addProvisionedSlave(@Nonnull PodTemplate template, @CheckForNull Label label, int scheduledCount) throws Exception {
+ private boolean addProvisionedSlave(@Nonnull PodTemplate template, @CheckForNull Label label, int numProvisioned) throws Exception {
if (containerCap == 0) {
- return true;
+ return false;
}
KubernetesClient client = connect();
@@ -592,23 +592,27 @@ private boolean addProvisionedSlave(@Nonnull PodTemplate template, @CheckForNull
templateNamespace = client.getNamespace();
}
+ // check overall concurrency limit using the default label(s) on all templates
Map podLabels = getPodLabelsMap();
- List allActiveSlavePods = getActiveSlavePods(client, templateNamespace, podLabels);
- if (allActiveSlavePods != null && containerCap <= allActiveSlavePods.size() + scheduledCount) {
+ long numRunningOrPending = getNumActiveSlavePods(client, templateNamespace, podLabels);
+ if (numRunningOrPending + numProvisioned >= containerCap) {
LOGGER.log(Level.INFO,
- "Maximum number of concurrently running agent pods ({0}) reached for Kubernetes Cloud {4}, not provisioning: {1} running or pending in namespace {2} with Kubernetes labels {3}",
- new Object[] { containerCap, allActiveSlavePods.size() + scheduledCount, templateNamespace, getLabels(), name });
+ "Maximum number of concurrently running agent pods ({0}) reached for Kubernetes Cloud {4}, " +
+ "not provisioning: {1} running or pending in namespace {2} with Kubernetes labels {3}",
+ new Object[] { containerCap, numRunningOrPending, templateNamespace, getLabels(), name });
return false;
}
- Map labelsMap = new HashMap<>(podLabels);
- labelsMap.putAll(template.getLabelsMap());
- List activeTemplateSlavePods = getActiveSlavePods(client, templateNamespace, labelsMap);
- if (activeTemplateSlavePods != null && allActiveSlavePods != null && template.getInstanceCap() <= activeTemplateSlavePods.size() + scheduledCount) {
+ // check template-level concurrency limit using template-level labels
+ Map templateLabels = new HashMap<>(podLabels);
+ templateLabels.putAll(template.getLabelsMap());
+ numRunningOrPending = getNumActiveSlavePods(client, templateNamespace, podLabels);
+ if (numRunningOrPending + numProvisioned >= template.getInstanceCap()) {
LOGGER.log(Level.INFO,
- "Maximum number of concurrently running agent pods ({0}) reached for template {1} in Kubernetes Cloud {6}, not provisioning: {2} running or pending in namespace {3} with label \"{4}\" and Kubernetes labels {5}",
- new Object[] { template.getInstanceCap(), template.getName(), activeTemplateSlavePods.size() + scheduledCount,
- templateNamespace, label == null ? "" : label.toString(), labelsMap, name });
+ "Maximum number of concurrently running agent pods ({0}) reached for template {1} in Kubernetes Cloud {6}, " +
+ "not provisioning: {2} running or pending in namespace {3} with label \"{4}\" and Kubernetes labels {5}",
+ new Object[] { template.getInstanceCap(), template.getName(), numRunningOrPending,
+ templateNamespace, label == null ? "" : label.toString(), templateLabels, name });
return false;
}
return true;
@@ -617,16 +621,15 @@ private boolean addProvisionedSlave(@Nonnull PodTemplate template, @CheckForNull
/**
* Query for running or pending pods
*/
- private List getActiveSlavePods(KubernetesClient client, String templateNamespace, Map podLabels) {
+ private long getNumActiveSlavePods(KubernetesClient client, String templateNamespace, Map podLabels) {
PodList slaveList = client.pods().inNamespace(templateNamespace).withLabels(podLabels).list();
- List activeSlavePods = null;
// JENKINS-53370 check for nulls
if (slaveList != null && slaveList.getItems() != null) {
- activeSlavePods = slaveList.getItems().stream() //
+ return slaveList.getItems().stream() //
.filter(x -> x.getStatus().getPhase().toLowerCase().matches("(running|pending)"))
- .collect(Collectors.toList());
+ .count();
}
- return activeSlavePods;
+ return 0;
}
@Override
@@ -735,7 +738,10 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
- return Objects.hash(defaultsProviderTemplate, templates, serverUrl, serverCertificate, skipTlsVerify, addMasterProxyEnvVars, capOnlyOnAlivePods, namespace, jenkinsUrl, jenkinsTunnel, credentialsId, containerCap, retentionTimeout, connectTimeout, readTimeout, podLabels, usageRestricted, maxRequestsPerHost, podRetention);
+ return Objects.hash(defaultsProviderTemplate, templates, serverUrl, serverCertificate, skipTlsVerify,
+ addMasterProxyEnvVars, capOnlyOnAlivePods, namespace, jenkinsUrl, jenkinsTunnel, credentialsId,
+ containerCap, retentionTimeout, connectTimeout, readTimeout, podLabels, usageRestricted,
+ maxRequestsPerHost, podRetention);
}
public Integer getWaitForPodSec() {
@@ -990,4 +996,17 @@ public List getList(@Nonnull KubernetesCloud cloud) {
return cloud.getTemplates();
}
}
+
+ @Initializer(after = InitMilestone.SYSTEM_CONFIG_LOADED)
+ public static void hpiRunInit() {
+ if (Main.isDevelopmentMode) {
+ Jenkins jenkins = Jenkins.get();
+ String hostAddress = System.getProperty("jenkins.host.address");
+ if (hostAddress != null && jenkins.clouds.getAll(KubernetesCloud.class).isEmpty()) {
+ KubernetesCloud cloud = new KubernetesCloud("kubernetes");
+ cloud.setJenkinsUrl("http://" + hostAddress + ":8080/jenkins/");
+ jenkins.clouds.add(cloud);
+ }
+ }
+ }
}
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesLauncher.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesLauncher.java
index f8785d0f30..01fc463c81 100644
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesLauncher.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesLauncher.java
@@ -38,9 +38,7 @@
import javax.annotation.CheckForNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import hudson.model.Queue;
import io.fabric8.kubernetes.client.KubernetesClientException;
-import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
@@ -56,7 +54,10 @@
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.dsl.LogWatch;
import io.fabric8.kubernetes.client.dsl.PrettyLoggable;
+
+import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
/**
* Launches on Kubernetes the specified {@link KubernetesComputer} instance.
@@ -124,12 +125,27 @@ public synchronized void launch(SlaveComputer computer, TaskListener listener) {
.stream().filter(s -> StringUtils.isNotBlank(s)).findFirst().orElse(null);
slave.setNamespace(namespace);
- LOGGER.log(Level.FINE, "Creating Pod: {0}/{1}", new Object[] { namespace, podName });
- pod = client.pods().inNamespace(namespace).create(pod);
+
+ TaskListener runListener = template.getListener();
+
+ LOGGER.log(FINE, "Creating Pod: {0}/{1}", new Object[] { namespace, podName });
+ try {
+ pod = client.pods().inNamespace(namespace).create(pod);
+ } catch (KubernetesClientException e) {
+ int httpCode = e.getCode();
+ if (400 <= httpCode && httpCode < 500) { // 4xx
+ runListener.getLogger().printf("ERROR: Unable to create pod %s/%s.%n%s%n", namespace, pod.getMetadata().getName(), e.getMessage());
+ PodUtils.cancelQueueItemFor(pod, e.getMessage());
+ } else if (500 <= httpCode && httpCode < 600) { // 5xx
+ LOGGER.log(FINE,"Kubernetes returned HTTP code {0} {1}. Retrying...", new Object[] {e.getCode(), e.getStatus()});
+ } else {
+ LOGGER.log(WARNING, "Kubernetes returned unhandled HTTP code {0} {1}", new Object[] {e.getCode(), e.getStatus()});
+ }
+ throw e;
+ }
LOGGER.log(INFO, "Created Pod: {0}/{1}", new Object[] { namespace, podName });
listener.getLogger().printf("Created Pod: %s/%s%n", namespace, podName);
- TaskListener runListener = template.getListener();
runListener.getLogger().printf("Created Pod: %s/%s%n", namespace, podName);
template.getWorkspaceVolume().createVolume(client, pod.getMetadata());
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java
index 69217f7224..2980066b71 100644
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java
@@ -14,6 +14,8 @@
import javax.annotation.Nonnull;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
@@ -68,6 +70,11 @@ public class PodTemplate extends AbstractDescribableImpl implements
public static final Integer DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT = Integer
.getInteger(PodTemplate.class.getName() + ".connectionTimeout", 100);
+ /**
+ * Digest function that is used to compute the kubernetes label "jenkins/label-digest"
+ */
+ public static final HashFunction LABEL_DIGEST_FUNCTION = Hashing.sha1();
+
private String inheritFrom;
private String name;
@@ -395,7 +402,17 @@ public Set getLabelSet() {
}
public Map getLabelsMap() {
- return ImmutableMap.of("jenkins/label", label == null ? DEFAULT_LABEL : sanitizeLabel(label));
+ if (label == null) {
+ return ImmutableMap.of(
+ "jenkins/label", DEFAULT_LABEL,
+ "jenkins/label-digest", "0"
+ );
+ } else {
+ return ImmutableMap.of(
+ "jenkins/label", sanitizeLabel(label),
+ "jenkins/label-digest", LABEL_DIGEST_FUNCTION.hashString(label).toString()
+ );
+ }
}
static String sanitizeLabel(String input) {
@@ -495,8 +512,8 @@ public void setHostNetwork(Boolean hostNetwork) {
this.hostNetwork = hostNetwork;
}
- public Boolean isHostNetwork() {
- return hostNetwork;
+ public boolean isHostNetwork() {
+ return isHostNetworkSet()?hostNetwork.booleanValue():false;
}
public boolean isHostNetworkSet() {
@@ -906,6 +923,19 @@ public void save() { }
@Extension
public static class DescriptorImpl extends Descriptor {
+ static final String[] STRING_FIELDS = {
+ "activeDeadlineSeconds",
+ "idleMinutes",
+ "instanceCap",
+ "slaveConnectTimeout",
+ };
+
+ public DescriptorImpl() {
+ for (String field : STRING_FIELDS) {
+ addHelpFileRedirect(field + "Str", PodTemplate.class, field);
+ }
+ }
+
@Override
public String getDisplayName() {
return "Kubernetes Pod Template";
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtils.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtils.java
index e61b461afa..508277a9d5 100644
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtils.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtils.java
@@ -417,7 +417,11 @@ public static PodTemplate combine(PodTemplate parent, PodTemplate template) {
podTemplate.setSupplementalGroups(template.getSupplementalGroups() != null ? template.getSupplementalGroups() : parent.getSupplementalGroups());
- podTemplate.setHostNetwork(template.isHostNetworkSet() ? template.isHostNetwork() : parent.isHostNetwork());
+ if (template.isHostNetworkSet()) {
+ podTemplate.setHostNetwork(template.isHostNetwork());
+ } else if (parent.isHostNetworkSet()) {
+ podTemplate.setHostNetwork(parent.isHostNetwork());
+ }
List yamls = new ArrayList<>(parent.getYamls());
yamls.addAll(template.getYamls());
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodUtils.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodUtils.java
index 95737c8407..30e2636f39 100644
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodUtils.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodUtils.java
@@ -17,16 +17,22 @@
package org.csanchez.jenkins.plugins.kubernetes;
import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.model.Queue;
import io.fabric8.kubernetes.api.model.ContainerStatus;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodStatus;
+import jenkins.model.Jenkins;
+import org.apache.commons.lang.StringUtils;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import java.util.stream.Collectors;
public final class PodUtils {
+ private static final Logger LOGGER = Logger.getLogger(PodUtils.class.getName());
public static final Predicate CONTAINER_IS_TERMINATED = cs -> cs.getState().getTerminated() != null;
public static final Predicate CONTAINER_IS_WAITING = cs -> cs.getState().getWaiting() != null;
@@ -51,4 +57,32 @@ public static List getContainerStatus(Pod pod) {
public static List getContainers(Pod pod, Predicate predicate) {
return getContainerStatus(pod).stream().filter(predicate).collect(Collectors.toList());
}
+
+ /**
+ * Cancel queue items matching the given pod.
+ * It uses the annotation "runUrl" added to the pod to do the matching.
+ *
+ * It uses the current thread context to list item queues,
+ * so make sure to be in the right context before calling this method.
+ *
+ * @param pod The pod to cancel items for.
+ * @param reason The reason the item are being cancelled.
+ */
+ public static void cancelQueueItemFor(Pod pod, String reason) {
+ Queue q = Jenkins.get().getQueue();
+ boolean cancelled = false;
+ for (Queue.Item item: q.getItems()) {
+ Queue.Task task = item.task;
+ if (task.getUrl().equals(pod.getMetadata().getAnnotations().get("runUrl"))) {
+ LOGGER.log(Level.FINE, "Cancelling queue item: \"{0}\"\n{1}",
+ new Object[]{ task.getDisplayName(), !StringUtils.isBlank(reason) ? "due to " + reason : ""});
+ q.cancel(item);
+ cancelled = true;
+ break;
+ }
+ }
+ if (!cancelled) {
+ LOGGER.log(Level.FINE, "No queue item found for pod: {0}/{1}", new Object[] {pod.getMetadata().getNamespace(), pod.getMetadata().getName()});
+ }
+ }
}
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecDecorator.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecDecorator.java
index 0b275ce2b5..f20ca9b0ba 100755
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecDecorator.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecDecorator.java
@@ -93,8 +93,7 @@ public class ContainerExecDecorator extends LauncherDecorator implements Seriali
* stdin buffer size for commands sent to Kubernetes exec api. A low value will cause slowness in commands executed.
* A higher value will consume more memory
*/
- private static final int STDIN_BUFFER_SIZE = Integer
- .getInteger(ContainerExecDecorator.class.getName() + ".stdinBufferSize", 2 * 1024);
+ private static final int STDIN_BUFFER_SIZE = Integer.getInteger(ContainerExecDecorator.class.getName() + ".stdinBufferSize", 16 * 1024);
@SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "not needed on deserialization")
private transient List closables;
@@ -612,6 +611,7 @@ private boolean isSeparator(int b) {
}
private static void doExec(PrintStream in, PrintStream out, boolean[] masks, String... statements) {
+ long start = System.nanoTime();
// For logging
ByteArrayOutputStream loggingOutput = new ByteArrayOutputStream();
// Tee both outputs
@@ -632,7 +632,7 @@ private static void doExec(PrintStream in, PrintStream out, boolean[] masks, Str
.append("\" ");
}
tee.println();
- LOGGER.log(Level.FINEST, loggingOutput.toString(encoding));
+ LOGGER.log(Level.FINEST, loggingOutput.toString(encoding) + "[" + TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - start) + " μs." + "]");
// We need to exit so that we know when the command has finished.
tee.println(EXIT);
tee.flush();
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent.java
index 2398a76ac2..e42feabd8d 100644
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent.java
@@ -1,7 +1,16 @@
package org.csanchez.jenkins.plugins.kubernetes.pipeline;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.ExtensionList;
+import hudson.Util;
+import hudson.model.Descriptor;
+import hudson.model.Label;
+import hudson.util.ListBoxModel;
import org.apache.commons.lang.StringUtils;
import org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate;
+import org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud;
+import org.csanchez.jenkins.plugins.kubernetes.PodTemplate;
import org.csanchez.jenkins.plugins.kubernetes.pod.retention.PodRetention;
import org.csanchez.jenkins.plugins.kubernetes.pod.yaml.YamlMergeStrategy;
import org.csanchez.jenkins.plugins.kubernetes.volumes.workspace.WorkspaceVolume;
@@ -13,45 +22,59 @@
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
+import org.kohsuke.stapler.QueryParameter;
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import edu.umd.cs.findbugs.annotations.NonNull;
-import hudson.Util;
-
+import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
public class KubernetesDeclarativeAgent extends DeclarativeAgent {
private static final Logger LOGGER = Logger.getLogger(KubernetesDeclarativeAgent.class.getName());
+ @CheckForNull
private String label;
+ @CheckForNull
private String customWorkspace;
+ @CheckForNull
private String cloud;
+ @CheckForNull
private String inheritFrom;
private int idleMinutes;
- private int instanceCap;
+ private int instanceCap = Integer.MAX_VALUE;
+ @CheckForNull
private String serviceAccount;
+ @CheckForNull
private String nodeSelector;
+ @CheckForNull
private String namespace;
+ @CheckForNull
private String workingDir;
private int activeDeadlineSeconds;
private int slaveConnectTimeout;
+ @CheckForNull
private PodRetention podRetention;
private ContainerTemplate containerTemplate;
private List containerTemplates;
+ @CheckForNull
private String defaultContainer;
+ @CheckForNull
private String yaml;
+ @CheckForNull
private String yamlFile;
private YamlMergeStrategy yamlMergeStrategy;
+ @CheckForNull
private WorkspaceVolume workspaceVolume;
+ @CheckForNull
private String supplementalGroups;
@DataBoundConstructor
@@ -68,6 +91,10 @@ public String getLabel() {
return label;
}
+ public String getLabelExpression() {
+ return label != null ? Label.parse(label).stream().map(Objects::toString).sorted().collect(Collectors.joining(" && ")) : null;
+ }
+
@DataBoundSetter
public void setLabel(String label) {
this.label = Util.fixEmpty(label);
@@ -89,7 +116,7 @@ public String getCloud() {
@DataBoundSetter
public void setCloud(String cloud) {
- this.cloud = cloud;
+ this.cloud = Util.fixEmpty(cloud);
}
public int getIdleMinutes() {
@@ -107,7 +134,11 @@ public String getInheritFrom() {
@DataBoundSetter
public void setInheritFrom(String inheritFrom) {
- this.inheritFrom = inheritFrom;
+ if (PodTemplateStep.DescriptorImpl.defaultInheritFrom.equals(inheritFrom)) {
+ this.inheritFrom = null;
+ } else {
+ this.inheritFrom = inheritFrom;
+ }
}
public int getInstanceCap() {
@@ -116,7 +147,11 @@ public int getInstanceCap() {
@DataBoundSetter
public void setInstanceCap(int instanceCap) {
- this.instanceCap = instanceCap;
+ if (instanceCap <= 0) {
+ this.instanceCap = Integer.MAX_VALUE;
+ } else {
+ this.instanceCap = instanceCap;
+ }
}
public String getServiceAccount() {
@@ -213,12 +248,12 @@ public void setSlaveConnectTimeout(int slaveConnectTimeout) {
}
public PodRetention getPodRetention() {
- return podRetention;
+ return this.podRetention == null ? PodTemplateStep.DescriptorImpl.defaultPodRetention : this.podRetention;
}
@DataBoundSetter
- public void setPodRetention(PodRetention podRetention) {
- this.podRetention = podRetention;
+ public void setPodRetention(@CheckForNull PodRetention podRetention) {
+ this.podRetention = (podRetention == null || podRetention.equals(PodTemplateStep.DescriptorImpl.defaultPodRetention)) ? null : podRetention;
}
public String getYamlFile() {
@@ -240,17 +275,17 @@ public void setYamlMergeStrategy(YamlMergeStrategy yamlMergeStrategy) {
}
public WorkspaceVolume getWorkspaceVolume() {
- return workspaceVolume;
+ return workspaceVolume == null ? PodTemplateStep.DescriptorImpl.defaultWorkspaceVolume : this.workspaceVolume;
}
@DataBoundSetter
public void setWorkspaceVolume(WorkspaceVolume workspaceVolume) {
- this.workspaceVolume = workspaceVolume;
+ this.workspaceVolume = (workspaceVolume == null || workspaceVolume.equals(PodTemplateStep.DescriptorImpl.defaultWorkspaceVolume)) ? null : workspaceVolume;
}
@DataBoundSetter
public void setSupplementalGroups(String supplementalGroups) {
- this.supplementalGroups = supplementalGroups;
+ this.supplementalGroups = Util.fixEmpty(supplementalGroups);
}
public String getSupplementalGroups() {
@@ -274,7 +309,9 @@ public Map getAsArgs() {
"Ignoring containerTemplate option as containerTemplates is also defined");
}
}
- argMap.put("containers", containerTemplates);
+ if (containerTemplates != null && !containerTemplates.isEmpty()) {
+ argMap.put("containers", containerTemplates);
+ }
if (!StringUtils.isEmpty(yaml)) {
argMap.put("yaml", yaml);
@@ -315,7 +352,7 @@ public Map getAsArgs() {
if (podRetention != null) {
argMap.put("podRetention", podRetention);
}
- if (instanceCap > 0) {
+ if (instanceCap > 0 && instanceCap < Integer.MAX_VALUE) {
argMap.put("instanceCap", instanceCap);
}
if (!StringUtils.isEmpty(supplementalGroups)){
@@ -328,5 +365,40 @@ public Map getAsArgs() {
@OptionalExtension(requirePlugins = "pipeline-model-extensions")
@Symbol("kubernetes")
public static class DescriptorImpl extends DeclarativeAgentDescriptor {
+
+ static final String[] POD_TEMPLATE_FIELDS = {"namespace", "inheritFrom", "yaml", "instanceCap", "podRetention", "supplementalGroups", "idleMinutes", "activeDeadlineSeconds", "serviceAccount", "nodeSelector", "workingDir", "workspaceVolume"};
+
+ public DescriptorImpl() {
+ for (String field: new String[] {"cloud", "label"}) {
+ addHelpFileRedirect(field, PodTemplateStep.class, field);
+ }
+ for (String field: POD_TEMPLATE_FIELDS) {
+ addHelpFileRedirect(field, PodTemplate.class, field);
+ }
+ }
+
+ @Nonnull
+ @Override
+ public String getDisplayName() {
+ return Messages.KubernetesDeclarativeAgent_displayName();
+ }
+
+ @SuppressWarnings("unused") // by stapler/jelly
+ public ListBoxModel doFillCloudItems() {
+ return ExtensionList.lookupSingleton(PodTemplateStep.DescriptorImpl.class).doFillCloudItems();
+ }
+
+ @SuppressWarnings("unused") // by stapler/jelly
+ public ListBoxModel doFillInheritFromItems(@QueryParameter("cloud") String cloudName) {
+ return ExtensionList.lookupSingleton(PodTemplateStep.DescriptorImpl.class).doFillInheritFromItems(cloudName);
+ }
+
+ public PodRetention getDefaultPodRetention() {
+ return PodRetention.getPodTemplateDefault();
+ }
+
+ public WorkspaceVolume getDefaultWorkspaceVolume() {
+ return WorkspaceVolume.getDefault();
+ }
}
}
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep.java
index 29f80a4310..733f9192b2 100755
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep.java
@@ -5,10 +5,17 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import com.google.common.collect.ImmutableSet;
+import hudson.model.Job;
+import hudson.slaves.Cloud;
+import hudson.util.ListBoxModel;
+import jenkins.model.Jenkins;
+import org.apache.commons.lang.StringUtils;
import org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate;
+import org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud;
import org.csanchez.jenkins.plugins.kubernetes.PodAnnotation;
import org.csanchez.jenkins.plugins.kubernetes.PodTemplate;
import org.csanchez.jenkins.plugins.kubernetes.model.TemplateEnvVar;
@@ -28,16 +35,16 @@
import hudson.model.Node;
import hudson.model.Run;
import hudson.model.TaskListener;
+import org.kohsuke.stapler.QueryParameter;
+
import javax.annotation.CheckForNull;
public class PodTemplateStep extends Step implements Serializable {
private static final long serialVersionUID = 5588861066775717487L;
- private static final String DEFAULT_CLOUD = "kubernetes";
-
@CheckForNull
- private String cloud = DEFAULT_CLOUD;
+ private String cloud;
@CheckForNull
private String inheritFrom;
@@ -384,6 +391,47 @@ public void setSupplementalGroups(@CheckForNull String supplementalGroups) {
@Extension
public static class DescriptorImpl extends StepDescriptor {
+ static final String[] POD_TEMPLATE_FIELDS = {"name", "namespace", "inheritFrom", "containers", "envVars", "volumes", "annotations", "yaml", "showRawYaml", "instanceCap", "podRetention", "supplementalGroups", "idleMinutes", "activeDeadlineSeconds", "serviceAccount", "nodeSelector", "workingDir", "workspaceVolume"};
+
+ public DescriptorImpl() {
+ for (String field : POD_TEMPLATE_FIELDS) {
+ addHelpFileRedirect(field, PodTemplate.class, field);
+ }
+ }
+
+ @SuppressWarnings("unused") // by stapler/jelly
+ public ListBoxModel doFillCloudItems() {
+ ListBoxModel result = new ListBoxModel();
+ result.add("—any—", "");
+ Jenkins.get().clouds
+ .getAll(KubernetesCloud.class)
+ .forEach(cloud -> result.add(cloud.name));
+ return result;
+ }
+
+ @SuppressWarnings("unused") // by stapler/jelly
+ public ListBoxModel doFillInheritFromItems(@QueryParameter("cloud") String cloudName) {
+ cloudName = Util.fixEmpty(cloudName);
+ ListBoxModel result = new ListBoxModel();
+ result.add("—Default inheritance—", "");
+ result.add("—Disable inheritance—", " ");
+ Cloud cloud;
+ if (cloudName == null) {
+ cloud = Jenkins.get().clouds.get(KubernetesCloud.class);
+ } else {
+ cloud = Jenkins.get().getCloud(cloudName);
+ }
+ if (cloud instanceof KubernetesCloud) {
+ List templates = ((KubernetesCloud) cloud).getTemplates();
+ result.addAll(templates.stream()
+ .filter(template -> StringUtils.isNotEmpty(template.getName()))
+ .map(PodTemplate::getName)
+ .map(ListBoxModel.Option::new)
+ .collect(Collectors.toList()));
+ }
+ return result;
+ }
+
@Override
public String getFunctionName() {
return "podTemplate";
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStepExecution.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStepExecution.java
index 70b738d4ec..e8f0f2fb30 100755
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStepExecution.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStepExecution.java
@@ -144,11 +144,6 @@ public boolean start() throws Exception {
throw new AbortException(Messages.RFC1123_error(String.join(", ", errors)));
}
- // Note that after JENKINS-51248 this must be a single label atom, not a space-separated list, unlike PodTemplate.label generally.
- if (!PodTemplateUtils.validateLabel(newTemplate.getLabel())) {
- throw new AbortException(Messages.label_error(newTemplate.getLabel()));
- }
-
cloud.addDynamicTemplate(newTemplate);
BodyInvoker invoker = getContext().newBodyInvoker().withContexts(step, new PodTemplateContext(namespace, name)).withCallback(new PodTemplateCallback(newTemplate));
if (step.getLabel() == null) {
@@ -259,12 +254,12 @@ private PodTemplateCallback(PodTemplate podTemplate) {
protected void finished(StepContext context) throws Exception {
Cloud cloud = Jenkins.get().getCloud(cloudName);
if (cloud == null) {
- LOGGER.log(Level.WARNING, "Cloud {0} no longer exists, cannot delete pod template {1}",
+ LOGGER.log(Level.FINE, "Cloud {0} no longer exists, cannot delete pod template {1}",
new Object[] { cloudName, podTemplate.getName() });
return;
}
if (cloud instanceof KubernetesCloud) {
- LOGGER.log(Level.INFO, "Removing pod template {1} from cloud {0}",
+ LOGGER.log(Level.FINE, "Removing pod template {1} from cloud {0}",
new Object[] { cloud.name, podTemplate.getName() });
KubernetesCloud kubernetesCloud = (KubernetesCloud) cloud;
kubernetesCloud.removeDynamicTemplate(podTemplate);
diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java
index 821264feef..4faf27182c 100644
--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java
+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java
@@ -26,6 +26,8 @@
import hudson.model.Result;
import hudson.model.TaskListener;
import hudson.model.listeners.ItemListener;
+import hudson.security.ACL;
+import hudson.security.ACLContext;
import hudson.slaves.Cloud;
import hudson.slaves.ComputerListener;
import hudson.slaves.EphemeralNode;
@@ -269,13 +271,8 @@ public void onEvent(@NonNull Action action, @NonNull KubernetesSlave node, @NonN
TaskListener runListener = node.getTemplate().getListener();
runListener.error("Unable to pull Docker image \""+cs.getImage()+"\". Check if image tag name is spelled correctly.");
});
- Queue q = Jenkins.get().getQueue();
- String runUrl = pod.getMetadata().getAnnotations().get("runUrl");
- for (Queue.Item item: q.getItems()) {
- if (item.task.getUrl().equals(runUrl)) {
- q.cancel(item);
- break;
- }
+ try (ACLContext _ = ACL.as(ACL.SYSTEM)) {
+ PodUtils.cancelQueueItemFor(pod, "ImagePullBackOff");
}
node.terminate();
}
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-failureThreshold.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-failureThreshold.html
new file mode 100644
index 0000000000..79fe0783c0
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-failureThreshold.html
@@ -0,0 +1,5 @@
+
+ When a Pod starts and the probe fails, Kubernetes will try failureThreshold times before giving up.
+ Giving up in case of liveness probe means restarting the container.
+ In case of readiness probe the Pod will be marked Unready. Defaults to 3. Minimum value is 1.
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-initialDelaySeconds.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-initialDelaySeconds.html
new file mode 100644
index 0000000000..04958b3a23
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-initialDelaySeconds.html
@@ -0,0 +1 @@
+Number of seconds after the container has started before liveness or readiness probes are initiated. Defaults to 0 seconds. Minimum value is 0.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-periodSeconds.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-periodSeconds.html
new file mode 100644
index 0000000000..ec42d0a935
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-periodSeconds.html
@@ -0,0 +1,3 @@
+
+ How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-successThreshold.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-successThreshold.html
new file mode 100644
index 0000000000..1f7cd6e5d2
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-successThreshold.html
@@ -0,0 +1 @@
+Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-timeoutSeconds.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-timeoutSeconds.html
new file mode 100644
index 0000000000..9910930063
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerLivenessProbe/help-timeoutSeconds.html
@@ -0,0 +1 @@
+Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/config.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/config.jelly
index 44008cc4d3..33f73951a2 100644
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/config.jelly
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/config.jelly
@@ -18,7 +18,7 @@
-
+
@@ -34,7 +34,7 @@
-
+
@@ -75,7 +75,7 @@
-
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/config_zh_CN.properties b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/config_zh_CN.properties
index fb72416bc0..32d471f7c6 100644
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/config_zh_CN.properties
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/config_zh_CN.properties
@@ -36,7 +36,7 @@ Request\ Memory=\u5185\u5B58\u9700\u6C42
Limit\ CPU=CPU \u9650\u5236
Limit\ Memory=\u5185\u5B58\u9650\u5236
Liveness\ Probe=\u5065\u5EB7\u68C0\u67E5
-PortMappings=\u7AEF\u53E3\u6620\u5C04
+Port\ Mappings=\u7AEF\u53E3\u6620\u5C04
List\ of\ exposed\ ports=\u66B4\u9732\u7684\u7AEF\u53E3\u5217\u8868
Add\ Port\ Mapping=\u6DFB\u52A0\u7AEF\u53E3\u6620\u5C04
Delete\ Port\ Mapping=\u5220\u9664\u7AEF\u53E3\u6620\u5C04
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-alwaysPullImage.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-alwaysPullImage.html
new file mode 100644
index 0000000000..f8e3eb1042
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-alwaysPullImage.html
@@ -0,0 +1,3 @@
+If ticked, the latest version of the image will be pulled every time it is used.
+
+See Images - Kubernetes for the default Kubernetes behaviour.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-ttyEnabled.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-ttyEnabled.html
new file mode 100644
index 0000000000..5abe303de3
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-ttyEnabled.html
@@ -0,0 +1 @@
+Whether this container should allocate a TTY for itself.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-workingDir.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-workingDir.html
new file mode 100644
index 0000000000..a55ab18305
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/ContainerTemplate/help-workingDir.html
@@ -0,0 +1 @@
+Path to the root of the workspace from the view point of this container, such as /home/jenkins/agent.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/container.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/container.jelly
index e4f4896515..f4c58736c9 100644
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/container.jelly
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/container.jelly
@@ -24,7 +24,7 @@ THE SOFTWARE.
-
+
@@ -34,4 +34,4 @@ THE SOFTWARE.
-
\ No newline at end of file
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/events.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/events.jelly
index 62a837899b..c3ea376d11 100644
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/events.jelly
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/events.jelly
@@ -24,7 +24,7 @@ THE SOFTWARE.
-
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/podLog.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/podLog.jelly
index 7899433981..f1acdbd19f 100644
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/podLog.jelly
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer/podLog.jelly
@@ -24,7 +24,7 @@ THE SOFTWARE.
-
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-containers.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-containers.html
new file mode 100644
index 0000000000..81ae7b0249
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-containers.html
@@ -0,0 +1,2 @@
+By default, the pod contains a single container with name jnlp running the Jenkins agent.
+It is possible to override this container by adding a new entry and use jnlp as name.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-envVars.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-envVars.html
new file mode 100644
index 0000000000..7c35db8b89
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-envVars.html
@@ -0,0 +1 @@
+Environment variables can be hardcoded, or injected from a Kubernetes secret.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-hostNetwork.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-hostNetwork.html
new file mode 100644
index 0000000000..312712bb78
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-hostNetwork.html
@@ -0,0 +1 @@
+Allows the pod to share the host network namespace. Not recommended.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-idleMinutes.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-idleMinutes.html
new file mode 100644
index 0000000000..028fea0c62
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-idleMinutes.html
@@ -0,0 +1,8 @@
+
+ By default, agents are terminated as soon as they have completed the task they have been assigned.
+
+
+
+ Setting a value for this field will keep agents around for N minutes (N being the defined value).
+ In that case, the agent may be reused by another build.
+
diff --git a/src/main/webapp/help/imagePullSecrets.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-imagePullSecrets.html
similarity index 82%
rename from src/main/webapp/help/imagePullSecrets.html
rename to src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-imagePullSecrets.html
index abc3b3dcb3..fcff57c8ce 100755
--- a/src/main/webapp/help/imagePullSecrets.html
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-imagePullSecrets.html
@@ -13,6 +13,4 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-The list of secrets. Secrets are passed in the form of key/value pairs, where key represents the secret name and value
-the mount path.
\ No newline at end of file
+Name of secrets that can be used to pull the specified image.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-inheritFrom.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-inheritFrom.html
new file mode 100644
index 0000000000..4bb769730c
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-inheritFrom.html
@@ -0,0 +1,19 @@
+
+ A podTemplate may or may not inherit from an existing template.
+ This means that the podTemplate will inherit node selector, service account, image pull secrets, containerTemplates and volumes from the template it inherits from.
+
+
+ Service account and Node selector, when overridden, completely replace any value found on the “parent”.
+
+
+ Container templates that are added to the podTemplate, that has a matching containerTemplate (a containerTemplate with the same name) in the parent template, will inherit the configuration of the parent containerTemplate.
+ If no matching containerTemplate is found, the template is added as is.
+
+ Volume inheritance works exactly as Container templates.
+
+ Image Pull Secrets are combined (all secrets defined both on 'parent' and 'current' template are used).
+
+
+ By default, pod template inherits outer pod template definitions.
+ Inheritance can be stopped by using the empty string.
+
diff --git a/src/main/webapp/help/instanceCap.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-instanceCap.html
similarity index 58%
rename from src/main/webapp/help/instanceCap.html
rename to src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-instanceCap.html
index 82dfc8a9e6..ac1f0c0cf7 100644
--- a/src/main/webapp/help/instanceCap.html
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-instanceCap.html
@@ -1,5 +1,5 @@
-
-The maximum number of concurrently running agent pods created from this template that are permitted in the Kubernetes Cloud.
-The number of running agents will never exceed the global concurrency limit sets at the Cloud Configuration level.
+
+The maximum number of concurrently running agent pods created from this template that are permitted in the Kubernetes Cloud.
+The number of running agents will never exceed the global concurrency limit sets at the Cloud Configuration level.
If set to empty or negative number it means no limit.
-
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-label.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-label.html
new file mode 100644
index 0000000000..095e88c8a7
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-label.html
@@ -0,0 +1 @@
+Labels to put on the created agents
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-name.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-name.html
similarity index 100%
rename from src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-name.html
rename to src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-name.html
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-namespace.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-namespace.html
new file mode 100644
index 0000000000..6215d744e2
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-namespace.html
@@ -0,0 +1,3 @@
+Namespace in which to schedule the pod.
+
+Leave empty to use the namespace defined at cloud level.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/form/taglib b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-nodeProperties.html
similarity index 100%
rename from src/main/resources/org/csanchez/jenkins/plugins/kubernetes/form/taglib
rename to src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-nodeProperties.html
diff --git a/src/main/webapp/help/nodeSelector.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-nodeSelector.html
similarity index 100%
rename from src/main/webapp/help/nodeSelector.html
rename to src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-nodeSelector.html
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-podRetention.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-podRetention.html
index 3678135308..881c9b46d9 100644
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-podRetention.html
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-podRetention.html
@@ -1,6 +1,6 @@
- This setting controls how slave pods are retained after the Jenkins build completes for this pod template.
+ This setting controls how agent pods are retained after the Jenkins build completes for this pod template.
Values other than "Default" will override the plugin's Pod Retention setting.
The following retention policies are provided:
@@ -11,7 +11,7 @@
On Failure - keep the slave pod if it fails during the build.
- Note: Kubernetes administrators are responsible for managing any kept slave pod.
+ Note: Kubernetes administrators are responsible for managing any kept agent pod.
These will not be deleted by the Jenkins Kubernetes plugin.
-
\ No newline at end of file
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-serviceAccount.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-serviceAccount.html
new file mode 100755
index 0000000000..a62439a98e
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-serviceAccount.html
@@ -0,0 +1 @@
+The service account to use to run the pod.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-showRawYaml.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-showRawYaml.html
new file mode 100644
index 0000000000..c313bd28e9
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-showRawYaml.html
@@ -0,0 +1,5 @@
+
+ When checked, the actual pod definition in YAML will be dumped in the build console (secrets redacted).
+ This helps audit builds to understand what was the exact environment when the build ran.
+ If you don't care about this information and want less verbosity in build logs, disable this feature.
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-slaveConnectTimeout.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-slaveConnectTimeout.html
new file mode 100644
index 0000000000..716e03c7dc
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-slaveConnectTimeout.html
@@ -0,0 +1,4 @@
+
+ Specify time in seconds up to which Jenkins should wait for the JNLP agent to establish a connection.
+ Value must be a positive integer.
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-slaveConnectTimeoutStr.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-slaveConnectTimeoutStr.html
deleted file mode 100644
index 10c84bf184..0000000000
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-slaveConnectTimeoutStr.html
+++ /dev/null
@@ -1,2 +0,0 @@
-Specify time in seconds up to which Jenkins should wait for the JNLP agent to
-estabilish a connection. Value should be a positive integer, default being 100.
diff --git a/src/main/webapp/help/supplementalGroups.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-supplementalGroups.html
similarity index 100%
rename from src/main/webapp/help/supplementalGroups.html
rename to src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-supplementalGroups.html
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-volumes.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-volumes.html
new file mode 100644
index 0000000000..8b01bb7146
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-volumes.html
@@ -0,0 +1,4 @@
+
+ Volumes get mounted in all containers with the specified mount path.
+ If you want more fine-grained control, use the raw YAML field.
+
diff --git a/src/main/webapp/help/workingDir.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-workingDir.html
similarity index 100%
rename from src/main/webapp/help/workingDir.html
rename to src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-workingDir.html
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-workspaceVolume.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-workspaceVolume.html
new file mode 100644
index 0000000000..d56a637298
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-workspaceVolume.html
@@ -0,0 +1 @@
+Specifies the type of volume to be used to mount the agent workspace.
diff --git a/src/main/webapp/help/yaml.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-yaml.html
similarity index 59%
rename from src/main/webapp/help/yaml.html
rename to src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-yaml.html
index 99f376aed6..fa51bab9e2 100644
--- a/src/main/webapp/help/yaml.html
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/PodTemplate/help-yaml.html
@@ -15,25 +15,25 @@
-->
-The raw yaml of a Pod API Object. Any pod fields set with non-empty values via the configuration fields above will generally take precedence
-as they are merged with this yaml, though in the case of arrays (like Volumes or Tolerations) the objects in the yaml will be appeneded to the
-existing list.
+ The raw yaml of a Pod API Object. Any pod fields set with non-empty values via the configuration fields above will generally take precedence
+ as they are merged with this yaml, though in the case of arrays (like Volumes or Tolerations) the objects in the yaml will be appended to the
+ existing list.
-Fragments of a full Pod API Object yaml representation are allowed, but you must specify enough of the structure to give context
-for the object construction.
+ Fragments of a full Pod API Object yaml representation are allowed, but you must specify enough of the structure to give context
+ for the object construction.
-For example, you can start with:
+ For example, you can start with:
-
+
apiVersion: v1
kind: Pod
spec:
....
-
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/form/checkbox.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/form/checkbox.jelly
deleted file mode 100644
index e937522b87..0000000000
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/form/checkbox.jelly
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
-
- <input type="checkbox"> tag that takes true/false for @checked, which is more Jelly friendly.
-
-
-
-
-
- Normally, the submitted JSON will be boolean indicating whether the checkbox was checked or not.
- This is sometimes inconvenient if you have a UI that lets user select a subset of a set.
- If this attribute is present, the submitted JSON will have this as a string value if the checkbox is checked,
- and none otherwise, making the subset selection easier.
-
-
- The default value of the check box, in case both @checked and @instance are null.
- If this attribute is unspecified or null, it defaults to unchecked, otherwise checked.
-
-
-
-
-
-
- If set to true, this will take precedence over the onclick attribute and prevent the state of the checkbox from being changed.
-
-
- Used for databinding. TBD.
-
-
- If specified, this human readable text will follow the checkbox, and clicking this text also
- toggles the checkbox.
-
-
- Used as tooltip of the checkbox, and, if a title is specified, of the title
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${customizedFields.add(name)}
-
-
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/config.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/config.jelly
new file mode 100644
index 0000000000..44873cbaa0
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/config.jelly
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/help-customWorkspace.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/help-customWorkspace.html
new file mode 100644
index 0000000000..debc7789c3
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/help-customWorkspace.html
@@ -0,0 +1 @@
+Allows the SCM repository to be checked out in a custom workspace directory.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentScript.groovy b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentScript.groovy
index 4371e2b610..d269b7ed96 100644
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentScript.groovy
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentScript.groovy
@@ -44,7 +44,7 @@ public class KubernetesDeclarativeAgentScript extends DeclarativeAgentScript
-
+
@@ -22,29 +22,31 @@
${it.description}
-
-
+
+
-
+
-
+
+
+
+
+
-
+
-
+
@@ -53,8 +55,7 @@
-
+
@@ -66,23 +67,20 @@
-
+
-
+
-
+
-
+
-
+
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-cloud.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-cloud.html
new file mode 100644
index 0000000000..3afe31fdd2
--- /dev/null
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-cloud.html
@@ -0,0 +1,2 @@
+The Kubernetes cloud to use to schedule the pod.
+If unset, the first available Kubernetes cloud will be used.
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-label.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-label.html
index 862de7a80f..6ee30ed7cb 100644
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-label.html
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help-label.html
@@ -2,5 +2,14 @@
Jenkins node label to bind.
If left blank, one will be generated for you,
and inside the step it will be bound to the variable POD_LABEL
- so you can use this as the argument to the node step.
+ so you can use this as the argument to the node step.
+
+ Example:
+
- This setting controls how slave pods are retained after the Jenkins build completes for this pod template.
- Values other than "default" will override the plugin's Pod Retention setting.
- The following retention policies are supported:
-
-
-
default - use the Pod Retention setting for the plugin.
-
Never - always delete the slave pod.
-
On Failure - keep the slave pod if it fails during the build.
-
Always - always keep the slave pod.
-
-
- Note: Kubernetes administrators are responsible for managing any kept slave pod.
- These will not be deleted by the Jenkins Kubernetes plugin.
-
-
\ No newline at end of file
diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help.html b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help.html
index 1fbf9e09ea..2152a094e1 100755
--- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help.html
+++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/PodTemplateStep/help.html
@@ -1,3 +1,13 @@
-
- Defines a container inside the kubernetes plugin configuration
-
+
+ Defines a Kubernetes pod template that can be used to create nodes.
+