From 748ec4e4131c56d11e441f5f1014dcab482c33e6 Mon Sep 17 00:00:00 2001 From: Jiaqi Luo <6218999+jiaqiluo@users.noreply.github.com> Date: Mon, 15 Mar 2021 10:36:44 -0700 Subject: [PATCH] add a post-delete-hook for cleaning up the apps left after uninstalling Rancher --- chart/scripts/post-delete-hook.sh | 85 +++++++++++++++++++ ...post-delete-hook-cluster-role-binding.yaml | 19 +++++ .../post-delete-hook-cluster-role.yaml | 27 ++++++ .../post-delete-hook-config-map.yaml | 15 ++++ chart/templates/post-delete-hook-job.yaml | 44 ++++++++++ .../post-delete-hook-service-account.yaml | 12 +++ chart/values.yaml | 15 ++++ scripts/chart/build | 15 ++++ 8 files changed, 232 insertions(+) create mode 100755 chart/scripts/post-delete-hook.sh create mode 100644 chart/templates/post-delete-hook-cluster-role-binding.yaml create mode 100644 chart/templates/post-delete-hook-cluster-role.yaml create mode 100644 chart/templates/post-delete-hook-config-map.yaml create mode 100644 chart/templates/post-delete-hook-job.yaml create mode 100644 chart/templates/post-delete-hook-service-account.yaml diff --git a/chart/scripts/post-delete-hook.sh b/chart/scripts/post-delete-hook.sh new file mode 100755 index 000000000..6ab79bab2 --- /dev/null +++ b/chart/scripts/post-delete-hook.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +set -e + +namespaces="${NAMESPACES}" +timeout="${TIMEOUT}" +ignoreTimeoutError="${IGNORETIMEOUTERROR}" + +if [[ -z ${namespaces} ]]; then + echo "No namespace is provided." + exit 1 +fi + +if [[ -z ${timeout} ]]; then + echo "No timeout value is provided." + exit 1 +fi + +if [[ -z ${ignoreTimeoutError} ]]; then + echo "No ignoreTimeoutError value is provided." + exit 1 +fi + +succeeded=() +failed=() + +get_pod_count() { + kubectl get pods --selector app="${1}" -n "${2}" -o json | jq '.items | length' +} + +echo "Uninstalling Rancher resources in the following namespaces: ${namespaces}" + +for namespace in ${namespaces}; do + for app in $(helm list -n "${namespace}" -q); do + if [[ ${app} =~ .crd$ ]]; then + echo "--- Skip the app [${app}] in the namespace [${namespace}]" + continue + fi + echo "--- Deleting the app [${app}] in the namespace [${namespace}]" + if [[ ! $(helm uninstall "${app}" -n "${namespace}") ]]; then + failed=("${failed[@]}" "${app}") + continue + fi + + t=0 + while true; do + if [[ $(get_pod_count "${app}" "${namespace}") -eq 0 ]]; then + echo "successfully uninstalled [${app}] in the namespace [${namespace}]" + succeeded=("${succeeded[@]}" "${app}") + break + fi + if [[ ${t} -ge ${timeout} ]]; then + echo "timeout uninstalling [${app}] in the namespace [${namespace}]" + failed=("${failed[@]}" "${app}") + break + fi + # by default, wait 120 seconds in total for an app to be uninstalled + echo "waiting 5 seconds for pods of [${app}] to be terminated ..." + sleep 5 + t=$((t + 5)) + done + done + + # delete the helm operator pods + for pod in $(kubectl get pods -n "${namespace}" -o name); do + if [[ ${pod} =~ ^pod\/helm-operation-* ]]; then + echo "--- Deleting the pod [${pod}] in the namespace [${namespace}]" + kubectl delete "${pod}" -n "${namespace}" + fi + done +done + +echo "------ Summary ------" +if [[ ${#succeeded[@]} -ne 0 ]]; then + echo "Succeeded to uninstall the following apps:" "${succeeded[@]}" +fi + +if [[ ${#failed[@]} -ne 0 ]]; then + echo "Failed to uninstall the following apps:" "${failed[@]}" + if [[ "${ignoreTimeoutError}" == "false" ]]; then + exit 2 + fi +else + echo "Cleanup finished successfully." +fi diff --git a/chart/templates/post-delete-hook-cluster-role-binding.yaml b/chart/templates/post-delete-hook-cluster-role-binding.yaml new file mode 100644 index 000000000..476957b49 --- /dev/null +++ b/chart/templates/post-delete-hook-cluster-role-binding.yaml @@ -0,0 +1,19 @@ +{{- if .Values.postDelete.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "rancher.fullname" . }}-post-delete + labels: {{ include "rancher.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-delete + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "rancher.fullname" . }}-post-delete +subjects: + - kind: ServiceAccount + name: {{ template "rancher.fullname" . }}-post-delete + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/chart/templates/post-delete-hook-cluster-role.yaml b/chart/templates/post-delete-hook-cluster-role.yaml new file mode 100644 index 000000000..82d42467c --- /dev/null +++ b/chart/templates/post-delete-hook-cluster-role.yaml @@ -0,0 +1,27 @@ +{{- if .Values.postDelete.enabled }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "rancher.fullname" . }}-post-delete + labels: {{ include "rancher.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-delete + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed +rules: + - apiGroups: [ "extensions","apps" ] + resources: [ "deployments" ] + verbs: [ "get", "list", "delete" ] + - apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "clusterroles", "clusterrolebindings", "roles", "rolebindings" ] + verbs: [ "get", "list", "delete" ] + - apiGroups: [ "" ] + resources: [ "serviceaccounts", "pods", "secrets", "services", "configmaps" ] + verbs: [ "get", "list", "delete" ] + - apiGroups: [ "networking.k8s.io" ] + resources: [ "networkpolicies" ] + verbs: [ "get", "list", "delete" ] + - apiGroups: [ "admissionregistration.k8s.io" ] + resources: [ "validatingwebhookconfigurations" ] + verbs: [ "get", "list", "delete" ] +{{- end }} diff --git a/chart/templates/post-delete-hook-config-map.yaml b/chart/templates/post-delete-hook-config-map.yaml new file mode 100644 index 000000000..eb7b9e66d --- /dev/null +++ b/chart/templates/post-delete-hook-config-map.yaml @@ -0,0 +1,15 @@ +{{- if .Values.postDelete.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "rancher.fullname" . }}-post-delete + namespace: {{ .Release.Namespace }} + labels: {{ include "rancher.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-delete + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed +data: + post-delete-hook.sh: |- +{{ $.Files.Get "scripts/post-delete-hook.sh" | indent 4 }} +{{- end }} diff --git a/chart/templates/post-delete-hook-job.yaml b/chart/templates/post-delete-hook-job.yaml new file mode 100644 index 000000000..661a74757 --- /dev/null +++ b/chart/templates/post-delete-hook-job.yaml @@ -0,0 +1,44 @@ +{{- if .Values.postDelete.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "rancher.fullname" . }}-post-delete + namespace: {{ .Release.Namespace }} + labels: {{ include "rancher.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-delete + "helm.sh/hook-weight": "3" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + backoffLimit: 3 + template: + metadata: + name: {{ template "rancher.fullname" . }}-post-delete + labels: {{ include "rancher.labels" . | nindent 8 }} + spec: + serviceAccountName: {{ template "rancher.fullname" . }}-post-delete + restartPolicy: OnFailure + containers: + - name: {{ template "rancher.name" . }}-post-delete + image: "{{ .Values.systemDefaultRegistry }}{{ .Values.postDelete.image.repository }}:{{ .Values.postDelete.image.tag }}" + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 0 + command: + - /scripts/post-delete-hook.sh + volumeMounts: + - mountPath: /scripts + name: config-volume + env: + - name: NAMESPACES + value: {{ .Values.postDelete.namespaceList | join " " | quote }} + - name: TIMEOUT + value: {{ .Values.postDelete.timeout | quote }} + - name: IGNORETIMEOUTERROR + value: {{ .Values.postDelete.ignoreTimeoutError | quote }} + volumes: + - name: config-volume + configMap: + name: {{ template "rancher.fullname" . }}-post-delete + defaultMode: 0777 +{{- end }} diff --git a/chart/templates/post-delete-hook-service-account.yaml b/chart/templates/post-delete-hook-service-account.yaml new file mode 100644 index 000000000..923687d60 --- /dev/null +++ b/chart/templates/post-delete-hook-service-account.yaml @@ -0,0 +1,12 @@ +{{- if .Values.postDelete.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "rancher.fullname" . }}-post-delete + namespace: {{ .Release.Namespace }} + labels: {{ include "rancher.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-delete + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed +{{- end }} diff --git a/chart/values.yaml b/chart/values.yaml index 548b09054..7eda1d6b7 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -137,3 +137,18 @@ customLogos: # storageClass: "-" accessMode: ReadWriteOnce size: 1Gi + +# Rancher post-delete hook +postDelete: + enabled: true + image: + repository: %POST_DELETE_IMAGE_NAME% + tag: %POST_DELETE_IMAGE_TAG% + namespaceList: + - fleet-system + - cattle-system + - rancher-operator-system + # Number of seconds to wait for an app to be uninstalled + timeout: 120 + # by default, the job will fail if it fail to uninstall any of the apps + ignoreTimeoutError: false diff --git a/scripts/chart/build b/scripts/chart/build index 188c4cef1..46267b606 100755 --- a/scripts/chart/build +++ b/scripts/chart/build @@ -14,3 +14,18 @@ cp -rf ${1} build/chart/rancher sed -i -e "s/%VERSION%/${CHART_VERSION}/g" build/chart/rancher/Chart.yaml sed -i -e "s/%APP_VERSION%/${VERSION}/g" build/chart/rancher/Chart.yaml + +# get the value of shell-image, such as rancher/shell:v0.1.6, from the file pkg/settings/setting.go +post_delete_base=$(grep -i shell-image pkg/settings/setting.go | cut -d "," -f 2 | sed -e 's/"//g' | sed -e 's/)//g' | sed -e 's/ //g') || "" +post_delete_image_name=$(echo "${post_delete_base}" | cut -d ":" -f 1) || "" +post_delete_image_tag=$(echo "${post_delete_base}" | cut -d ":" -f 2) || "" +if [[ ! ${post_delete_image_name} =~ ^rancher\/.+ ]]; then + echo "The image name [$post_delete_image_name] is invalid. Its prefix should be rancher/" + exit 1 +fi +if [[ ! ${post_delete_image_tag} =~ ^v.+ ]]; then + echo "The image tag [$post_delete_image_tag] is invalid. It should start with the letter v" + exit 1 +fi +sed -i -e "s@%POST_DELETE_IMAGE_NAME%@${post_delete_image_name}@g" build/chart/rancher/values.yaml +sed -i -e "s/%POST_DELETE_IMAGE_TAG%/${post_delete_image_tag}/g" build/chart/rancher/values.yaml -- GitLab