Skip to content

Commit ecdba34

Browse files
authored
Merge pull request jenkinsci#1690 from cronik/feature/ephemeral-containers-pod-utils
Extract pod name generation functions
2 parents 345364d + b23a29c commit ecdba34

File tree

3 files changed

+120
-13
lines changed

3 files changed

+120
-13
lines changed

src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlave.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import java.io.IOException;
3535
import java.time.Instant;
3636
import java.util.HashSet;
37-
import java.util.Locale;
3837
import java.util.Objects;
3938
import java.util.Optional;
4039
import java.util.Set;
@@ -47,7 +46,6 @@
4746
import jenkins.metrics.api.Metrics;
4847
import jenkins.model.Jenkins;
4948
import jenkins.security.MasterToSlaveCallable;
50-
import org.apache.commons.lang.RandomStringUtils;
5149
import org.apache.commons.lang.StringUtils;
5250
import org.apache.commons.lang.Validate;
5351
import org.csanchez.jenkins.plugins.kubernetes.pod.retention.PodRetention;
@@ -302,19 +300,16 @@ private static KubernetesCloud getKubernetesCloud(String cloudName) {
302300
}
303301

304302
static String getSlaveName(PodTemplate template) {
305-
String randString = RandomStringUtils.random(5, "bcdfghjklmnpqrstvwxz0123456789");
306303
String name = template.getName();
307304
if (StringUtils.isEmpty(name)) {
308-
return String.format("%s-%s", DEFAULT_AGENT_PREFIX, randString);
305+
name = DEFAULT_AGENT_PREFIX;
309306
}
310-
// no spaces
311-
name = name.replaceAll("[ _]", "-").toLowerCase(Locale.getDefault());
312-
// keep it under 63 chars (62 is used to account for the '-')
313-
name = name.substring(0, Math.min(name.length(), 62 - randString.length()));
314-
String slaveName = String.format("%s-%s", name, randString);
315-
if (!slaveName.matches("[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*")) {
316-
return String.format("%s-%s", DEFAULT_AGENT_PREFIX, randString);
307+
308+
String slaveName = PodUtils.createNameWithRandomSuffix(name);
309+
if (!PodUtils.isValidName(slaveName)) {
310+
slaveName = PodUtils.createNameWithRandomSuffix(DEFAULT_AGENT_PREFIX);
317311
}
312+
318313
return slaveName;
319314
}
320315

src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodUtils.java

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@
2929
import java.util.Arrays;
3030
import java.util.Collections;
3131
import java.util.List;
32+
import java.util.Locale;
3233
import java.util.Optional;
3334
import java.util.function.Predicate;
3435
import java.util.logging.Level;
3536
import java.util.logging.Logger;
37+
import java.util.regex.Pattern;
3638
import java.util.stream.Collectors;
3739
import jenkins.model.Jenkins;
40+
import org.apache.commons.lang.RandomStringUtils;
3841
import org.apache.commons.lang.StringUtils;
3942
import org.csanchez.jenkins.plugins.kubernetes.pipeline.PodTemplateStepExecution;
4043

@@ -43,6 +46,9 @@ private PodUtils() {}
4346

4447
private static final Logger LOGGER = Logger.getLogger(PodUtils.class.getName());
4548

49+
private static final Pattern NAME_PATTERN =
50+
Pattern.compile("[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*");
51+
4652
public static final Predicate<ContainerStatus> CONTAINER_IS_TERMINATED =
4753
cs -> cs.getState().getTerminated() != null;
4854
public static final Predicate<ContainerStatus> CONTAINER_IS_WAITING =
@@ -57,15 +63,27 @@ public static List<ContainerStatus> getWaitingContainers(Pod pod) {
5763
return getContainers(pod, CONTAINER_IS_WAITING);
5864
}
5965

60-
public static List<ContainerStatus> getContainerStatus(Pod pod) {
66+
/**
67+
* Get all container statuses (does not include ephemeral or init containers).
68+
* @param pod pod to get container statuses for
69+
* @return list of statuses, possibly empty, never null
70+
*/
71+
@NonNull
72+
public static List<ContainerStatus> getContainerStatus(@NonNull Pod pod) {
6173
PodStatus podStatus = pod.getStatus();
6274
if (podStatus == null) {
6375
return Collections.emptyList();
6476
}
6577
return podStatus.getContainerStatuses();
6678
}
6779

68-
public static List<ContainerStatus> getContainers(Pod pod, Predicate<ContainerStatus> predicate) {
80+
/**
81+
* Get pod container statuses (does not include ephemeral or init containers) that match the given filter.
82+
* @param pod pod to get container statuses for
83+
* @param predicate container status predicate
84+
* @return list of statuses, possibly empty, never null
85+
*/
86+
public static List<ContainerStatus> getContainers(@NonNull Pod pod, @NonNull Predicate<ContainerStatus> predicate) {
6987
return getContainerStatus(pod).stream().filter(predicate).collect(Collectors.toList());
7088
}
7189

@@ -182,4 +200,39 @@ public static String logLastLines(@NonNull Pod pod, @NonNull KubernetesClient cl
182200
}
183201
return Util.fixEmpty(sb.toString());
184202
}
203+
204+
/**
205+
* Generate a random string to be used as the suffix for dynamic resource names.
206+
* @return random string suitable for kubernetes resources
207+
*/
208+
@NonNull
209+
public static String generateRandomSuffix() {
210+
return RandomStringUtils.random(5, "bcdfghjklmnpqrstvwxz0123456789");
211+
}
212+
213+
/**
214+
* Create kubernetes resource name with a random suffix appended to the given base name. This method
215+
* performs some basic transforms to make the base name compliant (i.e. spaces and underscores). The
216+
* returned string will also be truncated to a max of 63 characters.
217+
* @param name base name to append to
218+
* @return resource name with random suffix and maximum length of 63 characters
219+
*/
220+
@NonNull
221+
public static String createNameWithRandomSuffix(@NonNull String name) {
222+
String suffix = generateRandomSuffix();
223+
// no spaces
224+
name = name.replaceAll("[ _]", "-").toLowerCase(Locale.getDefault());
225+
// keep it under 63 chars (62 is used to account for the '-')
226+
name = name.substring(0, Math.min(name.length(), 62 - suffix.length()));
227+
return String.join("-", name, suffix);
228+
}
229+
230+
/**
231+
* Check if the given name is a valid pod resource name. Does not validate string length.
232+
* @param name name to check
233+
* @return true if the given string contains valid pod resource name characters
234+
*/
235+
public static boolean isValidName(@NonNull String name) {
236+
return PodUtils.NAME_PATTERN.matcher(name).matches();
237+
}
185238
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.csanchez.jenkins.plugins.kubernetes;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertFalse;
5+
import static org.junit.Assert.assertTrue;
6+
7+
import java.util.HashSet;
8+
import java.util.List;
9+
import java.util.Set;
10+
import java.util.stream.IntStream;
11+
import org.apache.commons.lang.StringUtils;
12+
import org.junit.Test;
13+
14+
public class PodUtilsTest {
15+
16+
@Test
17+
public void generateRandomSuffix() {
18+
List<String> generated = IntStream.range(0, 100)
19+
.mapToObj(i -> PodUtils.generateRandomSuffix())
20+
.toList();
21+
Set<String> unique = new HashSet<>(generated);
22+
assertEquals(generated.size(), unique.size());
23+
24+
for (String suffix : generated) {
25+
assertEquals(5, suffix.length());
26+
assertTrue(PodUtils.isValidName(suffix));
27+
}
28+
}
29+
30+
@Test
31+
public void createNameWithRandomSuffix() {
32+
String name = PodUtils.createNameWithRandomSuffix("foo");
33+
assertEquals(9, name.length());
34+
assertTrue(name.startsWith("foo-"));
35+
assertTrue(PodUtils.isValidName(name));
36+
37+
// names with invalid characters
38+
name = PodUtils.createNameWithRandomSuffix("foo bar_cat");
39+
assertEquals(17, name.length());
40+
assertTrue(name.startsWith("foo-bar-cat-"));
41+
42+
// very long names
43+
name = PodUtils.createNameWithRandomSuffix(StringUtils.repeat("a", 70));
44+
assertEquals(63, name.length());
45+
assertTrue(name.startsWith(StringUtils.repeat("a", 57) + "-"));
46+
}
47+
48+
@Test
49+
public void isValidName() {
50+
assertTrue(PodUtils.isValidName("foo"));
51+
assertTrue(PodUtils.isValidName("foo-bar"));
52+
assertTrue(PodUtils.isValidName("foo.bar"));
53+
assertTrue(PodUtils.isValidName("foo.123"));
54+
assertTrue(PodUtils.isValidName("123-foo"));
55+
assertFalse(PodUtils.isValidName("foo bar"));
56+
assertFalse(PodUtils.isValidName("-foo"));
57+
assertFalse(PodUtils.isValidName(".foo"));
58+
}
59+
}

0 commit comments

Comments
 (0)