diff --git a/test/groovy/DockerExecuteOnKubernetesTest.groovy b/test/groovy/DockerExecuteOnKubernetesTest.groovy index 0ee9774bf..3813d0f48 100644 --- a/test/groovy/DockerExecuteOnKubernetesTest.groovy +++ b/test/groovy/DockerExecuteOnKubernetesTest.groovy @@ -24,6 +24,7 @@ import static org.junit.Assert.assertThat import static org.junit.Assert.assertTrue import static org.junit.Assert.assertEquals import static org.junit.Assert.assertFalse +import static org.junit.Assert.assertNull class DockerExecuteOnKubernetesTest extends BasePiperTest { private ExpectedException exception = ExpectedException.none() @@ -62,6 +63,7 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest { def securityContext def inheritFrom def yamlMergeStrategy + Map resources = [:] List stashList = [] @Before @@ -71,6 +73,7 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest { envList = [] portList = [] containerCommands = [] + resources = [:] bodyExecuted = false JenkinsUtils.metaClass.static.isPluginActive = { def s -> new PluginMock(s).isActive() } helper.registerAllowedMethod('sh', [Map.class], { return whichDockerReturnValue }) @@ -104,6 +107,7 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest { containerCommands.add(container.command) } pullImageMap.put(container.image.toString(), container.imagePullPolicy == "Always") + resources.put(container.name, container.resources) } body() }) @@ -236,6 +240,164 @@ class DockerExecuteOnKubernetesTest extends BasePiperTest { assertTrue(bodyExecuted) } + @Test + void testDockerExecuteOnKubernetesNoResourceLimitsOnEmptyResourcesMap() throws Exception { + + nullScript.commonPipelineEnvironment.configuration = [general: + [jenkinsKubernetes: [ + resources: [ + DEFAULT: [ + requests: [ + memory: '1Gi', + cpu: '0.25' + ], + limits: [ + memory: '2Gi', + cpu: '1' + ] + ], + mavenexecute: [:] + ] + ] + ]] + stepRule.step.dockerExecuteOnKubernetes(script: nullScript, + containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute'], { + bodyExecuted = true + }) + + assertNull(resources.mavenexecute) + assertTrue(bodyExecuted) + } + + @Test + void testDockerExecuteOnKubernetesWithDefaultResourceLimits() throws Exception { + + nullScript.commonPipelineEnvironment.configuration = [general: + [jenkinsKubernetes: [ + resources: [DEFAULT: [ + requests: [ + memory: '1Gi', + cpu: '0.25' + ], + limits: [ + memory: '2Gi', + cpu: '1' + ] + ] + ] + ]]] + stepRule.step.dockerExecuteOnKubernetes(script: nullScript, + containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute'], { + bodyExecuted = true + }) + + assertEquals(requests: [memory: '1Gi',cpu: '0.25'],limits: [memory: '2Gi',cpu: '1'], resources.jnlp) + assertEquals(requests: [memory: '1Gi',cpu: '0.25'],limits: [memory: '2Gi',cpu: '1'], resources.mavenexecute) + assertTrue(bodyExecuted) + } + + @Test + void testDockerExecuteOnKubernetesWithSpecificResourcLimitsParametersAreTakingPrecendence() throws Exception { + + // the settings here are expected to be overwritten by the parameters provided via signature + nullScript.commonPipelineEnvironment.configuration = [general: + [jenkinsKubernetes: [ + resources: [ + mavenexecute: [ + requests: [ + memory: '2Gi', + cpu: '0.75' + ], + limits: [ + memory: '4Gi', + cpu: '2' + ] + ] + ] + ]]] + stepRule.step.dockerExecuteOnKubernetes(script: nullScript, + containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute'], + resources: [ + mavenexecute: [ + requests: [ + memory: '8Gi', + cpu: '2' + ], + limits: [ + memory: '16Gi', + cpu: '4' + ] + ] + ]) { + bodyExecuted = true + } + + assertEquals(requests: [memory: '8Gi',cpu: '2'],limits: [memory: '16Gi',cpu: '4'], resources.mavenexecute) + assertTrue(bodyExecuted) + } + + @Test + void testDockerExecuteOnKubernetesWithSpecificResourceLimits() throws Exception { + + nullScript.commonPipelineEnvironment.configuration = [general: + [jenkinsKubernetes: [ + resources: [ + DEFAULT: [ + requests: [ + memory: '1Gi', + cpu: '0.25' + ], + limits: [ + memory: '2Gi', + cpu: '1' + ] + ], + mavenexecute: [ + requests: [ + memory: '2Gi', + cpu: '0.75' + ], + limits: [ + memory: '4Gi', + cpu: '2' + ] + ], + jnlp: [ + requests: [ + memory: '3Gi', + cpu: '0.33' + ], + limits: [ + memory: '6Gi', + cpu: '3' + ] + ], + mysidecar: [ + requests: [ + memory: '10Gi', + cpu: '5.00' + ], + limits: [ + memory: '20Gi', + cpu: '10' + ] + ] + ] + ] + ]] + stepRule.step.dockerExecuteOnKubernetes(script: nullScript, + containerMap: ['maven:3.5-jdk-8-alpine': 'mavenexecute'], + sidecarImage: 'ubuntu', + sidecarName: 'mysidecar') { + bodyExecuted = true + } + + assertEquals(requests: [memory: '10Gi',cpu: '5.00'],limits: [memory: '20Gi',cpu: '10'], resources.mysidecar) + assertEquals(requests: [memory: '3Gi',cpu: '0.33'],limits: [memory: '6Gi',cpu: '3'], resources.jnlp) + assertEquals(requests: [memory: '2Gi',cpu: '0.75'],limits: [memory: '4Gi',cpu: '2'], resources.mavenexecute) + assertTrue(bodyExecuted) + } + @Test void testDockerExecuteOnKubernetesUpperCaseContainerName() throws Exception { stepRule.step.dockerExecuteOnKubernetes(script: nullScript, diff --git a/vars/dockerExecuteOnKubernetes.groovy b/vars/dockerExecuteOnKubernetes.groovy index f011ffe27..e35eab9a0 100644 --- a/vars/dockerExecuteOnKubernetes.groovy +++ b/vars/dockerExecuteOnKubernetes.groovy @@ -33,6 +33,7 @@ import hudson.AbortException * @parentConfigKey jenkinsKubernetes */ 'inheritFrom', + 'resources', /** * Print more detailed information into the log. * @possibleValues `true`, `false` @@ -158,7 +159,17 @@ import hudson.AbortException * This flag controls whether the stashing does *not* use the default exclude patterns in addition to the patterns provided in `stashExcludes`. * @possibleValues `true`, `false` */ - 'stashNoDefaultExcludes' + 'stashNoDefaultExcludes', + /** + * A map containing the resources per container. The key is the + * container name. The value is a map defining valid resources. + * An entry with key `DEFAULT` can be used for defining resources + * for all containers which does not have resources specified otherwise. + * Alternate way for providing resources is via `general/jenkinsKubernetes/resources` + * in the project configuration. Providing the resources map as parameter + * to the step call takes precedence. + */ + 'resources', ]) @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.minus([ 'stashIncludes', @@ -367,10 +378,20 @@ private List getContainerList(config) { def result = [] //allow definition of jnlp image via environment variable JENKINS_JNLP_IMAGE in the Kubernetes landscape or via config as fallback if (env.JENKINS_JNLP_IMAGE || config.jenkinsKubernetes.jnlpAgent) { - result.push([ - name : 'jnlp', + + def jnlpContainerName = 'jnlp' + + def jnlpSpec = [ + name : jnlpContainerName, image: env.JENKINS_JNLP_IMAGE ?: config.jenkinsKubernetes.jnlpAgent - ]) + ] + + def resources = getResources(jnlpContainerName, config) + if(resources) { + jnlpSpec.resources = resources + } + + result.push(jnlpSpec) } config.containerMap.each { imageName, containerName -> def containerPullImage = config.containerPullImageFlags?.get(imageName) @@ -414,22 +435,46 @@ private List getContainerList(config) { } containerSpec.ports = ports } + def resources = getResources(containerName.toLowerCase(), config) + if(resources) { + containerSpec.resources = resources + } result.push(containerSpec) } if (config.sidecarImage) { + def sideCarContainerName = config.sidecarName.toLowerCase() def containerSpec = [ - name : config.sidecarName.toLowerCase(), + name : sideCarContainerName, image : config.sidecarImage, imagePullPolicy: config.sidecarPullImage ? "Always" : "IfNotPresent", env : getContainerEnvs(config, config.sidecarImage, config.sidecarEnvVars, config.sidecarWorkspace), command : [] ] + def resources = getResources(sideCarContainerName, config) + if(resources) { + containerSpec.resources = resources + } result.push(containerSpec) } return result } +private Map getResources(String containerName, Map config) { + Map resources = config.resources + if(resources == null) { + resources = config?.jenkinsKubernetes.resources + } + if(resources == null) { + return null + } + Map res = resources.get(containerName) + if(res == null) { + res = resources.get('DEFAULT') + } + return res +} + /* * Returns a list of envVar object consisting of set * environment variables, params (Parametrized Build) and working directory.