From 9ff506ca03ac35f63246b90fe215b4a0c3c17d7a Mon Sep 17 00:00:00 2001 From: Seth Goings Date: Tue, 24 Nov 2015 16:34:55 -0700 Subject: [PATCH] feat(_test): create gke cluster + test all charts on master - improve healthcheck of services - add multiple styles of searching for pods - add travis.yml tweaks --- .gitignore | 2 + .travis.yml | 4 + Makefile | 2 + _test/config.sh | 30 +++++ _test/gke.sh | 50 ++++++++ _test/lib.sh | 144 ++++++++++++++++++++++ _test/secrets/gcloud-credentials.json.enc | Bin 0 -> 2304 bytes _test/test-charts | 31 +++++ 8 files changed, 263 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Makefile create mode 100644 _test/config.sh create mode 100644 _test/gke.sh create mode 100644 _test/lib.sh create mode 100644 _test/secrets/gcloud-credentials.json.enc create mode 100755 _test/test-charts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..e3efa93d81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.bin +/_test/secrets/*.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..f61c1179be --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: bash + +script: + - make test-charts diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..77e1990e02 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +test-charts: + @./_test/test-charts $(TEST_CHARTS) diff --git a/_test/config.sh b/_test/config.sh new file mode 100644 index 0000000000..0ca3fd8868 --- /dev/null +++ b/_test/config.sh @@ -0,0 +1,30 @@ +export TEST_ROOT_DIR="$(cd "$(dirname $(dirname $0))"; pwd)" +export HELM_ARTIFACT_REPO="${HELM_ARTIFACT_REPO:-helm-ci}" +CLUSTER_NAME="${CLUSTER_NAME:-helm-test-travis}" +GOOGLE_SDK_DIR="${HOME}/google-cloud-sdk" +export HELM_BIN="${TEST_ROOT_DIR}/helm.bin" +export TEST_DIR="${TEST_ROOT_DIR}/_test" +export SECRETS_DIR="${TEST_DIR}/secrets" +SKIP_DESTROY=${SKIP_DESTROY:-false} + +export HEALTHCHECK_TIMEOUT_SEC=120 + +GCLOUD_CREDENTIALS_FILE="${GCLOUD_CREDENTIALS_FILE:-"${SECRETS_DIR}/gcloud-credentials.json"}" + +GCLOUD_PROJECT_ID="${GCLOUD_PROJECT_ID:-${CLUSTER_NAME}}" +K8S_ZONE="${K8S_ZONE:-us-central1-b}" +K8S_CLUSTER_NAME="${K8S_CLUSTER_NAME:-${GCLOUD_PROJECT_ID}-$(openssl rand -hex 2)}" + + + +# Text color variables +txtund=$(tput sgr 0 1) # Underline +txtbld=$(tput bold) # Bold +bldred=${txtbld}$(tput setaf 1) # red +bldblu=${txtbld}$(tput setaf 4) # blue +bldwht=${txtbld}$(tput setaf 7) # white +txtrst=$(tput sgr0) # Reset + +pass="${bldblu}-->${txtrst}" +warn="${bldred}-->${txtrst}" +ques="${bldblu}???${txtrst}" diff --git a/_test/gke.sh b/_test/gke.sh new file mode 100644 index 0000000000..8b2c2f792a --- /dev/null +++ b/_test/gke.sh @@ -0,0 +1,50 @@ +function gke-install { + if [ ! -d "${GOOGLE_SDK_DIR}" ]; then + export CLOUDSDK_CORE_DISABLE_PROMPTS=1 + curl https://sdk.cloud.google.com | bash + fi + + export PATH="{GOOGLE_SDK_DIR}/bin:$PATH" + gcloud -q components update kubectl +} + +function gke-login { + if [ -f ${GCLOUD_CREDENTIALS_FILE} ]; then + gcloud -q auth activate-service-account --key-file "${GCLOUD_CREDENTIALS_FILE}" + else + log-warn "No credentials file located at ${GCLOUD_CREDENTIALS_FILE}" + log-warn "You can set this via GCLOUD_CREDENTIALS_FILE" + return 1 + fi +} + +function gke-config { + gcloud -q config set project "${GCLOUD_PROJECT_ID}" + gcloud -q config set compute/zone "${K8S_ZONE}" +} + +function gke-create-cluster { + log-lifecycle "Creating cluster ${K8S_CLUSTER_NAME}" + gcloud -q container clusters create "${K8S_CLUSTER_NAME}" + gcloud -q config set container/cluster "${K8S_CLUSTER_NAME}" + gcloud -q container clusters get-credentials "${K8S_CLUSTER_NAME}" +} + +function gke-destroy { + if [ "${SKIP_DESTROY}" == false ]; then + log-lifecycle "Destroying cluster ${K8S_CLUSTER_NAME}" + if command -v gcloud &>/dev/null; then + gcloud -q container clusters delete "${K8S_CLUSTER_NAME}" --no-wait + log-info "Cluster ${K8S_CLUSTER_NAME} scheduled for destruction. Muahaha." + fi + else + log-warn "Skipping destruction of ${K8S_CLUSTER_NAME} (SKIP_DESTROY=${SKIP_DESTROY})" + fi +} + +function setup-gke { + gke-install + gke-login + gke-config + gke-create-cluster +} diff --git a/_test/lib.sh b/_test/lib.sh new file mode 100644 index 0000000000..8d2603565e --- /dev/null +++ b/_test/lib.sh @@ -0,0 +1,144 @@ +function jigger-files-for-travis { + helm update # need ~/.helm/config.yml + + if [ "${TRAVIS}" == true ]; then + mkdir -p ${TRAVIS_HOME}/.helm/cache + cp -r . ${TRAVIS_HOME}/.helm/cache + fi +} + +function install-helm { + # Uses HELM_ARTIFACT_REPO to determine which repository to grab helm from + + log-lifecycle "Installing helm into $(pwd)/.bin" + + mkdir -p .bin + ( + cd .bin + curl -s https://get.helm.sh | sh + ) + export PATH="$(pwd)/.bin:${PATH}" + + jigger-files-for-travis +} + +function get-changed-charts { + git diff --name-only HEAD origin/HEAD -- charts \ + | cut -d/ -f 1-2 \ + | sort \ + | uniq +} + +function ensure-dirs-exist { + local dirs="${@}" + local pruned + + # ensure directories exist and just output directory name + for dir in ${dirs}; do + if [ -d ${dir} ]; then + pruned+="$(basename "${dir}")\n" + fi + done + + echo -e "${pruned}" +} + +function generate-test-plan { + ensure-dirs-exist "$(get-changed-charts)" +} + +function get-all-charts { + local chartlist="$(find . -name Chart.yaml)" + local cleanedlist + + local chart + for chart in ${chartlist}; do + cleanedlist+="$(basename $(dirname ${chart})) " + done + + echo "${cleanedlist}" +} + +function helm-test { + log-warn "Start: ${1}" + .bin/helm install ${1} + healthcheck ${1} + .bin/helm uninstall -y ${1} + log-warn "Done: ${1}" +} + +function run-test-plan { + local test_plan + + if is-pull-request; then + test_plan="$(generate-test-plan)" + else + if [ -z "${TEST_CHARTS}" ]; then + test_plan="$(get-all-charts)" + else + test_plan="${TEST_CHARTS}" + fi + fi + + log-lifecycle "Running test plan" + log-info "Charts to be tested:" + echo "${test_plan}" + + local plan + for plan in ${test_plan}; do + helm-test ${plan} + done +} + +function is-pull-request { + if [ ! -z ${TRAVIS} ] && \ + [ ${TRAVIS_PULL_REQUEST} != false ]; then + log-warn "This is a pull request." + return 0 + else + return 1 + fi +} + +function is-pod-running { + local name="${1}" + + if kubectl get pods "${name}" &> /dev/null; then + log-info "Looking for pod named ${name}" + local jq_name_query=".status.phase" + kubectl get pods ${name} -o json | jq -r "${jq_name_query}" | grep -q "Running" && return 0 + fi + + log-info "Couldn't find pod named ${name}, looking for label: app=${name}" + local jq_app_label_query=".items[] | select(.metadata.labels.app == \"${name}\") | .status.phase" + kubectl get pods -o json | jq -r "${jq_app_label_query}" | grep -q "Running" && return 0 + + log-info "Couldn't find label: app=${name}, looking for label: provider=${name}" + local jq_provider_label_query=".items[] | select(.metadata.labels.provider == \"${name}\") | .status.phase" + kubectl get pods -o json | jq -r "${jq_provider_label_query}" | grep -q "Running" && return 0 +} + +function healthcheck { + WAIT_TIME=1 + log-lifecycle "Checking: ${1}" + until is-pod-running "${1}"; do + sleep 1 + (( WAIT_TIME += 1 )) + if [ ${WAIT_TIME} -gt ${HEALTHCHECK_TIMEOUT_SEC} ]; then + return 1 + fi + done + log-lifecycle "Checked!: ${1}" +} + +function log-lifecycle { + echo "${bldblu}==> ${@}...${txtrst}" +} + +function log-info { + echo "${wht}--> ${@}${txtrst}" +} + +function log-warn { + echo "${bldred}--> ${@}${txtrst}" +} diff --git a/_test/secrets/gcloud-credentials.json.enc b/_test/secrets/gcloud-credentials.json.enc new file mode 100644 index 0000000000000000000000000000000000000000..0954b0ab08e9af3a80a2cc78df7167fdb38f7dd3 GIT binary patch literal 2304 zcmV+b3IFzxjl2fEU_&Lbn2QYAFYk8Fjyn27Gf3=iK~1(}7>1SYNaLE*@so6QL1q}% z;Bt-h-}&&T{CMM{B|tO2Eig@7y{89m;de+ONgmia?CH|QCzEwtbKtQL}QG; zbWzEhskLVkS9#A#2PT^N62+Sg&XV`-UEpDGFoFSAQFH8dX5HEX0zY)(L7_bV64anV z*4aqJh2gXS%sd5=H9z|VFv*_BkupQSLx=H>vFvb{X+@|$WFAzsy}^J~$_myf{UhpP zP956brnl7Cx1voz?^4Mf)4$08VNU9^0pgHLSZvXNugrHY)+L-Ka-~)|micipL;s;{ ze7ZDVPBFz5#BLvrS}lGW>uf)ld4cSF8pFznujzq-HR{K~FtqI7sh6X~u6oc_@U7Li zalg%aX3LZWmAw-nkaN+Wo$5zAwJ#rr+Rf*|#E+HU_yf z;I?U^jCi73)hsZbooN>r6AxCbnk9a;2eT&H`pfW+9sqD*yxU0O>RUQlbw8>1Pw|1E z3eQ*a&8>uH$1(~yno=M%qX{3Y=#N{c34*e5nv7pzoi(ftYA^dwuY*`N)jfe(kYXTN z6}u4|Qqw2|Dw=zDEc#M~vV)Dntf0m-u_njIoqL~B0|F}w+>xJk&8w%xzSqBW^Ht}G zpkj~HfaOO2v`R4C2<#b2^KO99u!|IdIvF3j(baVW+F5{`X zdnXx$IT59l%2ViQ-XoCPaTcsQ4b*oWx@M)-W%x{RX~53&a4~HuH+XhhjwO`n#8d$$ z42V6}`fhf7%MRm0K?4uby>_X1*{EXOn8wkr#HMsT?lr^~(%Wn5{=IUPt=Jig(G2gwn{`wW25Dmu~s$R+= zqCjhrX+%2Z9;-1a??2q?qS`Bs4I1dcU<55_iw7=4?WxVQaILNUB${bNlD)T4y)&@a z;`HcJ`Op|S?mZieY|1zyC<*(kiSbttlGv`XotFgP0(Jt;Xd;ib36f+A zlOqmBvCZ$0Lcz0gmbs8f_>Mq5^LQ)}q@X%->*{nkH_UoPTy2KLhqVBkc4(&pFcuGS z3s0-?a`cHtBGM5u-y)OEHy1g-%g09?QIxrP?pb#0IR4&AOEtZNAeCW47LD>jqv#ZY z8OG8XUXRVou+UJE<@eU{Ck(Q9RO_JB@O+_N*_z%Qm#ny!;h=7`b+z$5!k>9@_q2_` zL;%C&V8r_DAu*DvZ}Fz8(mPrZjKJC7V%Pf_-Tjh1+5#;zvNjd$8uU0o?~N8C z^XD)h@oE3!E}#W>>*lGtsT^2J?i zE&QavO*ij+fgecs^Qal0IT^Ep;&CS!mN1CW=%~GGoQbg^fu8d5y}&u8h9psmNHYI> z0%U_)ju!sLctLeGFy(I68j)lG98&*e`Z^KIB_BZWU!g>J0($QWLg6j!Ofki;pPD3& zfpEM=n8d40?r>Pwb};=eM8NG=b)Wwea{fy);C#&+v`8 zfYF>#>gu+%c$;VoTA_if+^J?<`9#XM>Oh=vMVk7am>|tANW}nj<(&y|xD1P&t=aNg zUI`U$H0p-r){;ZBkQj(*8j18FV}k)=RpX=yQXLqSld81qvO#%@p=;WMhnKJw3DsTP zK%XW6dN>Zk(Ha)c8-}eGnhurkt1e#{B^Mtq$z>OB_X{suD7}eEXsSQzU*CW`Uxas^ z@D|2}_~B{EeVFg?@P zjSHX_nFNLIeyB6&lYiGwzl zKM9z($R@B}*SnW=ntdHlK4+JaABa7V!!lOJBBDC?y;*x}|IDE3MDSVVu>5kanHjjk+Nj1H$tvdbO`;1C&=zXq$o4?%C$+^IO zFeUm9^Yg9Sd?}PZrg|+J%E99yDFHEfBde;l{ZG8zGs70pq`}^eoYALOa4F08y$O`K z5|(sdhU94*zJZS{uh;XN;eJLW_wG`R6$Kxq{3^+uVIP|)ZV`!0kMGS%s}g2gs0NLy aEGoyLNBQ7lbr#;f7=T>I45}D(YD7*XM3I01 literal 0 HcmV?d00001 diff --git a/_test/test-charts b/_test/test-charts new file mode 100755 index 0000000000..1090a19617 --- /dev/null +++ b/_test/test-charts @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -eo pipefail + +source _test/config.sh + +cd "${TEST_ROOT_DIR}" + +source _test/lib.sh +source _test/gke.sh + +if [ "${TRAVIS}" == true ]; then + if [ "${TRAVIS_PULL_REQUEST}" != false ]; then + log-lifecycle "This is a pull request build. Skipping chart tests." + exit 0 + fi + + # decrypt gcloud credentials if on Travis and building the master branch + openssl aes-256-cbc -K $encrypted_e967971bb2cd_key \ + -iv $encrypted_e967971bb2cd_iv \ + -in _test/secrets/gcloud-credentials.json.enc \ + -out _test/secrets/gcloud-credentials.json -d +fi + +trap gke-destroy EXIT ERR + +install-helm + +setup-gke + +run-test-plan