...

Text file src/k8s.io/kubernetes/cluster/gce/upgrade.sh

Documentation: k8s.io/kubernetes/cluster/gce

     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
    21set -o errexit
    22set -o nounset
    23set -o pipefail
    24
    25if [[ "${KUBERNETES_PROVIDER:-gce}" != "gce" ]]; then
    26  echo "!!! ${1} only works on GCE" >&2
    27  exit 1
    28fi
    29
    30KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
    31source "${KUBE_ROOT}/hack/lib/util.sh"
    32source "${KUBE_ROOT}/cluster/kube-util.sh"
    33
    34function 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
    74function 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
    79function 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
   115function 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
   124function 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
   150function 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
   166function 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
   179function 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
   201function upgrade-nodes() {
   202  prepare-node-upgrade
   203  do-node-upgrade
   204}
   205
   206function 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
   240function 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
   286function 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.
   302function 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
   385function 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
   457function 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
   554echo "Fetching the previously installed CoreDNS version"
   555CURRENT_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/')
   556COREDNS_DEPLOY_RESOURCE_VERSION=$("${KUBE_ROOT}/cluster/kubectl.sh" -n kube-system get deployment coredns -o=jsonpath='{$.metadata.resourceVersion}')
   557
   558master_upgrade=true
   559node_upgrade=true
   560node_prereqs=false
   561local_binaries=false
   562env_os_distro=false
   563node_upgrade_parallelism=1
   564
   565while 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
   595done
   596shift $((OPTIND-1))
   597
   598if [[ $# -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
   602fi
   603
   604if [[ $# -lt 1 ]] && [[ "${local_binaries}" == "false" ]]; then
   605  usage
   606  exit 1
   607fi
   608
   609if [[ "${master_upgrade}" == "false" ]] && [[ "${node_upgrade}" == "false" ]]; then
   610  echo "Can't specify both -M and -N" >&2
   611  exit 1
   612fi
   613
   614# prompt if etcd storage media type isn't set unless using etcd2 when doing master upgrade
   615if [[ -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
   638fi
   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.
   644if [[ "${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
   671fi
   672
   673print-node-version-info "Pre-Upgrade"
   674
   675if [[ "${local_binaries}" == "false" ]]; then
   676  set_binary_version "${1}"
   677fi
   678
   679prepare-upgrade
   680
   681if [[ "${node_prereqs}" == "true" ]]; then
   682  prepare-node-upgrade
   683  exit 0
   684fi
   685
   686if [[ "${master_upgrade}" == "true" ]]; then
   687  upgrade-master
   688fi
   689
   690if [[ "${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
   697fi
   698
   699if [[ "${CLUSTER_DNS_CORE_DNS:-}" == "true" ]]; then
   700  update-coredns-config
   701fi
   702
   703echo "== Validating cluster post-upgrade =="
   704"${KUBE_ROOT}/cluster/validate-cluster.sh"
   705
   706print-node-version-info "Post-Upgrade"

View as plain text