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 3f15a344f..fbaf4f58d 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtils.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtils.java @@ -14,6 +14,7 @@ import hudson.model.Label; import hudson.model.Node; import hudson.slaves.NodeProperty; +import io.fabric8.kubernetes.api.model.Capabilities; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.ContainerBuilder; import io.fabric8.kubernetes.api.model.EnvFromSource; @@ -184,6 +185,7 @@ public static Container combine(@CheckForNull Container parent, @NonNull Contain : (parent.getSecurityContext() != null ? parent.getSecurityContext().getRunAsGroup() : null); + Capabilities capabilities = combineCapabilities(parent, template); String imagePullPolicy = isNullOrEmpty(template.getImagePullPolicy()) ? parent.getImagePullPolicy() : template.getImagePullPolicy(); @@ -219,12 +221,13 @@ public static Container combine(@CheckForNull Container parent, @NonNull Contain .withEnv(combineEnvVars(parent, template)) // .withEnvFrom(combinedEnvFromSources(parent, template)) .withVolumeMounts(new ArrayList<>(volumeMounts.values())); - if ((privileged != null && privileged) || runAsUser != null || runAsGroup != null) { + if ((privileged != null && privileged) || runAsUser != null || runAsGroup != null || capabilities != null) { containerBuilder = containerBuilder - .withNewSecurityContext() + .withNewSecurityContextLike(parent.getSecurityContext()) .withPrivileged(privileged) .withRunAsUser(runAsUser) .withRunAsGroup(runAsGroup) + .withCapabilities(capabilities) .endSecurityContext(); } return containerBuilder.build(); @@ -245,6 +248,41 @@ private static Map combineResources( ); } + private static Capabilities combineCapabilities(Container parent, Container template) { + Capabilities parentCapabilities = parent.getSecurityContext() != null + ? parent.getSecurityContext().getCapabilities() + : null; + Capabilities templateCapabilities = template.getSecurityContext() != null + ? template.getSecurityContext().getCapabilities() + : null; + if (parentCapabilities == null && templateCapabilities == null) { + return null; + } + if (parentCapabilities == null) { + return templateCapabilities; + } + if (templateCapabilities == null) { + return parentCapabilities; + } + Capabilities combined = new Capabilities(); + combined.setAdd(combineCapabilities(parentCapabilities, templateCapabilities, Capabilities::getAdd)); + combined.setDrop(combineCapabilities(parentCapabilities, templateCapabilities, Capabilities::getDrop)); + return combined; + } + + private static List combineCapabilities( + Capabilities parentCapabilities, + Capabilities templateCapabilities, + Function> capabilitiesListFunction) { + List parentCapabilitiesList = capabilitiesListFunction.apply(parentCapabilities); + List templateCapabilitiesList = capabilitiesListFunction.apply(templateCapabilities); + // override: template capabilities win + if (templateCapabilitiesList != null) { + return templateCapabilitiesList; + } + return parentCapabilitiesList; + } + /** * Combines all given pods together in order. * @param pods the pods to combine @@ -293,6 +331,10 @@ public static Pod combine(Pod parent, Pod template) { ? template.getSpec().getHostNetwork() : parent.getSpec().getHostNetwork(); + Boolean shareProcessNamespace = template.getSpec().getShareProcessNamespace() != null + ? template.getSpec().getShareProcessNamespace() + : parent.getSpec().getShareProcessNamespace(); + Map podAnnotations = mergeMaps( parent.getMetadata().getAnnotations(), template.getMetadata().getAnnotations()); Map podLabels = mergeMaps( @@ -346,6 +388,7 @@ public static Pod combine(Pod parent, Pod template) { .withServiceAccountName(serviceAccountName) // .withSchedulerName(schedulerName) .withHostNetwork(hostNetwork) // + .withShareProcessNamespace(shareProcessNamespace) // .withContainers(combinedContainers) // .withInitContainers(combinedInitContainers) // .withVolumes(combinedVolumes) // diff --git a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtilsTest.java b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtilsTest.java index 7858ebba3..e11b9ac63 100644 --- a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtilsTest.java +++ b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplateUtilsTest.java @@ -982,4 +982,137 @@ public void shouldSanitizeJenkinsLabel() { "ylooooooooooooooooooooooooooooonglabelendinginunderscorex", sanitizeLabel("label1 label2 verylooooooooooooooooooooooooooooonglabelendinginunderscore_")); } + + @Test + public void shouldCombineCapabilities() { + Container container1 = containerBuilder() + .withNewSecurityContext() + .withNewCapabilities() + .addToAdd("TO_ADD") + .withDrop((List) null) + .endCapabilities() + .withRunAsUser(1000L) + .endSecurityContext() + .build(); + Container container2 = containerBuilder() + .withNewSecurityContext() + .withNewCapabilities() + .addToDrop("TO_DROP") + .withAdd((List) null) + .endCapabilities() + .endSecurityContext() + .build(); + Container container3 = containerBuilder().build(); + + Container result = combine(container1, container3); + assertNotNull(result.getSecurityContext()); + assertNotNull(result.getSecurityContext().getCapabilities()); + assertTrue(result.getSecurityContext().getCapabilities().getAdd().contains("TO_ADD")); + + result = combine(container3, container1); + assertNotNull(result.getSecurityContext()); + assertNotNull(result.getSecurityContext().getCapabilities()); + assertTrue(result.getSecurityContext().getCapabilities().getAdd().contains("TO_ADD")); + + result = combine(container2, container3); + assertNotNull(result.getSecurityContext()); + assertNotNull(result.getSecurityContext().getCapabilities()); + assertTrue(result.getSecurityContext().getCapabilities().getDrop().contains("TO_DROP")); + + result = combine(container1, container2); + assertNotNull(result.getSecurityContext()); + assertNotNull(result.getSecurityContext().getCapabilities()); + assertTrue(result.getSecurityContext().getCapabilities().getAdd().contains("TO_ADD")); + assertTrue(result.getSecurityContext().getCapabilities().getDrop().contains("TO_DROP")); + } + + @Test + public void shouldOverrideCapabilitiesWithTemplate() { + Container container1 = containerBuilder() + .withNewSecurityContext() + .withNewCapabilities() + .addToAdd("CONTAINER1_ADD") + .addToDrop("CONTAINER1_DROP") + .endCapabilities() + .endSecurityContext() + .build(); + Container container2 = containerBuilder() + .withNewSecurityContext() + .withNewCapabilities() + .addToAdd("CONTAINER2_ADD") + .addToDrop("CONTAINER2_DROP") + .endCapabilities() + .endSecurityContext() + .build(); + + Container result = combine(container1, container2); + assertNotNull(result.getSecurityContext()); + assertNotNull(result.getSecurityContext().getCapabilities()); + assertTrue(result.getSecurityContext().getCapabilities().getAdd().contains("CONTAINER2_ADD")); + assertTrue(result.getSecurityContext().getCapabilities().getDrop().contains("CONTAINER2_DROP")); + } + + @Test + public void shouldRetainNullsWhenCombiningCapabilities() { + + Container container1 = new ContainerBuilder().build(); + Container container2 = new ContainerBuilder().build(); + Container container3 = new ContainerBuilder() + .withNewSecurityContext() + .withPrivileged() + .endSecurityContext() + .build(); + + Container result = combine(container1, container2); + assertNull(result.getSecurityContext()); + + result = combine(container2, container3); + assertNotNull(result.getSecurityContext()); + assertNull(result.getSecurityContext().getCapabilities()); + } + + @Test + public void shouldOverrideShareProcessNamespaceIfSpecified() { + Pod parent1 = new PodBuilder() + .withNewMetadata() + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + + Pod parent2 = new PodBuilder() + .withNewMetadata() + .endMetadata() + .withNewSpec() + .withShareProcessNamespace() + .endSpec() + .build(); + + Pod child1 = new PodBuilder() + .withNewMetadata() + .endMetadata() + .withNewSpec() + .withShareProcessNamespace(false) + .endSpec() + .build(); + + Pod child2 = new PodBuilder() + .withNewMetadata() + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + + Pod result1 = combine(parent1, child1); + assertFalse(result1.getSpec().getShareProcessNamespace()); + + Pod result2 = combine(parent1, child2); + assertNull(result2.getSpec().getShareProcessNamespace()); + + Pod result3 = combine(parent2, child1); + assertFalse(result3.getSpec().getShareProcessNamespace()); + + Pod result4 = combine(parent2, child2); + assertTrue(result4.getSpec().getShareProcessNamespace()); + } }