Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

Commit

Permalink
Merge pull request #248 from jjo/jjo-add-integration-tests
Browse files Browse the repository at this point in the history
[jjo] add integration tests
  • Loading branch information
ngtuna authored Aug 10, 2017
2 parents 21de6ea + 04a36c3 commit bc472b7
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 160 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ go:
- 1.7
- 1.8.3

go_import_path: github.com/kubeless/kubeless

services:
- docker
env:
Expand All @@ -27,6 +29,8 @@ install:
- chmod +x $GOPATH/bin/kubecfg
- git clone --depth=1 https://github.com/ksonnet/ksonnet-lib.git
- export KUBECFG_JPATH=$PWD/ksonnet-lib
- git clone --depth=1 https://github.com/sstephenson/bats.git bats
- export PATH=$PATH:$PWD/bats/bin

script:
- make test
Expand All @@ -37,6 +41,10 @@ script:
make controller-image CONTROLLER_IMAGE=$CONTROLLER_IMAGE
fi
- make all-yaml
- |
if [ "$TRAVIS_OS_NAME" = linux ]; then
make integration-tests
fi
after_success:
- |
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ validation:
./script/validate-gofmt
./script/validate-git-marks

integration-tests:
./script/integration-tests

minikube-rbac-test:
./script/minikube-rbac-test
./script/integration-test-rbac minikube

fmt:
$(GOFMT) -s -w $(GO_FILES)
12 changes: 12 additions & 0 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ get-python:
kubeless function deploy get-python --trigger-http --runtime python2.7 --handler helloget.foo --from-file python/helloget.py
echo "curl localhost:8080/api/v1/proxy/namespaces/default/services/get-python/"

get-python-verify:
kubeless function call get-python |egrep hello.world

get-nodejs:
kubeless function deploy get-nodejs --trigger-http --runtime nodejs6 --handler helloget.foo --from-file nodejs/helloget.js
echo "curl localhost:8080/api/v1/proxy/namespaces/default/services/get-nodejs/"
Expand All @@ -12,14 +15,23 @@ get-python-metadata:

get: get-python get-nodejs get-python-metadata

get-nodejs-verify:
kubeless function call get-nodejs |egrep hello.world

post-python:
kubeless function deploy post-python --trigger-http --runtime python2.7 --handler hellowithdata.handler --from-file python/hellowithdata.py
echo "curl --data '{\"hello\":\"world\"}' localhost:8080/api/v1/proxy/namespaces/default/services/post-python/ --header \"Content-Type:application/json\""

post-python-verify:
kubeless function call post-python --data '{"it-s": "alive"}'|egrep "it.*alive"

post-nodejs:
kubeless function deploy post-nodejs --trigger-http --runtime nodejs6 --handler hellowithdata.handler --from-file nodejs/hellowithdata.js
echo "curl --data '{\"hello\":\"world\"}' localhost:8080/api/v1/proxy/namespaces/default/services/post-nodejs/ --header \"Content-Type:application/json\""

post-nodejs-verify:
kubeless function call post-nodejs --data '{"it-s": "alive"}'|egrep "it.*alive"

post: post-python post-nodejs

pubsub:
Expand Down
9 changes: 9 additions & 0 deletions kubeless-rbac-novols.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Remove volumeClaimTemplates from kafkaSts to enable testing kubeless
# on simple clusters deploys like kubeadm-dind-cluster
local kubeless_rbac = import "kubeless-rbac.jsonnet";

kubeless_rbac + {
kafkaSts+:
{spec+: {volumeClaimTemplates: []}} +
{spec+: {template+: {spec+: {volumes: [{name: "datadir", emptyDir: {}}]}}}}
}
2 changes: 1 addition & 1 deletion script/binary
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
set -e

if [ -z "$1" ]; then
VERSION=dev-$(shell date +%FT%T%z)
VERSION=dev-$(date +%FT%T%z)
else
VERSION=$1
fi
Expand Down
44 changes: 44 additions & 0 deletions script/integration-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash
# Special case: if ./ksonnet-lib exists, set KUBECFG_JPATH
test -d $PWD/ksonnet-lib && export KUBECFG_JPATH=$PWD/ksonnet-lib

# We require below env
: ${GOPATH:?} ${KUBECFG_JPATH:?}

# Default to "dind" kubernetes context
INTEGRATION_TESTS_CTX=${1:-dind}

