kube-addons.sh 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #!/usr/bin/env bash
  2. # Copyright 2014 The Kubernetes Authors.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. # LIMITATIONS
  16. # 1. Exit code is probably not always correct.
  17. # 2. There are no unittests.
  18. # 3. Will not work if the total length of paths to addons is greater than
  19. # bash can handle. Probably it is not a problem: ARG_MAX=2097152 on GCE.
  20. # cosmetic improvements to be done
  21. # 1. Improve the log function; add timestamp, file name, etc.
  22. # 2. Logging doesn't work from files that print things out.
  23. # 3. Kubectl prints the output to stderr (the output should be captured and then
  24. # logged)
  25. KUBECTL=${KUBECTL_BIN:-/usr/local/bin/kubectl}
  26. KUBECTL_OPTS=${KUBECTL_OPTS:-}
  27. # KUBECTL_PRUNE_WHITELIST is a list of resources whitelisted by
  28. # default.
  29. # This is currently the same with the default in:
  30. # https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/apply.go
  31. KUBECTL_PRUNE_WHITELIST=(
  32. core/v1/ConfigMap
  33. core/v1/Endpoints
  34. core/v1/Namespace
  35. core/v1/PersistentVolumeClaim
  36. core/v1/PersistentVolume
  37. core/v1/Pod
  38. core/v1/ReplicationController
  39. core/v1/Secret
  40. core/v1/Service
  41. batch/v1/Job
  42. batch/v1beta1/CronJob
  43. apps/v1/DaemonSet
  44. apps/v1/Deployment
  45. apps/v1/ReplicaSet
  46. apps/v1/StatefulSet
  47. extensions/v1beta1/Ingress
  48. )
  49. ADDON_CHECK_INTERVAL_SEC=${TEST_ADDON_CHECK_INTERVAL_SEC:-60}
  50. ADDON_PATH=${ADDON_PATH:-/etc/kubernetes/addons}
  51. SYSTEM_NAMESPACE=kube-system
  52. # Addons could use this label with two modes:
  53. # - ADDON_MANAGER_LABEL=Reconcile
  54. # - ADDON_MANAGER_LABEL=EnsureExists
  55. ADDON_MANAGER_LABEL="addonmanager.kubernetes.io/mode"
  56. # This label is deprecated (only for Addon Manager). In future release
  57. # addon-manager may not respect it anymore. Addons with
  58. # CLUSTER_SERVICE_LABEL=true and without ADDON_MANAGER_LABEL=EnsureExists
  59. # will be reconciled for now.
  60. CLUSTER_SERVICE_LABEL="kubernetes.io/cluster-service"
  61. # Whether only one addon manager should be running in a multi-master setup.
  62. # Disabling this flag will force all addon managers to assume they are the
  63. # leaders.
  64. ADDON_MANAGER_LEADER_ELECTION=${ADDON_MANAGER_LEADER_ELECTION:-true}
  65. # Remember that you can't log from functions that print some output (because
  66. # logs are also printed on stdout).
  67. # $1 level
  68. # $2 message
  69. function log() {
  70. # manage log levels manually here
  71. # add the timestamp if you find it useful
  72. case $1 in
  73. DB3 )
  74. # echo "$1: $2"
  75. ;;
  76. DB2 )
  77. # echo "$1: $2"
  78. ;;
  79. DBG )
  80. # echo "$1: $2"
  81. ;;
  82. INFO )
  83. echo "$1: $2"
  84. ;;
  85. WRN )
  86. echo "$1: $2"
  87. ;;
  88. ERR )
  89. echo "$1: $2"
  90. ;;
  91. * )
  92. echo "INVALID_LOG_LEVEL $1: $2"
  93. ;;
  94. esac
  95. }
  96. # Generate kubectl prune-whitelist flags from provided resource list.
  97. function generate_prune_whitelist_flags() {
  98. local -r resources=( "$@" )
  99. for resource in "${resources[@]}"; do
  100. # Check if $resource isn't composed just of whitespaces by replacing ' '
  101. # with '' and checking whether the resulting string is not empty.
  102. if [[ -n "${resource// /}" ]]; then
  103. printf "%s" "--prune-whitelist ${resource} "
  104. fi
  105. done
  106. }
  107. # KUBECTL_EXTRA_PRUNE_WHITELIST is a list of extra whitelisted resources
  108. # besides the default ones.
  109. extra_prune_whitelist=
  110. if [ -n "${KUBECTL_EXTRA_PRUNE_WHITELIST:-}" ]; then
  111. read -ra extra_prune_whitelist <<< "${KUBECTL_EXTRA_PRUNE_WHITELIST}"
  112. fi
  113. prune_whitelist=( "${KUBECTL_PRUNE_WHITELIST[@]}" "${extra_prune_whitelist[@]}" )
  114. prune_whitelist_flags=$(generate_prune_whitelist_flags "${prune_whitelist[@]}")
  115. log INFO "== Generated kubectl prune whitelist flags: $prune_whitelist_flags =="
  116. # $1 filename of addon to start.
  117. # $2 count of tries to start the addon.
  118. # $3 delay in seconds between two consecutive tries
  119. # $4 namespace
  120. function start_addon() {
  121. local -r addon_filename=$1;
  122. local -r tries=$2;
  123. local -r delay=$3;
  124. local -r namespace=$4
  125. create_resource_from_string "$(cat "${addon_filename}")" "${tries}" "${delay}" "${addon_filename}" "${namespace}"
  126. }
  127. # $1 string with json or yaml.
  128. # $2 count of tries to start the addon.
  129. # $3 delay in seconds between two consecutive tries
  130. # $4 name of this object to use when logging about it.
  131. # $5 namespace for this object
  132. function create_resource_from_string() {
  133. local -r config_string=$1;
  134. local tries=$2;
  135. local -r delay=$3;
  136. local -r config_name=$4;
  137. local -r namespace=$5;
  138. while [ "${tries}" -gt 0 ]; do
  139. # shellcheck disable=SC2086
  140. # Disabling because "${KUBECTL_OPTS}" needs to allow for expansion here
  141. echo "${config_string}" | ${KUBECTL} ${KUBECTL_OPTS} --namespace="${namespace}" apply -f - && \
  142. log INFO "== Successfully started ${config_name} in namespace ${namespace} at $(date -Is)" && \
  143. return 0;
  144. (( tries-- ))
  145. log WRN "== Failed to start ${config_name} in namespace ${namespace} at $(date -Is). ${tries} tries remaining. =="
  146. sleep "${delay}";
  147. done
  148. return 1;
  149. }
  150. function reconcile_addons() {
  151. # TODO: Remove the first command in future release.
  152. # Adding this for backward compatibility. Old addons have CLUSTER_SERVICE_LABEL=true and don't have
  153. # ADDON_MANAGER_LABEL=EnsureExists will still be reconciled.
  154. # Filter out `configured` message to not noisily log.
  155. # `created`, `pruned` and errors will be logged.
  156. log INFO "== Reconciling with deprecated label =="
  157. # shellcheck disable=SC2086
  158. # Disabling because "${KUBECTL_OPTS}" needs to allow for expansion here
  159. ${KUBECTL} ${KUBECTL_OPTS} apply -f ${ADDON_PATH} \
  160. -l ${CLUSTER_SERVICE_LABEL}=true,${ADDON_MANAGER_LABEL}!=EnsureExists \
  161. --prune=true ${prune_whitelist_flags} --recursive | grep -v configured
  162. log INFO "== Reconciling with addon-manager label =="
  163. # shellcheck disable=SC2086
  164. # Disabling because "${KUBECTL_OPTS}" needs to allow for expansion here
  165. ${KUBECTL} ${KUBECTL_OPTS} apply -f ${ADDON_PATH} \
  166. -l ${CLUSTER_SERVICE_LABEL}!=true,${ADDON_MANAGER_LABEL}=Reconcile \
  167. --prune=true ${prune_whitelist_flags} --recursive | grep -v configured
  168. log INFO "== Kubernetes addon reconcile completed at $(date -Is) =="
  169. }
  170. function ensure_addons() {
  171. # Create objects already exist should fail.
  172. # Filter out `AlreadyExists` message to not noisily log.
  173. # shellcheck disable=SC2086
  174. # Disabling because "${KUBECTL_OPTS}" needs to allow for expansion here
  175. ${KUBECTL} ${KUBECTL_OPTS} create -f ${ADDON_PATH} \
  176. -l ${ADDON_MANAGER_LABEL}=EnsureExists --recursive 2>&1 | grep -v AlreadyExists
  177. log INFO "== Kubernetes addon ensure completed at $(date -Is) =="
  178. }
  179. function is_leader() {
  180. # In multi-master setup, only one addon manager should be running. We use
  181. # existing leader election in kube-controller-manager instead of implementing
  182. # a separate mechanism here.
  183. if ! $ADDON_MANAGER_LEADER_ELECTION; then
  184. log INFO "Leader election disabled."
  185. return 0;
  186. fi
  187. # shellcheck disable=SC2086
  188. # Disabling because "${KUBECTL_OPTS}" needs to allow for expansion here
  189. KUBE_CONTROLLER_MANAGER_LEADER=$(${KUBECTL} ${KUBECTL_OPTS} -n kube-system get ep kube-controller-manager \
  190. -o go-template=$'{{index .metadata.annotations "control-plane.alpha.kubernetes.io/leader"}}' \
  191. | sed 's/^.*"holderIdentity":"\([^"]*\)".*/\1/' | awk -F'_' '{print $1}')
  192. # If there was any problem with getting the leader election results, var will
  193. # be empty. Since it's better to have multiple addon managers than no addon
  194. # managers at all, we're going to assume that we're the leader in such case.
  195. log INFO "Leader is $KUBE_CONTROLLER_MANAGER_LEADER"
  196. [[ "$KUBE_CONTROLLER_MANAGER_LEADER" == "" ||
  197. "$HOSTNAME" == "$KUBE_CONTROLLER_MANAGER_LEADER" ]]
  198. }
  199. # The business logic for whether a given object should be created
  200. # was already enforced by salt, and /etc/kubernetes/addons is the
  201. # managed result of that. Start everything below that directory.
  202. log INFO "== Kubernetes addon manager started at $(date -Is) with ADDON_CHECK_INTERVAL_SEC=${ADDON_CHECK_INTERVAL_SEC} =="
  203. # Wait for the default service account to be created in the kube-system namespace.
  204. token_found=""
  205. while [ -z "${token_found}" ]; do
  206. sleep .5
  207. # shellcheck disable=SC2086
  208. # Disabling because "${KUBECTL_OPTS}" needs to allow for expansion here
  209. if ! token_found=$(${KUBECTL} ${KUBECTL_OPTS} get --namespace="${SYSTEM_NAMESPACE}" serviceaccount default -o go-template="{{with index .secrets 0}}{{.name}}{{end}}"); then
  210. token_found="";
  211. log WRN "== Error getting default service account, retry in 0.5 second =="
  212. fi
  213. done
  214. log INFO "== Default service account in the ${SYSTEM_NAMESPACE} namespace has token ${token_found} =="
  215. # Create admission_control objects if defined before any other addon services. If the limits
  216. # are defined in a namespace other than default, we should still create the limits for the
  217. # default namespace.
  218. while IFS=$'\n' read -r obj; do
  219. start_addon "${obj}" 100 10 default &
  220. log INFO "++ obj ${obj} is created ++"
  221. done < <(find /etc/kubernetes/admission-controls \( -name \*.yaml -o -name \*.json \))
  222. # Start the apply loop.
  223. # Check if the configuration has changed recently - in case the user
  224. # created/updated/deleted the files on the master.
  225. log INFO "== Entering periodical apply loop at $(date -Is) =="
  226. while true; do
  227. start_sec=$(date +"%s")
  228. if is_leader; then
  229. ensure_addons
  230. reconcile_addons
  231. else
  232. log INFO "Not elected leader, going back to sleep."
  233. fi
  234. end_sec=$(date +"%s")
  235. len_sec=$((end_sec-start_sec))
  236. # subtract the time passed from the sleep time
  237. if [[ ${len_sec} -lt ${ADDON_CHECK_INTERVAL_SEC} ]]; then
  238. sleep_time=$((ADDON_CHECK_INTERVAL_SEC-len_sec))
  239. sleep ${sleep_time}
  240. fi
  241. done