kube-addons.sh 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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. printf "%s" "--prune-whitelist ${resource} "
  101. done
  102. }
  103. # KUBECTL_EXTRA_PRUNE_WHITELIST is a list of extra whitelisted resources
  104. # besides the default ones.
  105. extra_prune_whitelist=
  106. if [ -n "${KUBECTL_EXTRA_PRUNE_WHITELIST:-}" ]; then
  107. extra_prune_whitelist=( ${KUBECTL_EXTRA_PRUNE_WHITELIST:-} )
  108. fi
  109. prune_whitelist=( ${KUBECTL_PRUNE_WHITELIST[@]} ${extra_prune_whitelist[@]} )
  110. prune_whitelist_flags=$(generate_prune_whitelist_flags ${prune_whitelist[@]})
  111. log INFO "== Generated kubectl prune whitelist flags: $prune_whitelist_flags =="
  112. # $1 filename of addon to start.
  113. # $2 count of tries to start the addon.
  114. # $3 delay in seconds between two consecutive tries
  115. # $4 namespace
  116. function start_addon() {
  117. local -r addon_filename=$1;
  118. local -r tries=$2;
  119. local -r delay=$3;
  120. local -r namespace=$4
  121. create_resource_from_string "$(cat ${addon_filename})" "${tries}" "${delay}" "${addon_filename}" "${namespace}"
  122. }
  123. # $1 string with json or yaml.
  124. # $2 count of tries to start the addon.
  125. # $3 delay in seconds between two consecutive tries
  126. # $4 name of this object to use when logging about it.
  127. # $5 namespace for this object
  128. function create_resource_from_string() {
  129. local -r config_string=$1;
  130. local tries=$2;
  131. local -r delay=$3;
  132. local -r config_name=$4;
  133. local -r namespace=$5;
  134. while [ ${tries} -gt 0 ]; do
  135. echo "${config_string}" | ${KUBECTL} ${KUBECTL_OPTS} --namespace="${namespace}" apply -f - && \
  136. log INFO "== Successfully started ${config_name} in namespace ${namespace} at $(date -Is)" && \
  137. return 0;
  138. let tries=tries-1;
  139. log WRN "== Failed to start ${config_name} in namespace ${namespace} at $(date -Is). ${tries} tries remaining. =="
  140. sleep ${delay};
  141. done
  142. return 1;
  143. }
  144. function reconcile_addons() {
  145. # TODO: Remove the first command in future release.
  146. # Adding this for backward compatibility. Old addons have CLUSTER_SERVICE_LABEL=true and don't have
  147. # ADDON_MANAGER_LABEL=EnsureExists will still be reconciled.
  148. # Filter out `configured` message to not noisily log.
  149. # `created`, `pruned` and errors will be logged.
  150. log INFO "== Reconciling with deprecated label =="
  151. ${KUBECTL} ${KUBECTL_OPTS} apply -f ${ADDON_PATH} \
  152. -l ${CLUSTER_SERVICE_LABEL}=true,${ADDON_MANAGER_LABEL}!=EnsureExists \
  153. --prune=true ${prune_whitelist_flags} --recursive | grep -v configured
  154. log INFO "== Reconciling with addon-manager label =="
  155. ${KUBECTL} ${KUBECTL_OPTS} apply -f ${ADDON_PATH} \
  156. -l ${CLUSTER_SERVICE_LABEL}!=true,${ADDON_MANAGER_LABEL}=Reconcile \
  157. --prune=true ${prune_whitelist_flags} --recursive | grep -v configured
  158. log INFO "== Kubernetes addon reconcile completed at $(date -Is) =="
  159. }
  160. function ensure_addons() {
  161. # Create objects already exist should fail.
  162. # Filter out `AlreadyExists` message to not noisily log.
  163. ${KUBECTL} ${KUBECTL_OPTS} create -f ${ADDON_PATH} \
  164. -l ${ADDON_MANAGER_LABEL}=EnsureExists --recursive 2>&1 | grep -v AlreadyExists
  165. log INFO "== Kubernetes addon ensure completed at $(date -Is) =="
  166. }
  167. function is_leader() {
  168. # In multi-master setup, only one addon manager should be running. We use
  169. # existing leader election in kube-controller-manager instead of implementing
  170. # a separate mechanism here.
  171. if ! $ADDON_MANAGER_LEADER_ELECTION; then
  172. log INFO "Leader election disabled."
  173. return 0;
  174. fi
  175. KUBE_CONTROLLER_MANAGER_LEADER=`${KUBECTL} -n kube-system get ep kube-controller-manager \
  176. -o go-template=$'{{index .metadata.annotations "control-plane.alpha.kubernetes.io/leader"}}' \
  177. | sed 's/^.*"holderIdentity":"\([^"]*\)".*/\1/' | awk -F'_' '{print $1}'`
  178. # If there was any problem with getting the leader election results, var will
  179. # be empty. Since it's better to have multiple addon managers than no addon
  180. # managers at all, we're going to assume that we're the leader in such case.
  181. log INFO "Leader is $KUBE_CONTROLLER_MANAGER_LEADER"
  182. [[ "$KUBE_CONTROLLER_MANAGER_LEADER" == "" ||
  183. "$HOSTNAME" == "$KUBE_CONTROLLER_MANAGER_LEADER" ]]
  184. }
  185. # The business logic for whether a given object should be created
  186. # was already enforced by salt, and /etc/kubernetes/addons is the
  187. # managed result of that. Start everything below that directory.
  188. log INFO "== Kubernetes addon manager started at $(date -Is) with ADDON_CHECK_INTERVAL_SEC=${ADDON_CHECK_INTERVAL_SEC} =="
  189. # Wait for the default service account to be created in the kube-system namespace.
  190. token_found=""
  191. while [ -z "${token_found}" ]; do
  192. sleep .5
  193. token_found=$(${KUBECTL} ${KUBECTL_OPTS} get --namespace="${SYSTEM_NAMESPACE}" serviceaccount default -o go-template="{{with index .secrets 0}}{{.name}}{{end}}")
  194. if [[ $? -ne 0 ]]; then
  195. token_found="";
  196. log WRN "== Error getting default service account, retry in 0.5 second =="
  197. fi
  198. done
  199. log INFO "== Default service account in the ${SYSTEM_NAMESPACE} namespace has token ${token_found} =="
  200. # Create admission_control objects if defined before any other addon services. If the limits
  201. # are defined in a namespace other than default, we should still create the limits for the
  202. # default namespace.
  203. for obj in $(find /etc/kubernetes/admission-controls \( -name \*.yaml -o -name \*.json \)); do
  204. start_addon "${obj}" 100 10 default &
  205. log INFO "++ obj ${obj} is created ++"
  206. done
  207. # Start the apply loop.
  208. # Check if the configuration has changed recently - in case the user
  209. # created/updated/deleted the files on the master.
  210. log INFO "== Entering periodical apply loop at $(date -Is) =="
  211. while true; do
  212. start_sec=$(date +"%s")
  213. if is_leader; then
  214. ensure_addons
  215. reconcile_addons
  216. else
  217. log INFO "Not elected leader, going back to sleep."
  218. fi
  219. end_sec=$(date +"%s")
  220. len_sec=$((${end_sec}-${start_sec}))
  221. # subtract the time passed from the sleep time
  222. if [[ ${len_sec} -lt ${ADDON_CHECK_INTERVAL_SEC} ]]; then
  223. sleep_time=$((${ADDON_CHECK_INTERVAL_SEC}-${len_sec}))
  224. sleep ${sleep_time}
  225. fi
  226. done