# ... and 'bats' installed
which bats > /dev/null || {
echo "ERROR: 'bats' is required to run these tests," \
"install it from https://github.com/sstephenson/bats"
exit 255
}

k8s_create_dind() {
# Bring up kubeadm-dind-cluster (docker-in-docker k8s cluster)
DIND_CLUSTER_SH=dind-cluster-v1.7.sh
DIND_URL=https://cdn.rawgit.com/Mirantis/kubeadm-dind-cluster/master/fixed/${DIND_CLUSTER_SH}
rm -f ${DIND_CLUSTER_SH}
wget ${DIND_URL}
chmod +x ${DIND_CLUSTER_SH}
./${DIND_CLUSTER_SH} up
export PATH="$HOME/.kubeadm-dind-cluster:$PATH"
sleep 5
}

## main() ##
# Create k8s cluster (only "dind" supported atm) if missing:
kubectl get nodes --context=${INTEGRATION_TESTS_CTX:?} || k8s_create_${INTEGRATION_TESTS_CTX} || exit 255
export TEST_CONTEXT=${INTEGRATION_TESTS_CTX}

source script/libtest.bash
trap k8s_context_restore 0
k8s_context_save

# Run the tests thru bats:
kubectl create namespace kubeless
bats tests/integration-tests.bats

# Just showing remaining k8s objects
kubectl get all --all-namespaces
185 changes: 185 additions & 0 deletions script/libtest.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#!/bin/bash
# k8s and kubeless helpers, specially "wait"-ers on pod ready/deleted/etc

KUBELESS_JSONNET=kubeless.jsonnet
KUBELESS_JSONNET_RBAC=kubeless-rbac-novols.jsonnet

KUBECTL_BIN=$(which kubectl)
KUBECFG_BIN=$(which kubecfg)

export TEST_MAX_WAIT_SEC=120

# Workaround 'bats' lack of forced output support, dup() stderr fd
exec 9>&2
echo_info() {
test -z "$TEST_DEBUG" && return 0
echo "INFO: $*" >&9
}
export -f echo_info

kubectl() {
${KUBECTL_BIN:?} --context=${TEST_CONTEXT:?} "$@"
}
kubecfg() {
${KUBECFG_BIN:?} --context=${TEST_CONTEXT:?} "$@"
}

## k8s specific Helper functions
k8s_wait_for_pod_ready() {
echo_info "Waiting for pod '${@}' to be ready ... "
local -i cnt=${TEST_MAX_WAIT_SEC:?}
until kubectl get pod "${@}" |&grep -q Running; do
((cnt=cnt-1)) || return 1
sleep 1
done
}
k8s_wait_for_pod_gone() {
echo_info "Waiting for pod '${@}' to be gone ... "
local -i cnt=${TEST_MAX_WAIT_SEC:?}
until kubectl get pod "${@}" |&grep -q No.resources.found; do
((cnt=cnt-1)) || return 1
sleep 1
done
}
k8s_wait_for_pod_logline() {
local string="${1:?}"; shift
local -i cnt=${TEST_MAX_WAIT_SEC:?}
echo_info "Waiting for '${@}' to show logline '${string}' ..."
until kubectl logs --tail=10 "${@}"|&grep -q "${string}"; do
((cnt=cnt-1)) || return 1
sleep 1
done
}
k8s_context_save() {
TEST_CONTEXT_SAVED=$(${KUBECTL_BIN} config current-context)
# Kubeless doesn't support contexts yet, save+restore it
# Don't save current_context if it's the same already
[[ $TEST_CONTEXT_SAVED == $TEST_CONTEXT ]] && TEST_CONTEXT_SAVED=""

# Save current_context
[[ $TEST_CONTEXT_SAVED != "" ]] && \
echo_info "Saved context: '${TEST_CONTEXT_SAVED}'" && \
${KUBECTL_BIN} config use-context ${TEST_CONTEXT}
}
k8s_context_restore() {
# Restore saved context
[[ $TEST_CONTEXT_SAVED != "" ]] && \
echo_info "Restoring context: '${TEST_CONTEXT_SAVED}'" && \
${KUBECTL_BIN} config use-context ${TEST_CONTEXT_SAVED}
}
_wait_for_cmd_ok() {
local cmd="${*:?}"; shift
local -i cnt=${TEST_MAX_WAIT_SEC:?}
echo_info "Waiting for '${*}' to successfully exit ..."
until env ${cmd}; do
((cnt=cnt-1)) || return 1
sleep 1
done
}

