Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve tests and uniform code #48

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ pipeline {
}
steps {
checkout scm
sh "make build"
krufab marked this conversation as resolved.
Show resolved Hide resolved
sh 'make tests && make build'
krufab marked this conversation as resolved.
Show resolved Hide resolved
sh 'docker system prune --force --all'
}
}
}
Expand Down
33 changes: 7 additions & 26 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,32 +1,13 @@
ROOT:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))

IMAGE_NAME:=jenkins4eval/ssh-slave
IMAGE_ALPINE:=${IMAGE_NAME}:alpine
IMAGE_DEBIAN:=${IMAGE_NAME}:test
IMAGE_JDK11:=${IMAGE_NAME}:jdk11

build: build-alpine build-debian build-jdk11

build-alpine:
docker build -t ${IMAGE_ALPINE} --file Dockerfile-alpine .

build-debian:
docker build -t ${IMAGE_DEBIAN} --file Dockerfile .

build-jdk11:
docker build -t ${IMAGE_JDK11} --file Dockerfile-jdk11 .

.PHONY: test
test: test-alpine test-jdk11 test-debian
build:
krufab marked this conversation as resolved.
Show resolved Hide resolved
docker build -t ${IMAGE_NAME}:latest .
docker build -t ${IMAGE_NAME}:alpine -f Dockerfile-alpine .
docker build -t ${IMAGE_NAME}jdk11 -f Dockerfile-jdk11 .

.PHONY: test-alpine
test-alpine:
.PHONY: tests
tests:
krufab marked this conversation as resolved.
Show resolved Hide resolved
@bats tests/tests.bats
@FLAVOR=alpine bats tests/tests.bats

.PHONY: test-jdk11
test-alpine:
@FLAVOR=jdk11 bats tests/tests.bats

.PHONY: test-debian
test-debian:
@bats tests/tests.bats
43 changes: 26 additions & 17 deletions tests/test_helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,57 @@ set -eu
type ssh &>/dev/null || ( echo "ssh is not available"; exit 1 )
)>&2

function printMessage {
echo "# ${@}" >&3
}

# Assert that $1 is the output of a command $2
function assert {
local expected_output=$1
local expected_output
local actual_output
expected_output="${1}"
shift
actual_output=$("$@")
if ! [[ "$actual_output" = "$expected_output" ]]; then
echo "expected: '$expected_output', actual: '$actual_output'"
actual_output=$("${@}")
if ! [[ "${actual_output}" = "${expected_output}" ]]; then
printMessage "Expected: '${expected_output}', actual: '${actual_output}'"
false
fi
}

# Retry a command $1 times until it succeeds. Wait $2 seconds between retries.
function retry {
local attempts=$1
local attempts
local delay
local i
attempts="${1}"
shift
local delay=$1
delay="${1}"
shift
local i

for ((i=0; i < attempts; i++)); do
run "$@"
if [[ "$status" -eq 0 ]]; then
run "${@}"
if [[ "${status}" -eq 0 ]]; then
return 0
fi
sleep "$delay"
sleep "${delay}"
done

echo "Command '$*' failed $attempts times. Status: $status. Output: $output"
printMessage "Command '${*}' failed $attempts times. Status: ${status}. Output: ${output}"

false
}

# return the published port for given container port $1
function get_port {
docker port "${SUT_CONTAINER}" "$1" | cut -d: -f2
docker port "${AGENT_CONTAINER}" "${1}" | cut -d: -f2
}

