mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
cbd932a5eb
* wrap all in docker.withRegistry() * Renamed parameter No backwards alias, since it never worked before. * Fix code and tests * Rename parameter as per review * Rename parameter as per review Co-authored-by: Daniel Kurzynski <daniel.kurzynski@sap.com>
515 lines
19 KiB
Groovy
515 lines
19 KiB
Groovy
import com.sap.piper.k8s.ContainerMap
|
|
|
|
import com.sap.piper.JenkinsUtils
|
|
import com.sap.piper.SidecarUtils
|
|
import com.sap.piper.Utils
|
|
import org.junit.After
|
|
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)
|
|
Utils.metaClass.echo = { def m -> }
|
|
}
|
|
|
|
@After
|
|
public void tearDown() {
|
|
Utils.metaClass = null
|
|
}
|
|
|
|
@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.getImageNames()[0])
|
|
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 testPullSidecarWithDedicatedCredentialsAndRegistry() {
|
|
nullScript.commonPipelineEnvironment.configuration =
|
|
[
|
|
steps: [
|
|
dockerExecute: [
|
|
dockerRegistryUrl: 'https://registry.example.org',
|
|
dockerRegistryCredentialsId: 'mySecrets',
|
|
sidecarRegistryUrl: 'https://sidecarregistry.example.org',
|
|
sidecarRegistryCredentialsId: 'mySidecarRegistryCredentials',
|
|
]
|
|
]
|
|
]
|
|
stepRule.step.dockerExecute(
|
|
script: nullScript,
|
|
dockerImage: 'maven:3.5-jdk-8-alpine',
|
|
dockerRegistryCredentialsId: 'mySecrets',
|
|
sidecarImage: 'ubuntu',
|
|
) {
|
|
bodyExecuted = true
|
|
}
|
|
// not clear which image has been pulled with which registry, but at least
|
|
// both registries are involved.
|
|
assertThat(docker.registriesWithCredentials, is([
|
|
[
|
|
registry: 'https://registry.example.org',
|
|
credentialsId: 'mySecrets',
|
|
],
|
|
[
|
|
registry: 'https://sidecarregistry.example.org',
|
|
credentialsId: 'mySidecarRegistryCredentials',
|
|
]
|
|
]))
|
|
assertThat(docker.imagePullCount, is(2))
|
|
assertThat(bodyExecuted, is(true))
|
|
}
|
|
|
|
@Test
|
|
void testPullSidecarWithSameCredentialsAndRegistryLikeBaseImageWhenNothingElseIsSpecified() {
|
|
nullScript.commonPipelineEnvironment.configuration =
|
|
[
|
|
steps: [
|
|
dockerExecute: [
|
|
dockerRegistryUrl: 'https://registry.example.org',
|
|
]
|
|
]
|
|
]
|
|
stepRule.step.dockerExecute(
|
|
script: nullScript,
|
|
dockerImage: 'maven:3.5-jdk-8-alpine',
|
|
dockerRegistryCredentialsId: 'mySecrets',
|
|
sidecarImage: 'ubuntu',
|
|
) {
|
|
bodyExecuted = true
|
|
}
|
|
// from getting an empty list we derive withRegistry has not been called
|
|
// if it would have been called we would have the registry provided above.
|
|
assertThat(docker.registriesWithCredentials, is([
|
|
[
|
|
registry: 'https://registry.example.org',
|
|
credentialsId: 'mySecrets',
|
|
],
|
|
[
|
|
registry: 'https://registry.example.org',
|
|
credentialsId: 'mySecrets',
|
|
],
|
|
]))
|
|
assertThat(docker.imagePullCount, is(2))
|
|
assertThat(bodyExecuted, is(true))
|
|
}
|
|
|
|
@Test
|
|
void testPullWithRegistryOnlyAndNoCredentials() {
|
|
nullScript.commonPipelineEnvironment.configuration =
|
|
[
|
|
steps: [
|
|
dockerExecute: [
|
|
dockerRegistryUrl: 'https://registry.example.org',
|
|
]
|
|
]
|
|
]
|
|
stepRule.step.dockerExecute(
|
|
script: nullScript,
|
|
dockerImage: 'maven:3.5-jdk-8-alpine'
|
|
) {
|
|
bodyExecuted = true
|
|
}
|
|
// from getting an empty list we derive withRegistry has not been called
|
|
// if it would have been called we would have the registry provided above.
|
|
assertThat(docker.registriesWithCredentials, is([
|
|
[
|
|
registry: 'https://registry.example.org',
|
|
]
|
|
]))
|
|
assertThat(docker.imagePullCount, is(1))
|
|
assertThat(bodyExecuted, is(true))
|
|
}
|
|
|
|
@Test
|
|
void testPullWithCredentials() throws Exception {
|
|
|
|
nullScript.commonPipelineEnvironment.configuration =
|
|
[
|
|
steps: [
|
|
dockerExecute: [
|
|
dockerRegistryUrl: 'https://registry.example.org',
|
|
dockerRegistryCredentialsId: 'mySecrets',
|
|
]
|
|
]
|
|
]
|
|
stepRule.step.dockerExecute(
|
|
script: nullScript,
|
|
dockerImage: 'maven:3.5-jdk-8-alpine'
|
|
) {
|
|
bodyExecuted = true
|
|
}
|
|
assertThat(docker.registriesWithCredentials, is([
|
|
[
|
|
registry: 'https://registry.example.org',
|
|
credentialsId: 'mySecrets',
|
|
]
|
|
]))
|
|
assertThat(docker.imagePullCount, is(1))
|
|
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 List imageNames = []
|
|
private boolean imagePulled = false
|
|
private int imagePullCount = 0
|
|
private String parameters
|
|
private String sidecarParameters
|
|
private List registriesWithCredentials = []
|
|
private String credentialsId
|
|
|
|
DockerMock image(String imageName) {
|
|
this.imageNames << imageName
|
|
return this
|
|
}
|
|
|
|
void pull() {
|
|
imagePullCount++
|
|
imagePulled = true
|
|
}
|
|
|
|
void withRegistry(String registry, String credentialsId, Closure c) {
|
|
this.registriesWithCredentials << [registry: registry, credentialsId: credentialsId]
|
|
c()
|
|
}
|
|
|
|
void withRegistry(String registry, Closure c) {
|
|
this.registriesWithCredentials << [registry: registry]
|
|
c()
|
|
}
|
|
|
|
void inside(String parameters, body) {
|
|
this.parameters = parameters
|
|
body()
|
|
}
|
|
|
|
void withRun(String parameters, body) {
|
|
this.sidecarParameters = parameters
|
|
body([id: 'uniqueId'])
|
|
}
|
|
|
|
def getImageNames() {
|
|
return imageNames
|
|
}
|
|
|
|
boolean isImagePulled() {
|
|
return imagePulled
|
|
}
|
|
|
|
String getParameters() {
|
|
return parameters
|
|
}
|
|
}
|
|
}
|