## Specific for kubeless
kubeless_recreate() {
local jsonnet_del=${1:?missing jsonnet delete manifest} jsonnet_upd=${2:?missing jsonnet update manifest}
local -i cnt=${TEST_MAX_WAIT_SEC:?}
echo_info "Delete kubeless namespace, wait to be gone ... "
kubecfg delete ${jsonnet_del}
kubectl delete namespace kubeless >& /dev/null || true
while kubectl get namespace kubeless >& /dev/null; do
((cnt=cnt-1)) || return 1
sleep 1
done
kubectl create namespace kubeless
kubecfg update ${jsonnet_upd}
}
kubeless_function_delete() {
local func=${1:?}; shift
echo_info "Deleting function "${func}" in case still present ... "
kubeless function delete "${func}" >& /dev/null || true
kubectl delete all -l function="${func}" > /dev/null || true
k8s_wait_for_pod_gone -l function="${func}"
}
kubeless_function_deploy() {
local func=${1:?}; shift
echo_info "Deploying function ..."
kubeless function deploy ${func} ${@}
}
kubeless_function_exp_regex_call() {
local exp_rc=${1:?} regex=${2:?} func=${3:?}; shift 3
echo_info "Calling function ${func}, expecting rc=${exp_rc} "
kubeless function call ${func} "${@}"|&egrep ${regex}
}
_wait_for_kubeless_controller_ready() {
echo_info "Waiting for kubeless controller to be ready ... "
k8s_wait_for_pod_ready -n kubeless -l kubeless=controller
_wait_for_cmd_ok kubectl get functions 2>/dev/null
}
_wait_for_controller_logline() {
local string="${1:?}"
k8s_wait_for_pod_logline "${string}" -n kubeless -l kubeless=controller
}
_wait_for_simple_function_pod_ready() {
k8s_wait_for_pod_ready -l function=get-python
}
_deploy_simple_function() {
make -C examples get-python
}
_call_simple_function() {
# Artifact to dodge 'bats' lack of support for positively testing _for_ errors
case "${1:?}" in
1) make -C examples get-python-verify |& egrep Error.1;;
0) make -C examples get-python-verify;;
esac
}
_delete_simple_function() {
kubeless_function_delete get-python
}

## Entry points used by 'bats' tests:
verify_k8s_tools() {
local tools="kubectl kubecfg kubeless"
for exe in $tools; do
which ${exe} >/dev/null && continue
echo "ERROR: '${exe}' needs to be installed"
return 1
done
}
verify_minikube_running () {
[[ $TEST_CONTEXT == minikube ]] || return 0
minikube status | grep -q "minikube: Running" && return 0
echo "ERROR: minikube not running."
return 1
}
verify_rbac_mode() {
kubectl api-versions |&grep -q rbac && return 0
echo "ERROR: Please run w/RBAC, eg minikube as: minikube start --extra-config=apiserver.Authorization.Mode=RBAC"
return 1
}
test_must_fail_without_rbac_roles() {
echo_info "RBAC TEST: function deploy/call must fail without RBAC roles"
_delete_simple_function
kubeless_recreate $KUBELESS_JSONNET_RBAC $KUBELESS_JSONNET
_wait_for_kubeless_controller_ready
_deploy_simple_function
_wait_for_controller_logline "User.*cannot"
_call_simple_function 1
}
test_must_pass_with_rbac_roles() {
echo_info "RBAC TEST: function deploy/call must succeed with RBAC roles"
_delete_simple_function
kubeless_recreate $KUBELESS_JSONNET_RBAC $KUBELESS_JSONNET_RBAC
_wait_for_kubeless_controller_ready
_deploy_simple_function
_wait_for_controller_logline "controller synced and ready"
_wait_for_simple_function_pod_ready
_call_simple_function 0
}

test_kubeless_function() {
local func=${1:?}
echo_info "TEST: $func"
kubeless_function_delete ${func}
make -sC examples ${func}
k8s_wait_for_pod_ready -l function=${func}
make -sC examples ${func}-verify
}
# vim: sw=4 ts=4 et si
Loading

0 comments on commit bc472b7

Please sign in to comment.