# run a given command through ssh on the test container.
# Use the $status, $output and $lines variables to make assertions
function run_through_ssh {
SSH_PORT=$(get_port 22)
if [[ "${SSH_PORT}" = "" ]]; then
echo "failed to get SSH port"
printMessage "failed to get SSH port"
false
else
TMP_PRIV_KEY_FILE=$(mktemp "${BATS_TMPDIR}"/bats_private_ssh_key_XXXXXXX)
Expand All @@ -64,18 +73,18 @@ function run_through_ssh {
-l jenkins \
localhost \
-p "${SSH_PORT}" \
"$@"
"${@}"

rm -f "${TMP_PRIV_KEY_FILE}"
fi
}

function clean_test_container {
docker kill "${SUT_CONTAINER}" &>/dev/null ||:
docker rm -fv "${SUT_CONTAINER}" &>/dev/null ||:
docker kill "${AGENT_CONTAINER}" &>/dev/null ||:
docker rm -fv "${AGENT_CONTAINER}" &>/dev/null ||:
}

function is_slave_container_running {
sleep 1 # give time to sshd to eventually fail to initialize
retry 3 1 assert "true" docker inspect -f '{{.State.Running}}' "${SUT_CONTAINER}"
retry 3 1 assert "true" docker inspect -f '{{.State.Running}}' "${AGENT_CONTAINER}"
}
93 changes: 56 additions & 37 deletions tests/tests.bats
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
#!/usr/bin/env bats

DOCKERFILE=Dockerfile
SUT_IMAGE=jenkins-ssh-slave
SUT_CONTAINER=bats-jenkins-ssh-slave
JDK=8
AGENT_IMAGE=jenkins-ssh-slave
AGENT_CONTAINER=bats-jenkins-ssh-slave

if [[ -z "${FLAVOR}" ]]
then
FLAVOR="debian"
elif [[ "${FLAVOR}" = "jdk11" ]]
then
DOCKERFILE+="-jdk11"
SUT_IMAGE+=":jdk11"
SUT_CONTAINER+="-jdk11"
JDK=11
AGENT_IMAGE+=":jdk11"
AGENT_CONTAINER+="-jdk11"
else
DOCKERFILE+="-alpine"
SUT_IMAGE+=":alpine"
SUT_CONTAINER+="-alpine"
AGENT_IMAGE+=":alpine"
AGENT_CONTAINER+="-alpine"
fi

load test_helpers
load keys

clean_test_container

function teardown () {
clean_test_container
}

@test "[${FLAVOR}] build image" {
cd "${BATS_TEST_DIRNAME}"/.. || false
docker build -t "${SUT_IMAGE}" -f "${DOCKERFILE}" .
docker build -t "${AGENT_IMAGE}" -f "${DOCKERFILE}" .
}

@test "[${FLAVOR}] checking image metadata" {
local VOLUMES_MAP="$(docker inspect -f '{{.Config.Volumes}}' ${SUT_IMAGE})"
local VOLUMES_MAP
VOLUMES_MAP="$(docker inspect -f '{{.Config.Volumes}}' ${AGENT_IMAGE})"

echo "${VOLUMES_MAP}" | grep '/tmp'
echo "${VOLUMES_MAP}" | grep '/home/jenkins'
Expand All @@ -38,18 +45,36 @@ clean_test_container
}

@test "[${FLAVOR}] image has bash and java installed and in the PATH" {
docker run -d --name "${SUT_CONTAINER}" -P "${SUT_IMAGE}" "${PUBLIC_SSH_KEY}"

docker exec "${SUT_CONTAINER}" which bash
docker exec "${SUT_CONTAINER}" bash --version
docker exec "${SUT_CONTAINER}" which java
docker exec "${SUT_CONTAINER}" java -version

clean_test_container
docker run -d --name "${AGENT_CONTAINER}" -P "${AGENT_IMAGE}" "${PUBLIC_SSH_KEY}"

run docker exec "${AGENT_CONTAINER}" which bash
[ "${status}" -eq 0 ]
run docker exec "${AGENT_CONTAINER}" bash --version
[ "${status}" -eq 0 ]
run docker exec "${AGENT_CONTAINER}" which java
[ "${status}" -eq 0 ]

if [[ "${JDK}" -eq 8 ]]
then
run docker exec "${AGENT_CONTAINER}" sh -c "
java -version 2>&1 \
| grep -o -E '^openjdk version \"[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+.*\"' \
| grep -o -E '\.[[:digit:]]+\.' \
| grep -o -E '[[:digit:]]+'
"
else
run docker exec "${AGENT_CONTAINER}" sh -c "
java -version 2>&1 \
| grep -o -E '^openjdk version \"[[:digit:]]+\.' \
| grep -o -E '\"[[:digit:]]+\.' \
| grep -o -E '[[:digit:]]+'
"
fi
[ "${JDK}" = "${lines[0]}" ]
}

@test "[${FLAVOR}] create slave container with pubkey as argument" {
docker run -d --name "${SUT_CONTAINER}" -P "${SUT_IMAGE}" "${PUBLIC_SSH_KEY}"
docker run -d --name "${AGENT_CONTAINER}" -P "${AGENT_IMAGE}" "${PUBLIC_SSH_KEY}"

is_slave_container_running

Expand All @@ -61,12 +86,10 @@ clean_test_container
echo "output: $output"; \
false \
)

clean_test_container
}

@test "[${FLAVOR}] create slave container with pubkey as environment variable" {
docker run -e "JENKINS_SLAVE_SSH_PUBKEY=${PUBLIC_SSH_KEY}" -d --name "${SUT_CONTAINER}" -P "${SUT_IMAGE}"
docker run -e "JENKINS_SLAVE_SSH_PUBKEY=${PUBLIC_SSH_KEY}" -d --name "${AGENT_CONTAINER}" -P "${AGENT_IMAGE}"

is_slave_container_running

Expand All @@ -78,8 +101,6 @@ clean_test_container
echo "output: $output"; \
false \
)

clean_test_container
}

@test "[${FLAVOR}] use build args correctly" {
Expand All @@ -97,21 +118,19 @@ clean_test_container
--build-arg "uid=${TEST_UID}" \
--build-arg "gid=${TEST_GID}" \
--build-arg "JENKINS_AGENT_HOME=${TEST_JAH}" \
-t "${SUT_IMAGE}" \
-t "${AGENT_IMAGE}" \
-f "${DOCKERFILE}" .

docker run -d --name "${SUT_CONTAINER}" -P "${SUT_IMAGE}" "${PUBLIC_SSH_KEY}"

RESULT=$(docker exec "${SUT_CONTAINER}" sh -c "id -u -n ${TEST_USER}")
[ "${RESULT}" = "${TEST_USER}" ]
RESULT=$(docker exec "${SUT_CONTAINER}" sh -c "id -g -n ${TEST_USER}")
[ "${RESULT}" = "${TEST_GROUP}" ]
RESULT=$(docker exec "${SUT_CONTAINER}" sh -c "id -u ${TEST_USER}")
[ "${RESULT}" = "${TEST_UID}" ]
RESULT=$(docker exec "${SUT_CONTAINER}" sh -c "id -g ${TEST_USER}")
[ "${RESULT}" = "${TEST_GID}" ]
RESULT=$(docker exec "${SUT_CONTAINER}" sh -c 'stat -c "%U:%G" "${JENKINS_AGENT_HOME}"')
[ "${RESULT}" = "${TEST_USER}:${TEST_GROUP}" ]

clean_test_container
docker run -d --name "${AGENT_CONTAINER}" -P "${AGENT_IMAGE}" "${PUBLIC_SSH_KEY}"

run docker exec "${AGENT_CONTAINER}" sh -c "id -u -n ${TEST_USER}"
[ "${TEST_USER}" = "${lines[0]}" ]
run docker exec "${AGENT_CONTAINER}" sh -c "id -g -n ${TEST_USER}"
[ "${TEST_GROUP}" = "${lines[0]}" ]
run docker exec "${AGENT_CONTAINER}" sh -c "id -u ${TEST_USER}"
[ "${TEST_UID}" = "${lines[0]}" ]
run docker exec "${AGENT_CONTAINER}" sh -c "id -g ${TEST_USER}"
[ "${TEST_GID}" = "${lines[0]}" ]
run docker exec "${AGENT_CONTAINER}" sh -c 'stat -c "%U:%G" "${JENKINS_AGENT_HOME}"'
[ "${TEST_USER}:${TEST_GROUP}" = "${lines[0]}" ]
}