import com.sap.piper.k8s.ContainerMap import com.sap.piper.JenkinsUtils import com.sap.piper.SidecarUtils import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain import util.BasePiperTest import util.JenkinsLoggingRule import util.JenkinsReadYamlRule import util.JenkinsShellCallRule import util.JenkinsStepRule import util.PluginMock import util.Rules import static org.hamcrest.Matchers.* import static org.junit.Assert.assertThat import static org.junit.Assert.assertTrue import static org.junit.Assert.assertEquals import static org.junit.Assert.assertFalse class DockerExecuteTest extends BasePiperTest { private DockerMock docker private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this) private JenkinsStepRule stepRule = new JenkinsStepRule(this) private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this) @Rule public RuleChain ruleChain = Rules .getCommonRules(this) .around(new JenkinsReadYamlRule(this)) .around(loggingRule) .around(stepRule) .around(shellRule) def bodyExecuted def containerName @Before void init() { bodyExecuted = false docker = new DockerMock() JenkinsUtils.metaClass.static.isPluginActive = { def s -> new PluginMock(s).isActive() } binding.setVariable('docker', docker) shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, "docker .*", 0) } @Test void testExecuteInsideContainerOfExistingPod() throws Exception { List usedDockerEnvVars helper.registerAllowedMethod('container', [String.class, Closure.class], { String container, Closure body -> containerName = container body() }) helper.registerAllowedMethod('withEnv', [List.class, Closure.class], { List envVars, Closure body -> usedDockerEnvVars = envVars body() }) binding.setVariable('env', [POD_NAME: 'testpod', ON_K8S: 'true']) ContainerMap.instance.setMap(['testpod': ['maven:3.5-jdk-8-alpine': 'mavenexec']]) stepRule.step.dockerExecute(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine', dockerEnvVars: ['http_proxy': 'http://proxy:8000']) { bodyExecuted = true } assertTrue(loggingRule.log.contains('Executing inside a Kubernetes Container')) assertEquals('mavenexec', containerName) assertEquals(usedDockerEnvVars[0].toString(), "http_proxy=http://proxy:8000") assertTrue(bodyExecuted) } @Test void testExecuteInsideNewlyCreatedPod() throws Exception { helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { Map config, Closure body -> body() }) binding.setVariable('env', [ON_K8S: 'true']) ContainerMap.instance.setMap(['testpod': ['maven:3.5-jdk-8-alpine': 'mavenexec']]) stepRule.step.dockerExecute(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine', dockerEnvVars: ['http_proxy': 'http://proxy:8000']) { bodyExecuted = true } assertTrue(loggingRule.log.contains('Executing inside a Kubernetes Pod')) assertTrue(bodyExecuted) } @Test void testExecuteInsidePodWithEmptyContainerMap() throws Exception { helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { Map config, Closure body -> body() }) binding.setVariable('env', [POD_NAME: 'testpod', ON_K8S: 'true']) ContainerMap.instance.setMap([:]) stepRule.step.dockerExecute(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine', dockerEnvVars: ['http_proxy': 'http://proxy:8000']) { bodyExecuted = true } assertTrue(loggingRule.log.contains('Executing inside a Kubernetes Pod')) assertTrue(bodyExecuted) } @Test void testExecuteInsidePodWithStageKeyEmptyValue() throws Exception { helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { Map config, Closure body -> body() }) binding.setVariable('env', [POD_NAME: 'testpod', ON_K8S: 'true']) ContainerMap.instance.setMap(['testpod': [:]]) stepRule.step.dockerExecute(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine', dockerEnvVars: ['http_proxy': 'http://proxy:8000']) { bodyExecuted = true } assertTrue(loggingRule.log.contains('Executing inside a Kubernetes Pod')) assertTrue(bodyExecuted) } @Test void testExecuteInsidePodWithCustomCommandAndShell() throws Exception { Map kubernetesConfig = [:] helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { Map config, Closure body -> kubernetesConfig = config return body() }) binding.setVariable('env', [ON_K8S: 'true']) stepRule.step.dockerExecute( script: nullScript, containerCommand: '/busybox/tail -f /dev/null', containerShell: '/busybox/sh', dockerImage: 'maven:3.5-jdk-8-alpine' ) { bodyExecuted = true } assertTrue(loggingRule.log.contains('Executing inside a Kubernetes Pod')) assertThat(kubernetesConfig.containerCommand, is('/busybox/tail -f /dev/null')) assertThat(kubernetesConfig.containerShell, is('/busybox/sh')) assertTrue(bodyExecuted) } @Test void testExecuteInsideDockerContainer() throws Exception { stepRule.step.dockerExecute(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine') { bodyExecuted = true } assertEquals('maven:3.5-jdk-8-alpine', docker.getImageName()) assertTrue(docker.isImagePulled()) assertEquals('--env http_proxy --env https_proxy --env no_proxy --env HTTP_PROXY --env HTTPS_PROXY --env NO_PROXY', docker.getParameters().trim()) assertTrue(bodyExecuted) } @Test void testSkipDockerImagePull() throws Exception { nullScript.commonPipelineEnvironment.configuration = [steps: [dockerExecute: [dockerPullImage: false]]] stepRule.step.dockerExecute( script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine' ) { bodyExecuted = true } assertThat(docker.imagePullCount, is(0)) assertThat(bodyExecuted, is(true)) } @Test void testSkipSidecarImagePull() throws Exception { stepRule.step.dockerExecute( script: nullScript, dockerName: 'maven', dockerImage: 'maven:3.5-jdk-8-alpine', sidecarEnvVars: ['testEnv': 'testVal'], sidecarImage: 'selenium/standalone-chrome', sidecarVolumeBind: ['/dev/shm': '/dev/shm'], sidecarName: 'testAlias', sidecarPorts: ['4444': '4444', '1111': '1111'], sidecarPullImage: false ) { bodyExecuted = true } assertThat(docker.imagePullCount, is(1)) assertThat(bodyExecuted, is(true)) } @Test void testExecuteInsideDockerContainerWithParameters() throws Exception { stepRule.step.dockerExecute(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine', dockerOptions: '-description=lorem ipsum', dockerVolumeBind: ['my_vol': '/my_vol'], dockerEnvVars: ['http_proxy': 'http://proxy:8000']) { bodyExecuted = true } assertTrue(docker.getParameters().contains('--env https_proxy ')) assertTrue(docker.getParameters().contains('--env http_proxy=http://proxy:8000')) assertTrue(docker.getParameters().contains('description=lorem\\ ipsum')) assertTrue(docker.getParameters().contains('--volume my_vol:/my_vol')) assertTrue(bodyExecuted) } @Test void testExecuteInsideDockerContainerWithDockerOptionsList() throws Exception { stepRule.step.dockerExecute(script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine', dockerOptions: ['-it', '--network=my-network', 'description=lorem ipsum'], dockerEnvVars: ['http_proxy': 'http://proxy:8000']) { bodyExecuted = true } assertTrue(docker.getParameters().contains('--env http_proxy=http://proxy:8000')) assertTrue(docker.getParameters().contains('-it')) assertTrue(docker.getParameters().contains('--network=my-network')) assertTrue(docker.getParameters().contains('description=lorem\\ ipsum')) } @Test void testDockerNotInstalledResultsInLocalExecution() throws Exception { shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, "docker .*", 1) stepRule.step.dockerExecute(script: nullScript, dockerOptions: '-it') { bodyExecuted = true } assertTrue(loggingRule.log.contains('Cannot connect to docker daemon')) assertTrue(loggingRule.log.contains('Running on local environment')) assertTrue(bodyExecuted) assertFalse(docker.isImagePulled()) } @Test void testSidecarDefault() { stepRule.step.dockerExecute( script: nullScript, dockerName: 'maven', dockerImage: 'maven:3.5-jdk-8-alpine', sidecarEnvVars: ['testEnv': 'testVal'], sidecarImage: 'selenium/standalone-chrome', sidecarVolumeBind: ['/dev/shm': '/dev/shm'], sidecarName: 'testAlias', sidecarPorts: ['4444': '4444', '1111': '1111'] ) { bodyExecuted = true } assertThat(bodyExecuted, is(true)) assertThat(docker.imagePullCount, is(2)) assertThat(docker.sidecarParameters, allOf( containsString('--env testEnv=testVal'), containsString('--volume /dev/shm:/dev/shm'), containsString('--network sidecar-'), containsString('--network-alias testAlias') )) assertThat(docker.parameters, allOf( containsString('--network sidecar-'), containsString('--network-alias maven') )) } @Test void testSidecarHealthCheck() { stepRule.step.dockerExecute( script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine', sidecarImage: 'selenium/standalone-chrome', sidecarName: 'testAlias', sidecarReadyCommand: "isReady.sh" ) {} assertThat(shellRule.shell, hasItem("docker exec uniqueId isReady.sh")) } @Test void testSidecarKubernetes() { boolean dockerExecuteOnKubernetesCalled = false binding.setVariable('env', [ON_K8S: 'true']) helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { params, body -> dockerExecuteOnKubernetesCalled = true assertThat(params.dockerImage, is('maven:3.5-jdk-8-alpine')) assertThat(params.containerName, is('maven')) assertThat(params.sidecarEnvVars, is(['testEnv': 'testVal'])) assertThat(params.sidecarName, is('selenium')) assertThat(params.sidecarImage, is('selenium/standalone-chrome')) assertThat(params.containerName, is('maven')) assertThat(params.containerPortMappings['selenium/standalone-chrome'], hasItem(allOf(hasEntry('containerPort', 4444), hasEntry('hostPort', 4444)))) assertThat(params.dockerWorkspace, is('/home/piper')) body() }) stepRule.step.dockerExecute( script: nullScript, containerPortMappings: [ 'selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]] ], dockerImage: 'maven:3.5-jdk-8-alpine', dockerName: 'maven', dockerWorkspace: '/home/piper', sidecarEnvVars: ['testEnv': 'testVal'], sidecarImage: 'selenium/standalone-chrome', sidecarName: 'selenium', sidecarVolumeBind: ['/dev/shm': '/dev/shm'] ) { bodyExecuted = true } assertThat(bodyExecuted, is(true)) assertThat(dockerExecuteOnKubernetesCalled, is(true)) } @Test void testSidecarKubernetesHealthCheck() { binding.setVariable('env', [ON_K8S: 'true']) helper.registerAllowedMethod('dockerExecuteOnKubernetes', [Map.class, Closure.class], { params, body -> body() SidecarUtils sidecarUtils = new SidecarUtils(nullScript) sidecarUtils.waitForSidecarReadyOnKubernetes(params.sidecarName, params.sidecarReadyCommand) }) def containerCalled = false helper.registerAllowedMethod('container', [Map.class, Closure.class], { params, body -> containerCalled = true assertThat(params.name, is('testAlias')) body() }) stepRule.step.dockerExecute( script: nullScript, dockerImage: 'maven:3.5-jdk-8-alpine', sidecarImage: 'selenium/standalone-chrome', sidecarName: 'testAlias', sidecarReadyCommand: "isReady.sh" ) {} assertThat(containerCalled, is(true)) assertThat(shellRule.shell, hasItem("isReady.sh")) } private class DockerMock { private String imageName private boolean imagePulled = false private int imagePullCount = 0 private String parameters private String sidecarParameters DockerMock image(String imageName) { this.imageName = imageName return this } void pull() { imagePullCount++ imagePulled = true } void inside(String parameters, body) { this.parameters = parameters body() } void withRun(String parameters, body) { this.sidecarParameters = parameters body([id: 'uniqueId']) } String getImageName() { return imageName } boolean isImagePulled() { return imagePulled } String getParameters() { return parameters } } }