k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cluster/gce/upgrade.sh (about) 1 #!/usr/bin/env bash 2 3 # Copyright 2015 The Kubernetes Authors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 # !!!EXPERIMENTAL !!! Upgrade script for GCE. Expect this to get 18 # rewritten in Go in relatively short order, but it allows us to start 19 # testing the concepts. 20 21 set -o errexit 22 set -o nounset 23 set -o pipefail 24 25 if [[ "${KUBERNETES_PROVIDER:-gce}" != "gce" ]]; then 26 echo "!!! ${1} only works on GCE" >&2 27 exit 1 28 fi 29 30 KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. 31 source "${KUBE_ROOT}/hack/lib/util.sh" 32 source "${KUBE_ROOT}/cluster/kube-util.sh" 33 34 function usage() { 35 echo "!!! EXPERIMENTAL !!!" 36 echo "!!! This upgrade script is not meant to be run in production !!!" 37 echo "" 38 echo "${0} [-M | -N | -P] [-o] (-l | <version number or publication>)" 39 echo " Upgrades master and nodes by default" 40 echo " -M: Upgrade master only" 41 echo " -N: Upgrade nodes only" 42 echo " -P: Node upgrade prerequisites only (create a new instance template)" 43 echo " -c: Upgrade NODE_UPGRADE_PARALLELISM nodes in parallel (default=1) within a single instance group. The MIGs themselves are dealt serially." 44 echo " -o: Use os distro specified in KUBE_NODE_OS_DISTRIBUTION for new nodes. Options include 'debian' or 'gci'" 45 echo " -l: Use local(dev) binaries. This is only supported for master upgrades." 46 echo "" 47 echo ' Version number or publication is either a proper version number' 48 echo ' (e.g. "v1.0.6", "v1.2.0-alpha.1.881+376438b69c7612") or a version' 49 echo ' publication of the form <bucket>/<version> (e.g. "release/stable",' 50 echo ' "ci/latest-1"). Some common ones are:' 51 echo ' - "release/stable"' 52 echo ' - "release/latest"' 53 echo ' - "ci/latest"' 54 echo ' See the docs on getting builds for more information about version publication.' 55 echo "" 56 echo "(... Fetching current release versions ...)" 57 echo "" 58 59 # NOTE: IF YOU CHANGE THE FOLLOWING LIST, ALSO UPDATE test/e2e/cluster_upgrade.go 60 local release_stable 61 local release_latest 62 local ci_latest 63 64 release_stable=$(curl -sL https://dl.k8s.io/release/stable.txt) 65 release_latest=$(curl -sL https://dl.k8s.io/release/latest.txt) 66 ci_latest=$(curl -sL https://dl.k8s.io/ci/latest.txt) 67 68 echo "Right now, versions are as follows:" 69 echo " release/stable: ${0} ${release_stable}" 70 echo " release/latest: ${0} ${release_latest}" 71 echo " ci/latest: ${0} ${ci_latest}" 72 } 73 74 function print-node-version-info() { 75 echo "== $1 Node OS and Kubelet Versions ==" 76 "${KUBE_ROOT}/cluster/kubectl.sh" get nodes -o=jsonpath='{range .items[*]}name: "{.metadata.name}", osImage: "{.status.nodeInfo.osImage}", kubeletVersion: "{.status.nodeInfo.kubeletVersion}"{"\n"}{end}' 77 } 78 79 function upgrade-master() { 80 local num_masters 81 num_masters=$(get-master-replicas-count) 82 if [[ "${num_masters}" -gt 1 ]]; then 83 echo "Upgrade of master not supported if more than one master replica present. The current number of master replicas: ${num_masters}" 84 exit 1 85 fi 86 87 echo "== Upgrading master to '${SERVER_BINARY_TAR_URL}'. Do not interrupt, deleting master instance. ==" 88 89 # Tries to figure out KUBE_USER/KUBE_PASSWORD by first looking under 90 # kubeconfig:username, and then under kubeconfig:username-basic-auth. 91 # TODO: KUBE_USER is used in generating ABAC policy which the 92 # apiserver may not have enabled. If it's enabled, we must have a user 93 # to generate a valid ABAC policy. If the username changes, should 94 # the script fail? Should we generate a default username and password 95 # if the section is missing in kubeconfig? Handle this better in 1.5. 96 get-kubeconfig-basicauth 97 get-kubeconfig-bearertoken 98 99 detect-master 100 parse-master-env 101 upgrade-master-env 102 103 # Delete the master instance. Note that the master-pd is created 104 # with auto-delete=no, so it should not be deleted. 105 gcloud compute instances delete \ 106 --project "${PROJECT}" \ 107 --quiet \ 108 --zone "${ZONE}" \ 109 "${MASTER_NAME}" 110 111 create-master-instance "${MASTER_NAME}-ip" 112 wait-for-master 113 } 114 115 function upgrade-master-env() { 116 echo "== Upgrading master environment variables. ==" 117 # Generate the node problem detector token if it isn't present on the original 118 # master. 119 if [[ "${ENABLE_NODE_PROBLEM_DETECTOR:-}" == "standalone" && "${NODE_PROBLEM_DETECTOR_TOKEN:-}" == "" ]]; then 120 NODE_PROBLEM_DETECTOR_TOKEN=$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64 | tr -d "=+/" | dd bs=32 count=1 2>/dev/null) 121 fi 122 } 123 124 function wait-for-master() { 125 echo "== Waiting for new master to respond to API requests ==" 126 127 local curl_auth_arg 128 if [[ -n ${KUBE_BEARER_TOKEN:-} ]]; then 129 curl_auth_arg=(-H "Authorization: Bearer ${KUBE_BEARER_TOKEN}") 130 elif [[ -n ${KUBE_PASSWORD:-} ]]; then 131 curl_auth_arg=(--user "${KUBE_USER}:${KUBE_PASSWORD}") 132 else 133 echo "can't get auth credentials for the current master" 134 exit 1 135 fi 136 137 until curl --insecure "${curl_auth_arg[@]}" --max-time 5 \ 138 --fail --output /dev/null --silent "https://${KUBE_MASTER_IP}/healthz"; do 139 printf "." 140 sleep 2 141 done 142 143 echo "== Done ==" 144 } 145 146 # Perform common upgrade setup tasks 147 # 148 # Assumed vars 149 # KUBE_VERSION 150 function prepare-upgrade() { 151 kube::util::ensure-temp-dir 152 detect-project 153 detect-subnetworks 154 detect-node-names # sets INSTANCE_GROUPS 155 write-cluster-location 156 write-cluster-name 157 tars_from_version 158 } 159 160 # Reads kube-env metadata from first node in NODE_NAMES. 161 # 162 # Assumed vars: 163 # NODE_NAMES 164 # PROJECT 165 # ZONE 166 function get-node-env() { 167 # TODO(zmerlynn): Make this more reliable with retries. 168 gcloud compute --project "${PROJECT}" ssh --zone "${ZONE}" "${NODE_NAMES[0]}" --command \ 169 "curl --fail --silent -H 'Metadata-Flavor: Google' \ 170 'http://metadata/computeMetadata/v1/instance/attributes/kube-env'" 2>/dev/null 171 } 172 173 # Read os distro information from /os/release on node. 174 # $1: The name of node 175 # 176 # Assumed vars: 177 # PROJECT 178 # ZONE 179 function get-node-os() { 180 gcloud compute ssh "$1" \ 181 --project "${PROJECT}" \ 182 --zone "${ZONE}" \ 183 --command \ 184 "cat /etc/os-release | grep \"^ID=.*\" | cut -c 4-" 185 } 186 187 # Assumed vars: 188 # KUBE_VERSION 189 # NODE_SCOPES 190 # NODE_INSTANCE_PREFIX 191 # PROJECT 192 # ZONE 193 # 194 # Vars set: 195 # KUBE_PROXY_TOKEN 196 # NODE_PROBLEM_DETECTOR_TOKEN 197 # CA_CERT_BASE64 198 # EXTRA_DOCKER_OPTS 199 # KUBELET_CERT_BASE64 200 # KUBELET_KEY_BASE64 201 function upgrade-nodes() { 202 prepare-node-upgrade 203 do-node-upgrade 204 } 205 206 function setup-base-image() { 207 if [[ "${env_os_distro}" == "false" ]]; then 208 echo "== Ensuring that new Node base OS image matched the existing Node base OS image" 209 NODE_OS_DISTRIBUTION=$(get-node-os "${NODE_NAMES[0]}") 210 211 if [[ "${NODE_OS_DISTRIBUTION}" == "cos" ]]; then 212 NODE_OS_DISTRIBUTION="gci" 213 fi 214 215 source "${KUBE_ROOT}/cluster/gce/${NODE_OS_DISTRIBUTION}/node-helper.sh" 216 # Reset the node image based on current os distro 217 set-linux-node-image 218 fi 219 } 220 221 # prepare-node-upgrade creates a new instance template suitable for upgrading 222 # to KUBE_VERSION and echos a single line with the name of the new template. 223 # 224 # Assumed vars: 225 # KUBE_VERSION 226 # NODE_SCOPES 227 # NODE_INSTANCE_PREFIX 228 # PROJECT 229 # ZONE 230 # 231 # Vars set: 232 # SANITIZED_VERSION 233 # INSTANCE_GROUPS 234 # KUBE_PROXY_TOKEN 235 # NODE_PROBLEM_DETECTOR_TOKEN 236 # CA_CERT_BASE64 237 # EXTRA_DOCKER_OPTS 238 # KUBELET_CERT_BASE64 239 # KUBELET_KEY_BASE64 240 function prepare-node-upgrade() { 241 echo "== Preparing node upgrade (to ${KUBE_VERSION}). ==" >&2 242 setup-base-image 243 244 SANITIZED_VERSION="${KUBE_VERSION//[\.\+]/-}" 245 246 # TODO(zmerlynn): Refactor setting scope flags. 247 local scope_flags= 248 if [ -n "${NODE_SCOPES}" ]; then 249 scope_flags="--scopes ${NODE_SCOPES}" 250 else 251 # shellcheck disable=SC2034 # 'scope_flags' is used by upstream 252 scope_flags="--no-scopes" 253 fi 254 255 # Get required node env vars from exiting template. 256 local node_env 257 node_env=$(get-node-env) 258 KUBE_PROXY_TOKEN=$(get-env-val "${node_env}" "KUBE_PROXY_TOKEN") 259 export KUBE_PROXY_TOKEN 260 NODE_PROBLEM_DETECTOR_TOKEN=$(get-env-val "${node_env}" "NODE_PROBLEM_DETECTOR_TOKEN") 261 CA_CERT_BASE64=$(get-env-val "${node_env}" "CA_CERT") 262 export CA_CERT_BASE64 263 EXTRA_DOCKER_OPTS=$(get-env-val "${node_env}" "EXTRA_DOCKER_OPTS") 264 export EXTRA_DOCKER_OPTS 265 KUBELET_CERT_BASE64=$(get-env-val "${node_env}" "KUBELET_CERT") 266 export KUBELET_CERT_BASE64 267 KUBELET_KEY_BASE64=$(get-env-val "${node_env}" "KUBELET_KEY") 268 export KUBELET_KEY_BASE64 269 270 upgrade-node-env 271 272 # TODO(zmerlynn): How do we ensure kube-env is written in a ${version}- 273 # compatible way? 274 write-linux-node-env 275 276 # TODO(zmerlynn): Get configure-vm script from ${version}. (Must plumb this 277 # through all create-linux-node-instance-template implementations). 278 local template_name 279 template_name=$(get-template-name-from-version "${SANITIZED_VERSION}" "${NODE_INSTANCE_PREFIX}") 280 create-linux-node-instance-template "${template_name}" 281 # The following is echo'd so that callers can get the template name. 282 echo "Instance template name: ${template_name}" 283 echo "== Finished preparing node upgrade (to ${KUBE_VERSION}). ==" >&2 284 } 285 286 function upgrade-node-env() { 287 echo "== Upgrading node environment variables. ==" 288 # Get the node problem detector token from master if it isn't present on 289 # the original node. 290 if [[ "${ENABLE_NODE_PROBLEM_DETECTOR:-}" == "standalone" && "${NODE_PROBLEM_DETECTOR_TOKEN:-}" == "" ]]; then 291 detect-master 292 local master_env 293 master_env=$(get-master-env) 294 NODE_PROBLEM_DETECTOR_TOKEN=$(get-env-val "${master_env}" "NODE_PROBLEM_DETECTOR_TOKEN") 295 fi 296 } 297 298 # Upgrades a single node. 299 # $1: The name of the node 300 # 301 # Note: This is called multiple times from do-node-upgrade() in parallel, so should be thread-safe. 302 function do-single-node-upgrade() { 303 local -r instance="$1" 304 local kubectl_rc 305 local boot_id 306 boot_id=$("${KUBE_ROOT}/cluster/kubectl.sh" get node "${instance}" --output=jsonpath='{.status.nodeInfo.bootID}' 2>&1) && kubectl_rc=$? || kubectl_rc=$? 307 if [[ "${kubectl_rc}" != 0 ]]; then 308 echo "== FAILED to get bootID ${instance} ==" 309 echo "${boot_id}" 310 return ${kubectl_rc} 311 fi 312 313 # Drain node 314 echo "== Draining ${instance}. == " >&2 315 local drain_rc 316 "${KUBE_ROOT}/cluster/kubectl.sh" drain --delete-emptydir-data --force --ignore-daemonsets "${instance}" \ 317 && drain_rc=$? || drain_rc=$? 318 if [[ "${drain_rc}" != 0 ]]; then 319 echo "== FAILED to drain ${instance} ==" 320 return ${drain_rc} 321 fi 322 323 # Recreate instance 324 echo "== Recreating instance ${instance}. ==" >&2 325 local recreate_rc 326 local recreate 327 recreate=$(gcloud compute instance-groups managed recreate-instances "${group}" \ 328 --project="${PROJECT}" \ 329 --zone="${ZONE}" \ 330 --instances="${instance}" 2>&1) && recreate_rc=$? || recreate_rc=$? 331 if [[ "${recreate_rc}" != 0 ]]; then 332 echo "== FAILED to recreate ${instance} ==" 333 echo "${recreate}" 334 return ${recreate_rc} 335 fi 336 337 # Wait for node status to reflect a new boot ID. This guarantees us 338 # that the node status in the API is from a different boot. This 339 # does not guarantee that the status is from the upgraded node, but 340 # it is a best effort approximation. 341 echo "== Waiting for new node to be added to k8s. ==" >&2 342 while true; do 343 local new_boot_id 344 new_boot_id=$("${KUBE_ROOT}/cluster/kubectl.sh" get node "${instance}" --output=jsonpath='{.status.nodeInfo.bootID}' 2>&1) && kubectl_rc=$? || kubectl_rc=$? 345 if [[ "${kubectl_rc}" != 0 ]]; then 346 echo "== FAILED to get node ${instance} ==" 347 echo "${boot_id}" 348 echo " (Will retry.)" 349 elif [[ "${boot_id}" != "${new_boot_id}" ]]; then 350 echo "Node ${instance} recreated." 351 break 352 else 353 echo -n . 354 fi 355 sleep 1 356 done 357 358 # Wait for the node to have Ready=True. 359 echo "== Waiting for ${instance} to become ready. ==" >&2 360 while true; do 361 local ready 362 ready=$("${KUBE_ROOT}/cluster/kubectl.sh" get node "${instance}" --output='jsonpath={.status.conditions[?(@.type == "Ready")].status}') 363 if [[ "${ready}" != 'True' ]]; then 364 echo "Node ${instance} is still not ready: Ready=${ready}" 365 else 366 echo "Node ${instance} Ready=${ready}" 367 break 368 fi 369 sleep 1 370 done 371 372 # Uncordon the node. 373 echo "== Uncordon ${instance}. == " >&2 374 local uncordon_rc 375 "${KUBE_ROOT}/cluster/kubectl.sh" uncordon "${instance}" \ 376 && uncordon_rc=$? || uncordon_rc=$? 377 if [[ "${uncordon_rc}" != 0 ]]; then 378 echo "== FAILED to uncordon ${instance} ==" 379 return ${uncordon_rc} 380 fi 381 } 382 383 # Prereqs: 384 # - prepare-node-upgrade should have been called successfully 385 function do-node-upgrade() { 386 echo "== Upgrading nodes to ${KUBE_VERSION} with max parallelism of ${node_upgrade_parallelism}. ==" >&2 387 # Do the actual upgrade. 388 # NOTE(zmerlynn): If you are changing this gcloud command, update 389 # test/e2e/cluster_upgrade.go to match this EXACTLY. 390 local template_name 391 template_name=$(get-template-name-from-version "${SANITIZED_VERSION}" "${NODE_INSTANCE_PREFIX}") 392 local old_templates=() 393 for group in "${INSTANCE_GROUPS[@]}"; do 394 while IFS='' read -r line; do old_templates+=("$line"); done < <(gcloud compute instance-groups managed list \ 395 --project="${PROJECT}" \ 396 --filter="name ~ '${group}' AND zone:(${ZONE})" \ 397 --format='value(instanceTemplate)' || true) 398 set_instance_template_out=$(gcloud compute instance-groups managed set-instance-template "${group}" \ 399 --template="${template_name}" \ 400 --project="${PROJECT}" \ 401 --zone="${ZONE}" 2>&1) && set_instance_template_rc=$? || set_instance_template_rc=$? 402 if [[ "${set_instance_template_rc}" != 0 ]]; then 403 echo "== FAILED to set-instance-template for ${group} to ${template_name} ==" 404 echo "${set_instance_template_out}" 405 return "${set_instance_template_rc}" 406 fi 407 instances=() 408 while IFS='' read -r line; do instances+=("$line"); done < <(gcloud compute instance-groups managed list-instances "${group}" \ 409 --format='value(name)' \ 410 --project="${PROJECT}" \ 411 --zone="${ZONE}" 2>&1) && list_instances_rc=$? || list_instances_rc=$? 412 if [[ "${list_instances_rc}" != 0 ]]; then 413 echo "== FAILED to list instances in group ${group} ==" 414 echo "${instances[@]}" 415 return "${list_instances_rc}" 416 fi 417 418 process_count_left=${node_upgrade_parallelism} 419 pids=() 420 ret_code_sum=0 # Should stay 0 in the loop iff all parallel node upgrades succeed. 421 for instance in "${instances[@]}"; do 422 do-single-node-upgrade "${instance}" & pids+=("$!") 423 424 # We don't want to run more than ${node_upgrade_parallelism} upgrades at a time, 425 # so wait once we hit that many nodes. This isn't ideal, since one might take much 426 # longer than the others, but it should help. 427 process_count_left=$((process_count_left - 1)) 428 if [[ process_count_left -eq 0 || "${instance}" == "${instances[-1]}" ]]; then 429 # Wait for each of the parallel node upgrades to finish. 430 for pid in "${pids[@]}"; do 431 wait "$pid" 432 ret_code_sum=$(( ret_code_sum + $? )) 433 done 434 # Return even if at least one of the node upgrades failed. 435 if [[ ${ret_code_sum} != 0 ]]; then 436 echo "== Some of the ${node_upgrade_parallelism} parallel node upgrades failed. ==" 437 return ${ret_code_sum} 438 fi 439 process_count_left=${node_upgrade_parallelism} 440 fi 441 done 442 done 443 444 # Remove the old templates. 445 echo "== Deleting old templates in ${PROJECT}. ==" >&2 446 for tmpl in "${old_templates[@]}"; do 447 gcloud compute instance-templates delete \ 448 --quiet \ 449 --project="${PROJECT}" \ 450 "${tmpl}" || true 451 done 452 453 echo "== Finished upgrading nodes to ${KUBE_VERSION}. ==" >&2 454 } 455 456 457 function update-coredns-config() { 458 # Get the current CoreDNS version 459 local -r coredns_addon_path="/etc/kubernetes/addons/0-dns/coredns" 460 local -r tmpdir=/tmp 461 local -r download_dir=$(mktemp --tmpdir=${tmpdir} -d coredns-migration.XXXXXXXXXX) || exit 1 462 463 # clean up 464 cleanup() { 465 if [ -n "${download_dir:-}" ]; then 466 rm -rf "${download_dir}" 467 fi 468 } 469 trap cleanup RETURN 470 471 # Get the new installed CoreDNS version 472 echo "== Waiting for CoreDNS to update ==" 473 local -r endtime=$(date -ud "3 minute" +%s) 474 until [[ $("${KUBE_ROOT}"/cluster/kubectl.sh -n kube-system get deployment coredns -o=jsonpath='{$.metadata.resourceVersion}') -ne ${COREDNS_DEPLOY_RESOURCE_VERSION} ]] || [[ $(date -u +%s) -gt $endtime ]]; do 475 sleep 1 476 done 477 478 if [[ $("${KUBE_ROOT}"/cluster/kubectl.sh -n kube-system get deployment coredns -o=jsonpath='{$.metadata.resourceVersion}') -ne ${COREDNS_DEPLOY_RESOURCE_VERSION} ]]; then 479 echo "== CoreDNS ResourceVersion changed ==" 480 fi 481 482 echo "== Fetching the latest installed CoreDNS version ==" 483 NEW_COREDNS_VERSION=$("${KUBE_ROOT}"/cluster/kubectl.sh -n kube-system get deployment coredns -o=jsonpath='{$.spec.template.spec.containers[:1].image}' | sed -r 's/.+:v?(.+)/\1/') 484 485 case "$(uname -m)" in 486 x86_64*) 487 host_arch=amd64 488 corefile_tool_SHA="686792ec91ad52e0761839845c7e09e02234c959b5c459b2cd358d24474c5c66" 489 ;; 490 i?86_64*) 491 host_arch=amd64 492 corefile_tool_SHA="686792ec91ad52e0761839845c7e09e02234c959b5c459b2cd358d24474c5c66" 493 ;; 494 amd64*) 495 host_arch=amd64 496 corefile_tool_SHA="686792ec91ad52e0761839845c7e09e02234c959b5c459b2cd358d24474c5c66" 497 ;; 498 aarch64*) 499 host_arch=arm64 500 corefile_tool_SHA="a968593d68c5564663f9068efa8c34f1baa7bd263be542a71b0b8d8dd44ad124" 501 ;; 502 arm64*) 503 host_arch=arm64 504 corefile_tool_SHA="a968593d68c5564663f9068efa8c34f1baa7bd263be542a71b0b8d8dd44ad124" 505 ;; 506 arm*) 507 host_arch=arm 508 corefile_tool_SHA="721dbfcabda71a2648fd7d4d9241930313397a07d72828b2054315f85b177794" 509 ;; 510 s390x*) 511 host_arch=s390x 512 corefile_tool_SHA="56452a00a703afd4f816d558f78f279af5f29f1940a478baa694da20f4586698" 513 ;; 514 ppc64le*) 515 host_arch=ppc64le 516 corefile_tool_SHA="8a5118cb0c998a79ad1d7e4b001af2e23c2cfa83b5489c2823d04ab1c9e33498" 517 ;; 518 *) 519 echo "Unsupported host arch. Must be x86_64, 386, arm, arm64, s390x or ppc64le." >&2 520 exit 1 521 ;; 522 esac 523 524 # Download the CoreDNS migration tool 525 echo "== Downloading the CoreDNS migration tool ==" 526 wget -P "${download_dir}" "https://github.com/coredns/corefile-migration/releases/download/v1.0.17/corefile-tool-${host_arch}" >/dev/null 2>&1 527 528 local -r checkSHA=$(sha256sum "${download_dir}/corefile-tool-${host_arch}" | cut -d " " -f 1) 529 if [[ "${checkSHA}" != "${corefile_tool_SHA}" ]]; then 530 echo "!!! CheckSum for the CoreDNS migration tool did not match !!!" >&2 531 exit 1 532 fi 533 534 chmod +x "${download_dir}/corefile-tool-${host_arch}" 535 536 # Migrate the CoreDNS ConfigMap depending on whether it is being downgraded or upgraded. 537 "${KUBE_ROOT}/cluster/kubectl.sh" -n kube-system get cm coredns -o jsonpath='{.data.Corefile}' > "${download_dir}/Corefile-old" 538 539 if test "$(printf '%s\n' "${CURRENT_COREDNS_VERSION}" "${NEW_COREDNS_VERSION}" | sort -V | head -n 1)" != "${NEW_COREDNS_VERSION}"; then 540 echo "== Upgrading the CoreDNS ConfigMap ==" 541 "${download_dir}/corefile-tool-${host_arch}" migrate --from "${CURRENT_COREDNS_VERSION}" --to "${NEW_COREDNS_VERSION}" --corefile "${download_dir}/Corefile-old" > "${download_dir}/Corefile" 542 "${KUBE_ROOT}/cluster/kubectl.sh" -n kube-system create configmap coredns --from-file "${download_dir}/Corefile" -o yaml --dry-run=client | "${KUBE_ROOT}/cluster/kubectl.sh" apply -f - 543 else 544 # In case of a downgrade, a custom CoreDNS Corefile will be overwritten by a default Corefile. In that case, 545 # the user will need to manually modify the resulting (default) Corefile after the downgrade is complete. 546 echo "== Applying the latest default CoreDNS configuration ==" 547 gcloud compute --project "${PROJECT}" scp --zone "${ZONE}" "${MASTER_NAME}:${coredns_addon_path}/coredns.yaml" "${download_dir}/coredns-manifest.yaml" > /dev/null 548 "${KUBE_ROOT}/cluster/kubectl.sh" apply -f "${download_dir}/coredns-manifest.yaml" 549 fi 550 551 echo "== The CoreDNS Config has been updated ==" 552 } 553 554 echo "Fetching the previously installed CoreDNS version" 555 CURRENT_COREDNS_VERSION=$("${KUBE_ROOT}/cluster/kubectl.sh" -n kube-system get deployment coredns -o=jsonpath='{$.spec.template.spec.containers[:1].image}' | sed -r 's/.+:v?(.+)/\1/') 556 COREDNS_DEPLOY_RESOURCE_VERSION=$("${KUBE_ROOT}/cluster/kubectl.sh" -n kube-system get deployment coredns -o=jsonpath='{$.metadata.resourceVersion}') 557 558 master_upgrade=true 559 node_upgrade=true 560 node_prereqs=false 561 local_binaries=false 562 env_os_distro=false 563 node_upgrade_parallelism=1 564 565 while getopts ":MNPlcho" opt; do 566 case "${opt}" in 567 M) 568 node_upgrade=false 569 ;; 570 N) 571 master_upgrade=false 572 ;; 573 P) 574 node_prereqs=true 575 ;; 576 l) 577 local_binaries=true 578 ;; 579 c) 580 node_upgrade_parallelism=${NODE_UPGRADE_PARALLELISM:-1} 581 ;; 582 o) 583 env_os_distro=true 584 ;; 585 h) 586 usage 587 exit 0 588 ;; 589 \?) 590 echo "Invalid option: -$OPTARG" >&2 591 usage 592 exit 1 593 ;; 594 esac 595 done 596 shift $((OPTIND-1)) 597 598 if [[ $# -gt 1 ]]; then 599 echo "Error: Only one parameter (<version number or publication>) may be passed after the set of flags!" >&2 600 usage 601 exit 1 602 fi 603 604 if [[ $# -lt 1 ]] && [[ "${local_binaries}" == "false" ]]; then 605 usage 606 exit 1 607 fi 608 609 if [[ "${master_upgrade}" == "false" ]] && [[ "${node_upgrade}" == "false" ]]; then 610 echo "Can't specify both -M and -N" >&2 611 exit 1 612 fi 613 614 # prompt if etcd storage media type isn't set unless using etcd2 when doing master upgrade 615 if [[ -z "${STORAGE_MEDIA_TYPE:-}" ]] && [[ "${STORAGE_BACKEND:-}" != "etcd2" ]] && [[ "${master_upgrade}" == "true" ]]; then 616 echo "The default etcd storage media type in 1.6 has changed from application/json to application/vnd.kubernetes.protobuf." 617 echo "Documentation about the change can be found at https://kubernetes.io/docs/admin/etcd_upgrade." 618 echo "" 619 echo "ETCD2 DOES NOT SUPPORT PROTOBUF: If you wish to have to ability to downgrade to etcd2 later application/json must be used." 620 echo "" 621 echo "It's HIGHLY recommended that etcd be backed up before this step!!" 622 echo "" 623 echo "To enable using json, before running this script set:" 624 echo "export STORAGE_MEDIA_TYPE=application/json" 625 echo "" 626 if [ -t 0 ] && [ -t 1 ]; then 627 read -r -p "Would you like to continue with the new default, and lose the ability to downgrade to etcd2? [y/N] " confirm 628 if [[ "${confirm}" != "y" ]]; then 629 exit 1 630 fi 631 else 632 echo "To enable using protobuf, before running this script set:" 633 echo "export STORAGE_MEDIA_TYPE=application/vnd.kubernetes.protobuf" 634 echo "" 635 echo "STORAGE_MEDIA_TYPE must be specified when run non-interactively." >&2 636 exit 1 637 fi 638 fi 639 640 # Prompt if etcd image/version is unspecified when doing master upgrade. 641 # In e2e tests, we use TEST_ALLOW_IMPLICIT_ETCD_UPGRADE=true to skip this 642 # prompt, simulating the behavior when the user confirms interactively. 643 # All other automated use of this script should explicitly specify a version. 644 if [[ "${master_upgrade}" == "true" ]]; then 645 if [[ -z "${ETCD_IMAGE:-}" && -z "${TEST_ETCD_IMAGE:-}" ]] || [[ -z "${ETCD_VERSION:-}" && -z "${TEST_ETCD_VERSION:-}" ]]; then 646 echo 647 echo "***WARNING***" 648 echo "Upgrading Kubernetes with this script might result in an upgrade to a new etcd version." 649 echo "Some etcd version upgrades, such as 3.0.x to 3.1.x, DO NOT offer a downgrade path." 650 echo "To pin the etcd version to your current one (e.g. v3.0.17), set the following variables" 651 echo "before running this script:" 652 echo 653 echo "# example: pin to etcd v3.0.17" 654 echo "export ETCD_IMAGE=3.0.17" 655 echo "export ETCD_VERSION=3.0.17" 656 echo 657 echo "Alternatively, if you choose to allow an etcd upgrade that doesn't support downgrade," 658 echo "you might still be able to downgrade Kubernetes by pinning to the newer etcd version." 659 echo "In all cases, it is strongly recommended to have an etcd backup before upgrading." 660 echo 661 if [ -t 0 ] && [ -t 1 ]; then 662 read -r -p "Continue with default etcd version, which might upgrade etcd? [y/N] " confirm 663 if [[ "${confirm}" != "y" ]]; then 664 exit 1 665 fi 666 elif [[ "${TEST_ALLOW_IMPLICIT_ETCD_UPGRADE:-}" != "true" ]]; then 667 echo "ETCD_IMAGE and ETCD_VERSION must be specified when run non-interactively." >&2 668 exit 1 669 fi 670 fi 671 fi 672 673 print-node-version-info "Pre-Upgrade" 674 675 if [[ "${local_binaries}" == "false" ]]; then 676 set_binary_version "${1}" 677 fi 678 679 prepare-upgrade 680 681 if [[ "${node_prereqs}" == "true" ]]; then 682 prepare-node-upgrade 683 exit 0 684 fi 685 686 if [[ "${master_upgrade}" == "true" ]]; then 687 upgrade-master 688 fi 689 690 if [[ "${node_upgrade}" == "true" ]]; then 691 if [[ "${local_binaries}" == "true" ]]; then 692 echo "Upgrading nodes to local binaries is not yet supported." >&2 693 exit 1 694 else 695 upgrade-nodes 696 fi 697 fi 698 699 if [[ "${CLUSTER_DNS_CORE_DNS:-}" == "true" ]]; then 700 update-coredns-config 701 fi 702 703 echo "== Validating cluster post-upgrade ==" 704 "${KUBE_ROOT}/cluster/validate-cluster.sh" 705 706 print-node-version-info "Post-Upgrade"