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"