Created
October 14, 2016 14:03
-
-
Save aklinkert/5d2e4b81d7cf3c0be524663c63dc3da6 to your computer and use it in GitHub Desktop.
Kubernetes continuous integration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -e | |
VERSION="${CIRCLE_SHA1}" | |
KUBE_CONTEXT="$(kubectl config current-context)" | |
# getKubeValue "${KUBE_CONTEXT}" "${KUBE_NAMESPACE}" "${deployment}" "${path}" | |
getKubeValue() { | |
kubectl --context "${1}" --namespace "${2}" get "deployment/${3}" -o "jsonpath={${4}}" | |
} | |
# getKubeRevision "${KUBE_CONTEXT}" "${KUBE_NAMESPACE}" "${deployment}" | |
getKubeRevision() { | |
kubectl --context "${1}" --namespace "${2}" get "deployment/${3}" -o go-template --template='{{ index .metadata.annotations "deployment.kubernetes.io/revision"}}' | |
} | |
# updateKubernetesDeployment "${KUBE_CONTEXT}" "${KUBE_NAMESPACE}" "${deployment}" "${repo}" | |
updateKubernetesDeployment() { | |
local deployedImage deployedRepo currentRevision wantedReplicas availableReplicas updatedReplicas wantedGeneration observedGeneration | |
# first check, if desired deployment does exist. If not, we can exit early | |
if ! kubectl --context "${KUBE_CONTEXT}" --namespace "${KUBE_NAMESPACE}" describe deployment "${deployment}" > /dev/null ; then | |
echo "Deployment '${deployment}' does not exist." | |
return 0 | |
fi | |
deployedImage="$(getKubeValue "${1}" "${2}" "${3}" ".spec.template.spec.containers[0].image")" | |
deployedRepo="$(echo "${deployedImage}" |cut -d':' -f1)" | |
echo "Deployment ${3} has currently the repo ${deployedRepo} deployed, trying to match repo ${4} ..." | |
# next we check, that the deployment we wanna update has given repo deployed. If not, we can exit early | |
if ! [[ "${deployedRepo}" == "${4}" ]]; then | |
echo "Deployment '${deployment}' does not contain docker repo ${repo}." | |
return 0 | |
fi | |
# get the current revision to enable rollbacks | |
currentRevision=$(getKubeRevision "${1}" "${2}" "${3}") | |
echo "Current revision is ${currentRevision}" | |
# actually update the deployment to new image version | |
echo "Updating kubernetes deployment ${3} to use ${4}:${VERSION} in env ${2} ..." | |
kubectl --context "${1}" --namespace "${2}" patch deployment "${3}" -p "{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\": \"${3}\",\"image\":\"${4}:${VERSION}\"}]}}}}" | |
echo "Updated to revision $(getKubeRevision "${1}" "${2}" "${3}")" | |
sleep 1 | |
# try 30 times to check the deployment for success | |
for i in $(seq 1 30); do | |
wantedReplicas="$(getKubeValue "${1}" "${2}" "${3}" ".spec.replicas")" | |
availableReplicas="$(getKubeValue "${1}" "${2}" "${3}" ".status.availableReplicas")" | |
updatedReplicas="$(getKubeValue "${1}" "${2}" "${3}" ".status.updatedReplicas")" | |
wantedGeneration="$(getKubeValue "${1}" "${2}" "${3}" ".metadata.generation")" | |
observedGeneration="$(getKubeValue "${1}" "${2}" "${3}" ".status.observedGeneration")" | |
echo "check ${i} | wanted repl: ${wantedReplicas}; updated repl: ${updatedReplicas}; available repl: ${availableReplicas}; wanted gen: ${wantedGeneration}; observed gen: ${observedGeneration}" | |
# when the deployment was successful, we can exit the function, our job here is done | |
if [[ "${wantedReplicas}" == "${updatedReplicas}" ]] && [[ "${wantedGeneration}" && "${observedGeneration}" ]] && [[ 0 -lt "${availableReplicas}" ]]; then | |
echo "Kubernetes deployment of ${3} was successful after ${i} check iterations." | |
return 0 | |
fi | |
done | |
# when the check loop ended without returning, we can assume that the deployment is broken. In this case, we roll back to the previous reversion. | |
echo "Kubernetes deployment of ${3} was NOT successful! Rolling back to previous revision ${currentRevision} ..." | |
kubectl --context "${1}" --namespace "${2}" rollout undo "deployment/${3}" --to-revision="${currentRevision}" | |
return 1 | |
} | |
echo "Using kubectl context ${KUBE_CONTEXT} and namespace ${KUBE_NAMESPACE}" | |
echo "checking if context for env exists ... " | |
if ! kubectl get nodes --context "${KUBE_CONTEXT}" > /dev/null ; then | |
echo "Desired context does not exist" | |
exit 0 | |
fi | |
if [ -z "${KUBE_DEPLOYMENTS}" ] && [ -n "${KUBE_DEPLOYMENT:-}" ]; then | |
KUBE_DEPLOYMENTS="${KUBE_DEPLOYMENT}" | |
fi | |
if [ -z "${KUBE_DEPLOYMENTS}" ]; then | |
echo "No kubernetes deployment env var set" | |
exit 0 | |
fi | |
declare -a deployments | |
deployments=(${KUBE_DEPLOYMENTS}) | |
declare -a repos | |
repos=(${DOCKER_REPOS}) | |
for repo in "${repos[@]}" ; do | |
for deployment in "${deployments[@]}" ; do | |
updateKubernetesDeployment "${KUBE_CONTEXT}" "${KUBE_NAMESPACE}" "${deployment}" "${repo}" | |
done | |
done | |
echo "Done." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment