Skip to content

Commit

Permalink
Adds support for readiness probe
Browse files Browse the repository at this point in the history
  • Loading branch information
geanpalacios committed Feb 16, 2023
1 parent 2522723 commit f093342
Show file tree
Hide file tree
Showing 27 changed files with 345 additions and 1 deletion.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,14 @@ containerTemplate(name: 'busybox', image: 'busybox', command: 'sleep', args: '99
```
See [Defining a liveness command](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#defining-a-liveness-command) for more details.

### Readiness Probe Usage
```groovy
containerTemplate(name: 'busybox', image: 'busybox', command: 'sleep', args: '99d',
readinessProbe: containerReadinessProbe(execArgs: 'some --command', initialDelaySeconds: 30, timeoutSeconds: 1, failureThreshold: 3, periodSeconds: 10, successThreshold: 1)
)
```
See [Define readiness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) for more details.

# Inheritance

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

import hudson.Extension;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.Serializable;

/**
* Created by geanpalacios on 15/02/23.
*/
public class ContainerReadinessProbe extends AbstractDescribableImpl<ContainerReadinessProbe> implements Serializable {
private String execArgs;
private int timeoutSeconds;
private int initialDelaySeconds;
private int failureThreshold;
private int periodSeconds;
private int successThreshold;

@DataBoundConstructor
public ContainerReadinessProbe(String execArgs, int timeoutSeconds, int initialDelaySeconds, int failureThreshold, int periodSeconds, int successThreshold) {
this.execArgs = execArgs;
this.timeoutSeconds = timeoutSeconds;
this.initialDelaySeconds = initialDelaySeconds;
this.failureThreshold = failureThreshold;
this.periodSeconds = periodSeconds;
this.successThreshold = successThreshold;
}

public String getExecArgs() {
return execArgs;
}

public void setExecArgs(String execArgs) {
this.execArgs = execArgs;
}

public int getTimeoutSeconds() {
return timeoutSeconds;
}

public void setTimeoutSeconds(int timeoutSeconds) {
this.timeoutSeconds = timeoutSeconds;
}

public int getInitialDelaySeconds() {
return initialDelaySeconds;
}

public void setInitialDelaySeconds(int initialDelaySeconds) {
this.initialDelaySeconds = initialDelaySeconds;
}

public int getFailureThreshold() {
return failureThreshold;
}

public void setFailureThreshold(int failureThreshold) {
this.failureThreshold = failureThreshold;
}

public int getPeriodSeconds() {
return periodSeconds;
}

public void setPeriodSeconds(int periodSeconds) {
this.periodSeconds = periodSeconds;
}

public int getSuccessThreshold() {
return successThreshold;
}

public void setSuccessThreshold(int successThreshold) {
this.successThreshold = successThreshold;
}

@Override
public String toString() {
return "ContainerReadinessProbe{" +
"execArgs='" + execArgs + '\'' +
", timeoutSeconds=" + timeoutSeconds +
", initialDelaySeconds=" + initialDelaySeconds +
", failureThreshold=" + failureThreshold +
", periodSeconds=" + periodSeconds +
", successThreshold=" + successThreshold +
'}';
}

@Extension
@Symbol("containerReadinessProbe")
public static class DescriptorImpl extends Descriptor<ContainerReadinessProbe> {
@Override
public String getDisplayName() {
return "Container Readiness Probe";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class ContainerTemplate extends AbstractDescribableImpl<ContainerTemplate
private List<PortMapping> ports = new ArrayList<>();

private ContainerLivenessProbe livenessProbe;
private ContainerReadinessProbe readinessProbe;

@Deprecated
public ContainerTemplate(String image) {
Expand Down Expand Up @@ -111,6 +112,7 @@ public ContainerTemplate(ContainerTemplate from) {
this.setEnvVars(from.getEnvVars());
this.setPorts(from.getPorts());
this.setLivenessProbe(from.getLivenessProbe());
this.setReadinessProbe(from.getReadinessProbe());
}

public void setName(String name) {
Expand Down Expand Up @@ -229,6 +231,14 @@ public void setEnvVars(List<TemplateEnvVar> envVars) {
public void setLivenessProbe(ContainerLivenessProbe livenessProbe) {
this.livenessProbe = livenessProbe;
}

public ContainerReadinessProbe getReadinessProbe() { return readinessProbe; }

@DataBoundSetter
public void setReadinessProbe(ContainerReadinessProbe readinessProbe) {
this.readinessProbe = readinessProbe;
}


public List<PortMapping> getPorts() {
return ports != null ? ports : Collections.emptyList();
Expand Down Expand Up @@ -375,6 +385,7 @@ public String toString() {
(envVars == null || envVars.isEmpty() ? "" : ", envVars=" + envVars) +
(ports == null || ports.isEmpty() ? "" : ", ports=" + ports) +
(livenessProbe == null ? "" : ", livenessProbe=" + livenessProbe) +
(readinessProbe == null ? "" : ", readinessProbe=" + readinessProbe) +
'}';
}

Expand Down Expand Up @@ -446,7 +457,10 @@ public boolean equals(Object o) {
if (!Objects.equals(ports, that.ports)) {
return false;
}
return Objects.equals(livenessProbe, that.livenessProbe);
if (!Objects.equals(livenessProbe, that.livenessProbe)) {
return false;
}
return Objects.equals(readinessProbe, that.readinessProbe);
}

@Override
Expand All @@ -471,6 +485,7 @@ public int hashCode() {
result = 31 * result + (envVars != null ? envVars.hashCode() : 0);
result = 31 * result + (ports != null ? ports.hashCode() : 0);
result = 31 * result + (livenessProbe != null ? livenessProbe.hashCode() : 0);
result = 31 * result + (readinessProbe != null ? readinessProbe.hashCode() : 0);
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,19 @@ private Container createContainer(ContainerTemplate containerTemplate, Collectio
.build();
}

ContainerReadinessProbe crp = containerTemplate.getReadinessProbe();
Probe readinessProbe = null;
if (crp != null && parseReadinessProbe(crp.getExecArgs()) != null) {
readinessProbe = new ProbeBuilder()
.withExec(new ExecAction(parseReadinessProbe(crp.getExecArgs())))
.withInitialDelaySeconds(crp.getInitialDelaySeconds())
.withTimeoutSeconds(crp.getTimeoutSeconds())
.withFailureThreshold(crp.getFailureThreshold())
.withPeriodSeconds(crp.getPeriodSeconds())
.withSuccessThreshold(crp.getSuccessThreshold())
.build();
}

ContainerBuilder containerBuilder = new ContainerBuilder()
.withName(substituteEnv(containerTemplate.getName()))
.withImage(substituteEnv(containerTemplate.getImage()))
Expand All @@ -506,6 +519,7 @@ private Container createContainer(ContainerTemplate containerTemplate, Collectio
.withCommand(parseDockerCommand(containerTemplate.getCommand()))
.withArgs(arguments)
.withLivenessProbe(livenessProbe)
.withReadinessProbe(readinessProbe)
.withTty(containerTemplate.isTtyEnabled())
.withNewResources()
.withRequests(getResourcesMap(containerTemplate.getResourceRequestMemory(), containerTemplate.getResourceRequestCpu(),containerTemplate.getResourceRequestEphemeralStorage()))
Expand Down Expand Up @@ -571,6 +585,26 @@ static List<String> parseLivenessProbe(String livenessProbeExec) {
return commands;
}

/**
* Split a command in the parts that ReadinessProbe needs
*
* @param readinessProbeExec
* @return
*/
@Restricted(NoExternalUse.class)
static List<String> parseReadinessProbe(String readinessProbeExec) {
if (StringUtils.isBlank(readinessProbeExec)) {
return null;
}
// handle quoted arguments
Matcher m = SPLIT_IN_SPACES.matcher(readinessProbeExec);
List<String> commands = new ArrayList<String>();
while (m.find()) {
commands.add(substituteEnv(m.group(1).replace("\"", "").replace("?:\\\"", "")));
}
return commands;
}

private Map<String, Quantity> getResourcesMap(String memory, String cpu, String ephemeralStorage) {
Map<String, Quantity> builder = new HashMap<>();
String actualMemory = substituteEnv(memory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public static ContainerTemplate combine(@CheckForNull ContainerTemplate parent,
.collect(Collectors.toMap(PortMapping::getName, Function.identity()));
template.getPorts().stream().forEach(p -> ports.put(p.getName(), p));
ContainerLivenessProbe livenessProbe = template.getLivenessProbe() != null ? template.getLivenessProbe() : parent.getLivenessProbe();
ContainerReadinessProbe readinessProbe = template.getReadinessProbe() != null ? template.getReadinessProbe() : parent.getReadinessProbe();

ContainerTemplate combined = new ContainerTemplate(image);
combined.setName(name);
Expand All @@ -131,6 +132,7 @@ public static ContainerTemplate combine(@CheckForNull ContainerTemplate parent,
combined.setEnvVars(combineEnvVars(parent, template));
combined.setPorts(new ArrayList<>(ports.values()));
combined.setLivenessProbe(livenessProbe);
combined.setReadinessProbe(readinessProbe);
return combined;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!--
Config page
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry field="execArgs" title="${%Exec action}">
<f:textbox/>
</f:entry>

<f:entry field="initialDelaySeconds" title="${%Initial Delay Seconds}">
<f:textbox/>
</f:entry>

<f:entry field="timeoutSeconds" title="${%Timeout Seconds}">
<f:textbox/>
</f:entry>

<f:entry field="failureThreshold" title="${%Failure Threshold}">
<f:textbox/>
</f:entry>

<f:entry field="periodSeconds" title="${%Period Seconds}">
<f:textbox/>
</f:entry>

<f:entry field="successThreshold" title="${%Success Threshold}">
<f:textbox/>
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# The MIT License
#
# Copyright (c) 2018, Alauda
#
# 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.

Exec\ action=
Initial\ Delay\ Seconds=
Timeout\ Seconds=
Failure\ Threshold=
Period\ Seconds=
Success\ Threshold=
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Command executed by the readiness probe.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<p>
When a Pod starts and the probe fails, Kubernetes will try <em>failureThreshold</em> times before giving up.<br/>
Giving up in case of liveness probe means restarting the container.<br/>
In case of readiness probe the Pod will be marked Unready. Defaults to 3. Minimum value is 1.
</p>
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p>
How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Minimum value is 1.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@
<st:include from="${descriptor}" page="${descriptor.configPage}" />
</f:rowSet>
</f:entry>

<f:entry field="readinessProbe" title="${%Readiness Probe}">
<f:rowSet name="${field}">
<j:set var="descriptor" value="${attrs.propertyDescriptor ?: app.getDescriptorOrDie(descriptor.getPropertyTypeOrDie(instance,field).clazz)}" />
<j:set var="instance" value="${instance[field]}"/>
<st:include from="${descriptor}" page="${descriptor.configPage}" />
</f:rowSet>
</f:entry>

<f:entry title="${%Port Mappings}" description="${%List of exposed ports}">
<f:repeatableHeteroProperty field="ports" hasHeader="true" addCaption="${%Add Port Mapping}"
deleteCaption="${%Delete Port Mapping}" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +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
Readiness\ Probe=\u5c31\u7eea\u6001\u68c0\u67e5
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public void testCopyConstructorCreatesEqualInstance() {
originalTemplate.setEnvVars(Collections.emptyList());
originalTemplate.setPorts(Collections.emptyList());
originalTemplate.setLivenessProbe(new ContainerLivenessProbe("test", 1, 2, 3, 4, 5));
originalTemplate.setReadinessProbe(new ContainerReadinessProbe("test", 1, 2, 3, 4, 5));

ContainerTemplate clonedTemplate = new ContainerTemplate(originalTemplate);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,19 @@ public void testParseLivenessProbe() {
parseLivenessProbe("curl -k --silent --output=/dev/null \"https://localhost:8080\""));
}

@WithoutJenkins
@Test
public void testParseReadinessProbe() {
assertNull(parseReadinessProbe(""));
assertNull(parseReadinessProbe(null));
assertEquals(Collections.unmodifiableList(Arrays.asList("docker", "info")), parseReadinessProbe("docker info"));
assertEquals(Collections.unmodifiableList(Arrays.asList("echo", "I said: 'I am ready'")),
parseReadinessProbe("echo \"I said: 'I am ready'\""));
assertEquals(Collections.unmodifiableList(Arrays.asList("docker", "--version")), parseReadinessProbe("docker --version"));
assertEquals(Collections.unmodifiableList(Arrays.asList("curl", "-k", "--silent", "--output=/dev/null", "https://localhost:8080")),
parseReadinessProbe("curl -k --silent --output=/dev/null \"https://localhost:8080\""));
}

@Test
@TestCaseName("{method}(directConnection={0})")
@Parameters({ "true", "false" })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.jenkins.plugins.casc.misc.RoundTripAbstractTest;
import org.csanchez.jenkins.plugins.kubernetes.ContainerLivenessProbe;
import org.csanchez.jenkins.plugins.kubernetes.ContainerReadinessProbe;
import org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate;
import org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud;
import org.csanchez.jenkins.plugins.kubernetes.PodTemplate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,12 @@ public void runInPodWithLivenessProbe() throws Exception {
r.assertLogContains("Still alive", b);
}

@Test
public void runInPodWithReadinessProbe() throws Exception {
r.assertBuildStatusSuccess(r.waitForCompletion(b));
r.assertLogContains("Still ready", b);
}

@Test
public void podTemplateWithMultipleLabels() throws Exception {
PodTemplate pt = new PodTemplate();
Expand Down
Loading

0 comments on commit f093342

Please sign in to comment.