Skip to content

Commit cb86ca0

Browse files
committed
add pod annotations
1 parent 94883c5 commit cb86ca0

6 files changed

Lines changed: 119 additions & 2 deletions

File tree

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ public class KubernetesCloud extends Cloud implements PodTemplateGroup {
160160
private transient Map<String, String> labels;
161161

162162
private List<PodLabel> podLabels = new ArrayList<>();
163+
private List<PodAnnotation> podAnnotations = new ArrayList<>();
163164
private boolean usageRestricted;
164165

165166
private int maxRequestsPerHost;
@@ -569,6 +570,37 @@ Map<String, String> getPodLabelsMap() {
569570
return PodLabel.toMap(getPodLabels());
570571
}
571572

573+
/**
574+
* Annotations for all pods started by the plugin
575+
*/
576+
@NonNull
577+
public List<PodAnnotation> getPodAnnotations() {
578+
return podAnnotations == null ? Collections.emptyList() : podAnnotations;
579+
}
580+
581+
/**
582+
* Set Pod annotations for all pods started by the plugin.
583+
*/
584+
@DataBoundSetter
585+
public void setPodAnnotations(@CheckForNull List<PodAnnotation> annotations) {
586+
this.podAnnotations = new ArrayList<>();
587+
if (annotations != null) {
588+
this.podAnnotations.addAll(annotations);
589+
}
590+
}
591+
592+
/**
593+
* Map of annotations to add to all pods started by the plugin
594+
* @return immutable map of pod annotations
595+
*/
596+
Map<String, String> getPodAnnotationsMap() {
597+
Map<String, String> result = new HashMap<>();
598+
for (PodAnnotation annotation : getPodAnnotations()) {
599+
result.put(annotation.getKey(), annotation.getValue());
600+
}
601+
return Collections.unmodifiableMap(result);
602+
}
603+
572604
@DataBoundSetter
573605
public void setMaxRequestsPerHostStr(String maxRequestsPerHostStr) {
574606
try {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,11 @@ public Pod build() {
236236
metadataBuilder.addToLabels(LABEL_KUBERNETES_CONTROLLER, sanitizeLabel(cloud.getJenkinsUrlOrNull()));
237237
}
238238

239-
Map<String, String> annotations = getAnnotationsMap(template.getAnnotations());
239+
Map<String, String> annotations = new HashMap<>();
240+
if (agent != null) {
241+
annotations.putAll(agent.getKubernetesCloud().getPodAnnotationsMap());
242+
}
243+
annotations.putAll(getAnnotationsMap(template.getAnnotations()));
240244
if (!annotations.isEmpty()) {
241245
metadataBuilder.withAnnotations(annotations);
242246
}

src/main/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud/config.jelly

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ THE SOFTWARE.
9191
deleteCaption="${%Delete Pod Label}" />
9292
</f:entry>
9393

94+
<f:entry title="${%Pod Annotations}" field="podAnnotations">
95+
<f:repeatableHeteroProperty field="podAnnotations" hasHeader="true" addCaption="${%Add Pod Annotation}"
96+
deleteCaption="${%Delete Pod Annotation}" />
97+
</f:entry>
98+
9499
<f:dropdownDescriptorSelector title="${%Pod Retention}" field="podRetention"
95100
descriptors="${descriptor.allowedPodRetentions}"
96101
default="${descriptor.defaultPodRetention}" />
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<div>
2+
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations">Annotations</a>
3+
applied to all Pods started by this cloud. These act as a baseline; annotations defined at the pipeline or
4+
pod template level override them, and system annotations (such as the build URL) always take precedence.
5+
<p>Examples:
6+
<ul>
7+
<li><code>app.kubernetes.io/managed-by=jenkins</code></li>
8+
<li><code>cost-center=platform</code></li>
9+
<li><code>team=backend</code></li>
10+
</ul>
11+
</p>
12+
</div>

src/test/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloudTest.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,35 @@ public void testPodLabels() {
198198
assertEquals(defaultPodLabelsList, cloud.getPodLabels());
199199
}
200200

201+
@Test
202+
public void testPodAnnotations() {
203+
KubernetesCloud cloud = new KubernetesCloud("name");
204+
205+
// default is empty list
206+
assertEquals(Collections.emptyList(), cloud.getPodAnnotations());
207+
assertEquals(Collections.emptyMap(), cloud.getPodAnnotationsMap());
208+
209+
// set annotations and verify list and map
210+
List<PodAnnotation> annotations =
211+
Arrays.asList(new PodAnnotation("team", "backend"), new PodAnnotation("cost-center", "platform"));
212+
cloud.setPodAnnotations(annotations);
213+
Map<String, String> expected = new LinkedHashMap<>();
214+
expected.put("team", "backend");
215+
expected.put("cost-center", "platform");
216+
assertEquals(expected, cloud.getPodAnnotationsMap());
217+
assertEquals(annotations, cloud.getPodAnnotations());
218+
219+
// null resets to empty
220+
cloud.setPodAnnotations(null);
221+
assertEquals(Collections.emptyList(), cloud.getPodAnnotations());
222+
assertEquals(Collections.emptyMap(), cloud.getPodAnnotationsMap());
223+
224+
// empty list also results in empty
225+
cloud.setPodAnnotations(new ArrayList<>());
226+
assertEquals(Collections.emptyList(), cloud.getPodAnnotations());
227+
assertEquals(Collections.emptyMap(), cloud.getPodAnnotationsMap());
228+
}
229+
201230
@Test
202231
public void testLabels() {
203232
KubernetesCloud cloud = new KubernetesCloud("name");
@@ -228,7 +257,14 @@ public void copyConstructor() throws Exception {
228257

229258
KubernetesCloud cloud = new KubernetesCloud("name");
230259
var objectProperties = Set.of(
231-
"templates", "podRetention", "podLabels", "labels", "serverCertificate", "garbageCollection", "traits");
260+
"templates",
261+
"podRetention",
262+
"podLabels",
263+
"podAnnotations",
264+
"labels",
265+
"serverCertificate",
266+
"garbageCollection",
267+
"traits");
232268
for (String property : PropertyUtils.describe(cloud).keySet()) {
233269
if (PropertyUtils.isWriteable(cloud, property)) {
234270
Class<?> propertyType = PropertyUtils.getPropertyType(cloud, property);
@@ -256,6 +292,7 @@ public void copyConstructor() throws Exception {
256292
cloud.setPodRetention(new Always());
257293
cloud.setPodLabels(PodLabel.listOf("foo", "bar", "cat", "dog"));
258294
cloud.setLabels(Collections.singletonMap("foo", "bar"));
295+
cloud.setPodAnnotations(Arrays.asList(new PodAnnotation("team", "backend")));
259296

260297
KubernetesCloud copy = new KubernetesCloud("copy", cloud);
261298
assertEquals("copy", copy.name);

src/test/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateBuilderTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,33 @@ public void testBuildFromYaml(boolean directConnection) throws Exception {
120120
assertThat(container0.getResources().getLimits(), hasEntry("example.com/dongle", new Quantity("42")));
121121
}
122122

123+
@Test
124+
public void testCloudAnnotationsAppliedToPod() throws Exception {
125+
cloud.setPodAnnotations(
126+
Arrays.asList(new PodAnnotation("team", "backend"), new PodAnnotation("cost-center", "platform")));
127+
PodTemplate template = new PodTemplate();
128+
template.setYaml(loadYamlFile("pod-busybox.yaml"));
129+
setupStubs();
130+
Pod pod = new PodTemplateBuilder(template, slave).build();
131+
Map<String, String> annotations = pod.getMetadata().getAnnotations();
132+
assertThat(annotations, hasEntry("team", "backend"));
133+
assertThat(annotations, hasEntry("cost-center", "platform"));
134+
// system GC annotation is always present
135+
assertThat(annotations, hasKey(GarbageCollection.ANNOTATION_LAST_REFRESH));
136+
}
137+
138+
@Test
139+
public void testTemplateAnnotationsOverrideCloudAnnotations() throws Exception {
140+
cloud.setPodAnnotations(Arrays.asList(new PodAnnotation("owner", "cloud-value")));
141+
PodTemplate template = new PodTemplate();
142+
template.setYaml(loadYamlFile("pod-busybox.yaml"));
143+
template.setAnnotations(Arrays.asList(new PodAnnotation("owner", "template-value")));
144+
setupStubs();
145+
Pod pod = new PodTemplateBuilder(template, slave).build();
146+
Map<String, String> annotations = pod.getMetadata().getAnnotations();
147+
assertThat("template annotation overrides cloud annotation", annotations, hasEntry("owner", "template-value"));
148+
}
149+
123150
@Test
124151
public void testBuildJnlpFromYamlWithNullEnv() throws Exception {
125152
PodTemplate template = new PodTemplate();

0 commit comments

Comments
 (0)