k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cluster/gce/util.sh (about)

     1  #!/usr/bin/env bash
     2  
     3  # Copyright 2017 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  # A library of helper functions and constant for the local config.
    18  
    19  # Use the config file specified in $KUBE_CONFIG_FILE, or default to
    20  # config-default.sh.
    21  readonly GCE_MAX_LOCAL_SSD=8
    22  
    23  KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
    24  source "${KUBE_ROOT}/cluster/gce/${KUBE_CONFIG_FILE-"config-default.sh"}"
    25  source "${KUBE_ROOT}/cluster/common.sh"
    26  source "${KUBE_ROOT}/hack/lib/util.sh"
    27  
    28  if [[ "${NODE_OS_DISTRIBUTION}" == "gci" || "${NODE_OS_DISTRIBUTION}" == "ubuntu" || "${NODE_OS_DISTRIBUTION}" == "custom" ]]; then
    29    source "${KUBE_ROOT}/cluster/gce/${NODE_OS_DISTRIBUTION}/node-helper.sh"
    30  else
    31    echo "Cannot operate on cluster using node os distro: ${NODE_OS_DISTRIBUTION}" >&2
    32    exit 1
    33  fi
    34  
    35  source "${KUBE_ROOT}/cluster/gce/windows/node-helper.sh"
    36  
    37  if [[ "${MASTER_OS_DISTRIBUTION}" == "trusty" || "${MASTER_OS_DISTRIBUTION}" == "gci" || "${MASTER_OS_DISTRIBUTION}" == "ubuntu" ]]; then
    38    source "${KUBE_ROOT}/cluster/gce/${MASTER_OS_DISTRIBUTION}/master-helper.sh"
    39  else
    40    echo "Cannot operate on cluster using master os distro: ${MASTER_OS_DISTRIBUTION}" >&2
    41    exit 1
    42  fi
    43  
    44  if [[ ${NODE_LOCAL_SSDS:-} -ge 1 ]] && [[ -n ${NODE_LOCAL_SSDS_EXT:-} ]] ; then
    45    echo -e "${color_red:-}Local SSD: Only one of NODE_LOCAL_SSDS and NODE_LOCAL_SSDS_EXT can be specified at once${color_norm:-}" >&2
    46    exit 2
    47  fi
    48  
    49  if [[ "${MASTER_OS_DISTRIBUTION}" == "gci" ]]; then
    50      DEFAULT_GCI_PROJECT=google-containers
    51      if [[ "${GCI_VERSION}" == "cos"* ]] || [[ "${MASTER_IMAGE_FAMILY}" == "cos"* ]]; then
    52          DEFAULT_GCI_PROJECT=cos-cloud
    53      fi
    54      export MASTER_IMAGE_PROJECT=${KUBE_GCE_MASTER_PROJECT:-${DEFAULT_GCI_PROJECT}}
    55  
    56      # If the master image is not set, we use the latest image based on image
    57      # family.
    58      kube_master_image="${KUBE_GCE_MASTER_IMAGE:-${GCI_VERSION}}"
    59      if [[ -z "${kube_master_image}" ]]; then
    60        kube_master_image=$(gcloud compute images list --project="${MASTER_IMAGE_PROJECT}" --no-standard-images --filter="family:${MASTER_IMAGE_FAMILY}" --format 'value(name)')
    61      fi
    62  
    63      echo "Using image: ${kube_master_image} from project: ${MASTER_IMAGE_PROJECT} as master image" >&2
    64      export MASTER_IMAGE="${kube_master_image}"
    65  fi
    66  
    67  # Sets node image based on the specified os distro. Currently this function only
    68  # supports gci and debian.
    69  #
    70  # Requires:
    71  #   NODE_OS_DISTRIBUTION
    72  # Sets:
    73  #   DEFAULT_GCI_PROJECT
    74  #   NODE_IMAGE
    75  #   NODE_IMAGE_PROJECT
    76  function set-linux-node-image() {
    77    if [[ "${NODE_OS_DISTRIBUTION}" == "gci" ]]; then
    78      DEFAULT_GCI_PROJECT=google-containers
    79      if [[ "${GCI_VERSION}" == "cos"* ]] || [[ "${NODE_IMAGE_FAMILY}" == "cos"* ]]; then
    80        DEFAULT_GCI_PROJECT=cos-cloud
    81      fi
    82  
    83      # If the node image is not set, we use the latest image based on image
    84      # family.
    85      # Otherwise, we respect whatever is set by the user.
    86      NODE_IMAGE_PROJECT=${KUBE_GCE_NODE_PROJECT:-${DEFAULT_GCI_PROJECT}}
    87      local kube_node_image
    88  
    89      kube_node_image="${KUBE_GCE_NODE_IMAGE:-${GCI_VERSION}}"
    90      if [[ -z "${kube_node_image}" ]]; then
    91        kube_node_image=$(gcloud compute images list --project="${NODE_IMAGE_PROJECT}" --no-standard-images --filter="family:${NODE_IMAGE_FAMILY}" --format 'value(name)')
    92      fi
    93  
    94      echo "Using image: ${kube_node_image} from project: ${NODE_IMAGE_PROJECT} as node image" >&2
    95      export NODE_IMAGE="${kube_node_image}"
    96    fi
    97  }
    98  
    99  # Requires:
   100  #   WINDOWS_NODE_OS_DISTRIBUTION
   101  # Sets:
   102  #   WINDOWS_NODE_IMAGE_PROJECT
   103  #   WINDOWS_NODE_IMAGE
   104  function set-windows-node-image() {
   105    WINDOWS_NODE_IMAGE_PROJECT="windows-cloud"
   106    if [[ "${WINDOWS_NODE_OS_DISTRIBUTION}" == "win2019" ]]; then
   107      WINDOWS_NODE_IMAGE="windows-server-2019-dc-core-v20210914"
   108    elif [[ "${WINDOWS_NODE_OS_DISTRIBUTION}" == "win1909" ]]; then
   109      WINDOWS_NODE_IMAGE="windows-server-1909-dc-core-v20210413"
   110    elif [[ "${WINDOWS_NODE_OS_DISTRIBUTION}" == "win2004" ]]; then
   111      WINDOWS_NODE_IMAGE="windows-server-2004-dc-core-v20210914"
   112    elif [[ "${WINDOWS_NODE_OS_DISTRIBUTION,,}" == "win20h2" ]]; then
   113      WINDOWS_NODE_IMAGE="windows-server-20h2-dc-core-v20210914"
   114    elif [[ "${WINDOWS_NODE_OS_DISTRIBUTION,,}" == "win2022" ]]; then
   115      WINDOWS_NODE_IMAGE="windows-server-2022-dc-core-v20220513"
   116    else
   117      echo "Unknown WINDOWS_NODE_OS_DISTRIBUTION ${WINDOWS_NODE_OS_DISTRIBUTION}" >&2
   118      exit 1
   119    fi
   120  }
   121  
   122  set-linux-node-image
   123  set-windows-node-image
   124  
   125  # Verify cluster autoscaler configuration.
   126  if [[ "${ENABLE_CLUSTER_AUTOSCALER}" == "true" ]]; then
   127    if [[ -z $AUTOSCALER_MIN_NODES ]]; then
   128      echo "AUTOSCALER_MIN_NODES not set."
   129      exit 1
   130    fi
   131    if [[ -z $AUTOSCALER_MAX_NODES ]]; then
   132      echo "AUTOSCALER_MAX_NODES not set."
   133      exit 1
   134    fi
   135  fi
   136  
   137  # These prefixes must not be prefixes of each other, so that they can be used to
   138  # detect mutually exclusive sets of nodes.
   139  NODE_INSTANCE_PREFIX=${NODE_INSTANCE_PREFIX:-"${INSTANCE_PREFIX}-minion"}
   140  WINDOWS_NODE_INSTANCE_PREFIX=${WINDOWS_NODE_INSTANCE_PREFIX:-"${INSTANCE_PREFIX}-windows-node"}
   141  
   142  # NODE_TAG (expected to be) defined by caller
   143  # shellcheck disable=SC2153
   144  NODE_TAGS="${NODE_TAG}"
   145  
   146  ALLOCATE_NODE_CIDRS=true
   147  PREEXISTING_NETWORK=false
   148  PREEXISTING_NETWORK_MODE=""
   149  
   150  KUBE_PROMPT_FOR_UPDATE=${KUBE_PROMPT_FOR_UPDATE:-"n"}
   151  # How long (in seconds) to wait for cluster initialization.
   152  KUBE_CLUSTER_INITIALIZATION_TIMEOUT=${KUBE_CLUSTER_INITIALIZATION_TIMEOUT:-300}
   153  
   154  function join_csv() {
   155    local IFS=','; echo "$*";
   156  }
   157  
   158  # This function returns the first string before the comma
   159  function split_csv() {
   160    echo "$*" | cut -d',' -f1
   161  }
   162  
   163  # Verify prereqs
   164  function verify-prereqs() {
   165    local cmd
   166  
   167    # we use openssl to generate certs
   168    kube::util::test_openssl_installed
   169  
   170    # ensure a version supported by easyrsa is installed
   171    if [ "$(openssl version | cut -d\  -f1)" == "LibreSSL" ]; then
   172      echo "LibreSSL is not supported. Please ensure openssl points to an OpenSSL binary"
   173      if [ "$(uname -s)" == "Darwin" ]; then
   174        # We want this print just the way it is
   175        # shellcheck disable=SC2016
   176        echo 'On macOS we recommend using homebrew and adding "$(brew --prefix openssl)/bin" to your PATH'
   177      fi
   178      exit 1
   179    fi
   180  
   181    # we use gcloud to create the cluster, gsutil to stage binaries and data
   182    for cmd in gcloud gsutil; do
   183      if ! which "${cmd}" >/dev/null; then
   184        echo "Can't find ${cmd} in PATH, please fix and retry. The Google Cloud " >&2
   185        echo "SDK can be downloaded from https://cloud.google.com/sdk/." >&2
   186        exit 1
   187      fi
   188    done
   189    update-or-verify-gcloud
   190  }
   191  
   192  # Use the gcloud defaults to find the project.  If it is already set in the
   193  # environment then go with that.
   194  #
   195  # Vars set:
   196  #   PROJECT
   197  #   NETWORK_PROJECT
   198  #   PROJECT_REPORTED
   199  function detect-project() {
   200    if [[ -z "${PROJECT-}" ]]; then
   201      PROJECT=$(gcloud config list project --format 'value(core.project)')
   202    fi
   203  
   204    NETWORK_PROJECT=${NETWORK_PROJECT:-${PROJECT}}
   205  
   206    if [[ -z "${PROJECT-}" ]]; then
   207      echo "Could not detect Google Cloud Platform project.  Set the default project using " >&2
   208      echo "'gcloud config set project <PROJECT>'" >&2
   209      exit 1
   210    fi
   211    if [[ -z "${PROJECT_REPORTED-}" ]]; then
   212      echo "Project: ${PROJECT}" >&2
   213      echo "Network Project: ${NETWORK_PROJECT}" >&2
   214      echo "Zone: ${ZONE}" >&2
   215      PROJECT_REPORTED=true
   216    fi
   217  }
   218  
   219  # Use gsutil to get the md5 hash for a particular tar
   220  function gsutil_get_tar_md5() {
   221    # location_tar could be local or in the cloud
   222    # local tar_location example ./_output/release-tars/kubernetes-server-linux-amd64.tar.gz
   223    # cloud tar_location example gs://kubernetes-staging-PROJECT/kubernetes-devel/kubernetes-server-linux-amd64.tar.gz
   224    local -r tar_location=$1
   225    #parse the output and return the md5 hash
   226    #the sed command at the end removes whitespace
   227    local -r tar_md5=$(gsutil hash -h -m "${tar_location}" 2>/dev/null | grep "Hash (md5):" | awk -F ':' '{print $2}' | sed 's/^[[:space:]]*//g')
   228    echo "${tar_md5}"
   229  }
   230  
   231  # Copy a release tar and its accompanying hash.
   232  function copy-to-staging() {
   233    local -r staging_path=$1
   234    local -r gs_url=$2
   235    local -r tar=$3
   236    local -r hash=$4
   237    local -r basename_tar=$(basename "${tar}")
   238  
   239    #check whether this tar alread exists and has the same hash
   240    #if it matches, then don't bother uploading it again
   241  
   242    #remote_tar_md5 checks the remote location for the existing tarball and its md5
   243    #staging_path example gs://kubernetes-staging-PROJECT/kubernetes-devel
   244    #basename_tar example kubernetes-server-linux-amd64.tar.gz
   245    local -r remote_tar_md5=$(gsutil_get_tar_md5 "${staging_path}/${basename_tar}")
   246    if [[ -n ${remote_tar_md5} ]]; then
   247      #local_tar_md5 checks the remote location for the existing tarball and its md5 hash
   248      #tar example ./_output/release-tars/kubernetes-server-linux-amd64.tar.gz
   249      local -r local_tar_md5=$(gsutil_get_tar_md5 "${tar}")
   250      if [[ "${remote_tar_md5}" == "${local_tar_md5}" ]]; then
   251        echo "+++ ${basename_tar} uploaded earlier, cloud and local file md5 match (md5 = ${local_tar_md5})"
   252        return 0
   253      fi
   254    fi
   255  
   256    echo "${hash}" > "${tar}.sha512"
   257    gsutil -m -q -h "Cache-Control:private, max-age=0" cp "${tar}" "${tar}.sha512" "${staging_path}"
   258    gsutil -m acl ch -g all:R "${gs_url}" "${gs_url}.sha512" >/dev/null 2>&1 || true
   259    echo "+++ ${basename_tar} uploaded (sha512 = ${hash})"
   260  }
   261  
   262  
   263  # Given the cluster zone, return the list of regional GCS release
   264  # bucket suffixes for the release in preference order. GCS doesn't
   265  # give us an API for this, so we hardcode it.
   266  #
   267  # Assumed vars:
   268  #   RELEASE_REGION_FALLBACK
   269  #   REGIONAL_KUBE_ADDONS
   270  #   ZONE
   271  # Vars set:
   272  #   PREFERRED_REGION
   273  function set-preferred-region() {
   274    case ${ZONE} in
   275      asia-*)
   276        PREFERRED_REGION=("asia-northeast1" "us-central1" "europe-west6")
   277        ;;
   278      europe-*)
   279        PREFERRED_REGION=("europe-west6" "us-central1" "asia-northeast1")
   280        ;;
   281      *)
   282        PREFERRED_REGION=("us-central1" "europe-west6" "asia-northeast1")
   283        ;;
   284    esac
   285  
   286    if [[ "${RELEASE_REGION_FALLBACK}" != "true" ]]; then
   287      PREFERRED_REGION=( "${PREFERRED_REGION[0]}" )
   288    fi
   289  }
   290  
   291  # Take the local tar files and upload them to Google Storage.  They will then be
   292  # downloaded by the master as part of the start up script for the master.
   293  #
   294  # Assumed vars:
   295  #   PROJECT
   296  #   SERVER_BINARY_TAR
   297  #   KUBE_MANIFESTS_TAR
   298  #   ZONE
   299  # Vars set:
   300  #   SERVER_BINARY_TAR_URL
   301  #   SERVER_BINARY_TAR_HASH
   302  #   NODE_BINARY_TAR_URL
   303  #   NODE_BINARY_TAR_HASH
   304  #   KUBE_MANIFESTS_TAR_URL
   305  #   KUBE_MANIFESTS_TAR_HASH
   306  function upload-tars() {
   307    SERVER_BINARY_TAR_URL=
   308    SERVER_BINARY_TAR_HASH=
   309    NODE_BINARY_TAR_URL=
   310    NODE_BINARY_TAR_HASH=
   311    KUBE_MANIFESTS_TAR_URL=
   312    KUBE_MANIFESTS_TAR_HASH=
   313  
   314    local project_hash
   315    if which md5 > /dev/null 2>&1; then
   316      project_hash=$(md5 -q -s "$PROJECT")
   317    else
   318      project_hash=$(echo -n "$PROJECT" | md5sum)
   319      project_hash=${project_hash%%[[:blank:]]*}
   320    fi
   321  
   322    # This requires 1 million projects before the probability of collision is 50%
   323    # that's probably good enough for now :P
   324    project_hash=${project_hash:0:10}
   325  
   326    set-preferred-region
   327  
   328    if [[ "${ENABLE_DOCKER_REGISTRY_CACHE:-}" == "true" ]]; then
   329      DOCKER_REGISTRY_MIRROR_URL="https://mirror.gcr.io"
   330    fi
   331  
   332    SERVER_BINARY_TAR_HASH=$(sha512sum-file "${SERVER_BINARY_TAR}")
   333  
   334    if [[ -n "${NODE_BINARY_TAR:-}" ]]; then
   335      NODE_BINARY_TAR_HASH=$(sha512sum-file "${NODE_BINARY_TAR}")
   336    fi
   337    if [[ -n "${KUBE_MANIFESTS_TAR:-}" ]]; then
   338      KUBE_MANIFESTS_TAR_HASH=$(sha512sum-file "${KUBE_MANIFESTS_TAR}")
   339    fi
   340  
   341    local server_binary_tar_urls=()
   342    local node_binary_tar_urls=()
   343  
   344    for region in "${PREFERRED_REGION[@]}"; do
   345      suffix="-${region}"
   346      local staging_bucket="gs://kubernetes-staging-${project_hash}${suffix}"
   347  
   348      # Ensure the buckets are created
   349      if ! gsutil ls "${staging_bucket}" >/dev/null; then
   350        echo "Creating ${staging_bucket}"
   351        gsutil mb -l "${region}" -p "${PROJECT}" "${staging_bucket}"
   352      fi
   353  
   354      local staging_path="${staging_bucket}/${INSTANCE_PREFIX}-devel"
   355  
   356      echo "+++ Staging tars to Google Storage: ${staging_path}"
   357      local server_binary_gs_url="${staging_path}/${SERVER_BINARY_TAR##*/}"
   358      copy-to-staging "${staging_path}" "${server_binary_gs_url}" "${SERVER_BINARY_TAR}" "${SERVER_BINARY_TAR_HASH}"
   359  
   360      if [[ -n "${NODE_BINARY_TAR:-}" ]]; then
   361        local node_binary_gs_url="${staging_path}/${NODE_BINARY_TAR##*/}"
   362        copy-to-staging "${staging_path}" "${node_binary_gs_url}" "${NODE_BINARY_TAR}" "${NODE_BINARY_TAR_HASH}"
   363      fi
   364  
   365      # Convert from gs:// URL to an https:// URL
   366      server_binary_tar_urls+=("${server_binary_gs_url/gs:\/\//https://storage.googleapis.com/}")
   367      if [[ -n "${NODE_BINARY_TAR:-}" ]]; then
   368        node_binary_tar_urls+=("${node_binary_gs_url/gs:\/\//https://storage.googleapis.com/}")
   369      fi
   370      if [[ -n "${KUBE_MANIFESTS_TAR:-}" ]]; then
   371        local kube_manifests_gs_url="${staging_path}/${KUBE_MANIFESTS_TAR##*/}"
   372        copy-to-staging "${staging_path}" "${kube_manifests_gs_url}" "${KUBE_MANIFESTS_TAR}" "${KUBE_MANIFESTS_TAR_HASH}"
   373        # Convert from gs:// URL to an https:// URL
   374        kube_manifests_tar_urls+=("${kube_manifests_gs_url/gs:\/\//https://storage.googleapis.com/}")
   375      fi
   376    done
   377  
   378    SERVER_BINARY_TAR_URL=$(join_csv "${server_binary_tar_urls[@]}")
   379    if [[ -n "${NODE_BINARY_TAR:-}" ]]; then
   380      NODE_BINARY_TAR_URL=$(join_csv "${node_binary_tar_urls[@]}")
   381    fi
   382    if [[ -n "${KUBE_MANIFESTS_TAR:-}" ]]; then
   383      KUBE_MANIFESTS_TAR_URL=$(join_csv "${kube_manifests_tar_urls[@]}")
   384    fi
   385  }
   386  
   387  # Detect Linux and Windows nodes created in the instance group.
   388  #
   389  # Assumed vars:
   390  #   NODE_INSTANCE_PREFIX
   391  #   WINDOWS_NODE_INSTANCE_PREFIX
   392  # Vars set:
   393  #   NODE_NAMES
   394  #   INSTANCE_GROUPS
   395  #   WINDOWS_NODE_NAMES
   396  #   WINDOWS_INSTANCE_GROUPS
   397  function detect-node-names() {
   398    detect-project
   399    INSTANCE_GROUPS=()
   400    kube::util::read-array INSTANCE_GROUPS < <(gcloud compute instance-groups managed list \
   401      --project "${PROJECT}" \
   402      --filter "name ~ '${NODE_INSTANCE_PREFIX}-.+' AND zone:(${ZONE})" \
   403      --format='value(name)' || true)
   404    WINDOWS_INSTANCE_GROUPS=()
   405    kube::util::read-array WINDOWS_INSTANCE_GROUPS < <(gcloud compute instance-groups managed list \
   406      --project "${PROJECT}" \
   407      --filter "name ~ '${WINDOWS_NODE_INSTANCE_PREFIX}-.+' AND zone:(${ZONE})" \
   408      --format='value(name)' || true)
   409  
   410    NODE_NAMES=()
   411    if [[ -n "${INSTANCE_GROUPS[*]:-}" ]]; then
   412      for group in "${INSTANCE_GROUPS[@]}"; do
   413        kube::util::read-array NODE_NAMES < <(gcloud compute instance-groups managed list-instances \
   414          "${group}" --zone "${ZONE}" --project "${PROJECT}" \
   415          --format='value(name)')
   416      done
   417    fi
   418    # Add heapster node name to the list too (if it exists).
   419    if [[ -n "${HEAPSTER_MACHINE_TYPE:-}" ]]; then
   420      NODE_NAMES+=("${NODE_INSTANCE_PREFIX}-heapster")
   421    fi
   422    export NODE_NAMES
   423    WINDOWS_NODE_NAMES=()
   424    if [[ -n "${WINDOWS_INSTANCE_GROUPS[*]:-}" ]]; then
   425      for group in "${WINDOWS_INSTANCE_GROUPS[@]}"; do
   426        kube::util::read-array WINDOWS_NODE_NAMES < <(gcloud compute instance-groups managed \
   427          list-instances "${group}" --zone "${ZONE}" --project "${PROJECT}" \
   428          --format='value(name)')
   429      done
   430    fi
   431    export WINDOWS_NODE_NAMES
   432  
   433    echo "INSTANCE_GROUPS=${INSTANCE_GROUPS[*]:-}" >&2
   434    echo "NODE_NAMES=${NODE_NAMES[*]:-}" >&2
   435  }
   436  
   437  # Detect the information about the minions
   438  #
   439  # Assumed vars:
   440  #   ZONE
   441  # Vars set:
   442  #   NODE_NAMES
   443  #   KUBE_NODE_IP_ADDRESSES (array)
   444  function detect-nodes() {
   445    detect-project
   446    detect-node-names
   447    KUBE_NODE_IP_ADDRESSES=()
   448    for (( i=0; i<${#NODE_NAMES[@]}; i++)); do
   449      local node_ip
   450      node_ip=$(gcloud compute instances describe --project "${PROJECT}" --zone "${ZONE}" \
   451        "${NODE_NAMES[$i]}" --format='value(networkInterfaces[0].accessConfigs[0].natIP)')
   452      if [[ -z "${node_ip-}" ]] ; then
   453        echo "Did not find ${NODE_NAMES[$i]}" >&2
   454      else
   455        echo "Found ${NODE_NAMES[$i]} at ${node_ip}"
   456        KUBE_NODE_IP_ADDRESSES+=("${node_ip}")
   457      fi
   458    done
   459    if [[ -z "${KUBE_NODE_IP_ADDRESSES-}" ]]; then
   460      echo "Could not detect Kubernetes minion nodes.  Make sure you've launched a cluster with 'kube-up.sh'" >&2
   461      exit 1
   462    fi
   463  }
   464  
   465  # Detect the IP for the master
   466  #
   467  # Assumed vars:
   468  #   MASTER_NAME
   469  #   ZONE
   470  #   REGION
   471  # Vars set:
   472  #   KUBE_MASTER
   473  #   KUBE_MASTER_IP
   474  function detect-master() {
   475    detect-project
   476    KUBE_MASTER=${MASTER_NAME}
   477    echo "Trying to find master named '${MASTER_NAME}'" >&2
   478    if [[ -z "${KUBE_MASTER_IP-}" ]]; then
   479      local master_address_name="${MASTER_NAME}-ip"
   480      echo "Looking for address '${master_address_name}'" >&2
   481      if ! KUBE_MASTER_IP=$(gcloud compute addresses describe "${master_address_name}" \
   482        --project "${PROJECT}" --region "${REGION}" -q --format='value(address)') || \
   483        [[ -z "${KUBE_MASTER_IP-}" ]]; then
   484        echo "Could not detect Kubernetes master node.  Make sure you've launched a cluster with 'kube-up.sh'" >&2
   485        exit 1
   486      fi
   487    fi
   488    if [[ -z "${KUBE_MASTER_INTERNAL_IP-}" ]] && [[ ${GCE_PRIVATE_CLUSTER:-} == "true" ]]; then
   489        local master_address_name="${MASTER_NAME}-internal-ip"
   490        echo "Looking for address '${master_address_name}'" >&2
   491        if ! KUBE_MASTER_INTERNAL_IP=$(gcloud compute addresses describe "${master_address_name}" \
   492          --project "${PROJECT}" --region "${REGION}" -q --format='value(address)') || \
   493          [[ -z "${KUBE_MASTER_INTERNAL_IP-}" ]]; then
   494          echo "Could not detect Kubernetes master node.  Make sure you've launched a cluster with 'kube-up.sh'" >&2
   495          exit 1
   496        fi
   497    fi
   498    echo "Using master: $KUBE_MASTER (external IP: $KUBE_MASTER_IP; internal IP: ${KUBE_MASTER_INTERNAL_IP:-(not set)})" >&2
   499  }
   500  
   501  function load-or-gen-kube-bearertoken() {
   502    if [[ -n "${KUBE_CONTEXT:-}" ]]; then
   503      get-kubeconfig-bearertoken
   504    fi
   505    if [[ -z "${KUBE_BEARER_TOKEN:-}" ]]; then
   506      gen-kube-bearertoken
   507    fi
   508  }
   509  
   510  # Figure out which binary use on the server and assure it is available.
   511  # If KUBE_VERSION is specified use binaries specified by it, otherwise
   512  # use local dev binaries.
   513  #
   514  # Assumed vars:
   515  #   KUBE_VERSION
   516  #   KUBE_RELEASE_VERSION_REGEX
   517  #   KUBE_CI_VERSION_REGEX
   518  # Vars set:
   519  #   KUBE_TAR_HASH
   520  #   SERVER_BINARY_TAR_URL
   521  #   SERVER_BINARY_TAR_HASH
   522  function tars_from_version() {
   523    local sha512sum=""
   524    if which sha512sum >/dev/null 2>&1; then
   525      sha512sum="sha512sum"
   526    else
   527      sha512sum="shasum -a512"
   528    fi
   529  
   530    if [[ -z "${KUBE_VERSION-}" ]]; then
   531      find-release-tars
   532      upload-tars
   533    elif [[ ${KUBE_VERSION} =~ ${KUBE_RELEASE_VERSION_REGEX} ]]; then
   534      SERVER_BINARY_TAR_URL="https://dl.k8s.io/release/${KUBE_VERSION}/kubernetes-server-linux-amd64.tar.gz"
   535      # TODO: Clean this up.
   536      KUBE_MANIFESTS_TAR_URL="${SERVER_BINARY_TAR_URL/server-linux-amd64/manifests}"
   537      KUBE_MANIFESTS_TAR_HASH=$(curl -L "${KUBE_MANIFESTS_TAR_URL}" --silent --show-error | ${sha512sum})
   538      KUBE_MANIFESTS_TAR_HASH=${KUBE_MANIFESTS_TAR_HASH%%[[:blank:]]*}
   539    elif [[ ${KUBE_VERSION} =~ ${KUBE_CI_VERSION_REGEX} ]]; then
   540      SERVER_BINARY_TAR_URL="https://storage.googleapis.com/k8s-release-dev/ci/${KUBE_VERSION}/kubernetes-server-linux-amd64.tar.gz"
   541      # TODO: Clean this up.
   542      KUBE_MANIFESTS_TAR_URL="${SERVER_BINARY_TAR_URL/server-linux-amd64/manifests}"
   543      KUBE_MANIFESTS_TAR_HASH=$(curl "${KUBE_MANIFESTS_TAR_URL}" --silent --show-error | ${sha512sum})
   544      KUBE_MANIFESTS_TAR_HASH=${KUBE_MANIFESTS_TAR_HASH%%[[:blank:]]*}
   545    else
   546      echo "Version doesn't match regexp" >&2
   547      exit 1
   548    fi
   549    if ! SERVER_BINARY_TAR_HASH=$(curl -Ss --fail "${SERVER_BINARY_TAR_URL}.sha512"); then
   550      echo "Failure trying to curl release .sha512"
   551    fi
   552  
   553    if ! curl -Ss --head "${SERVER_BINARY_TAR_URL}" >&/dev/null; then
   554      echo "Can't find release at ${SERVER_BINARY_TAR_URL}" >&2
   555      exit 1
   556    fi
   557  }
   558  
   559  # Reads kube-env metadata from master
   560  #
   561  # Assumed vars:
   562  #   KUBE_MASTER
   563  #   PROJECT
   564  #   ZONE
   565  function get-master-env() {
   566    # TODO(zmerlynn): Make this more reliable with retries.
   567    gcloud compute --project "${PROJECT}" ssh --zone "${ZONE}" "${KUBE_MASTER}" --command \
   568      "curl --fail --silent -H 'Metadata-Flavor: Google' \
   569        'http://metadata/computeMetadata/v1/instance/attributes/kube-env'" 2>/dev/null
   570    gcloud compute --project "${PROJECT}" ssh --zone "${ZONE}" "${KUBE_MASTER}" --command \
   571      "curl --fail --silent -H 'Metadata-Flavor: Google' \
   572        'http://metadata/computeMetadata/v1/instance/attributes/kube-master-certs'" 2>/dev/null
   573  }
   574  
   575  # Quote something appropriate for a yaml string.
   576  #
   577  # TODO(zmerlynn): Note that this function doesn't so much "quote" as
   578  # "strip out quotes", and we really should be using a YAML library for
   579  # this, but PyYAML isn't shipped by default, and *rant rant rant ... SIGH*
   580  function yaml-quote {
   581    echo "${@:-}" | sed -e "s/'/''/g;s/^/'/i;s/$/'/i"
   582  }
   583  
   584  # Writes the cluster location into a temporary file.
   585  # Assumed vars
   586  #   ZONE
   587  function write-cluster-location {
   588    cat >"${KUBE_TEMP}/cluster-location.txt" << EOF
   589  ${ZONE}
   590  EOF
   591  }
   592  
   593  # Writes the cluster name into a temporary file.
   594  # Assumed vars
   595  #   CLUSTER_NAME
   596  function write-cluster-name {
   597    cat >"${KUBE_TEMP}/cluster-name.txt" << EOF
   598  ${CLUSTER_NAME}
   599  EOF
   600  }
   601  
   602  function write-master-env {
   603    # If the user requested that the master be part of the cluster, set the
   604    # environment variable to program the master kubelet to register itself.
   605    if [[ "${REGISTER_MASTER_KUBELET:-}" == "true" && -z "${KUBELET_APISERVER:-}" ]]; then
   606      KUBELET_APISERVER="${MASTER_NAME}"
   607    fi
   608    if [[ -z "${KUBERNETES_MASTER_NAME:-}" ]]; then
   609      KUBERNETES_MASTER_NAME="${MASTER_NAME}"
   610    fi
   611  
   612    construct-linux-kubelet-flags "master"
   613    build-linux-kube-env true "${KUBE_TEMP}/master-kube-env.yaml"
   614    build-kubelet-config true "linux" "${KUBE_TEMP}/master-kubelet-config.yaml"
   615    build-kube-master-certs "${KUBE_TEMP}/kube-master-certs.yaml"
   616  }
   617  
   618  function write-linux-node-env {
   619    if [[ -z "${KUBERNETES_MASTER_NAME:-}" ]]; then
   620      KUBERNETES_MASTER_NAME="${MASTER_NAME}"
   621    fi
   622  
   623    construct-linux-kubelet-flags "heapster"
   624    build-linux-kube-env false "${KUBE_TEMP}/heapster-kube-env.yaml"
   625    construct-linux-kubelet-flags "node"
   626    build-linux-kube-env false "${KUBE_TEMP}/node-kube-env.yaml"
   627    build-kubelet-config false "linux" "${KUBE_TEMP}/node-kubelet-config.yaml"
   628  }
   629  
   630  function write-windows-node-env {
   631    construct-windows-kubelet-flags
   632    construct-windows-kubeproxy-flags
   633    build-windows-kube-env "${KUBE_TEMP}/windows-node-kube-env.yaml"
   634    build-kubelet-config false "windows" "${KUBE_TEMP}/windows-node-kubelet-config.yaml"
   635  }
   636  
   637  function build-linux-node-labels {
   638    local node_type=$1
   639    local node_labels=""
   640    if [[ "${KUBE_PROXY_DAEMONSET:-}" == "true" && "${node_type}" != "master" ]]; then
   641      # Add kube-proxy daemonset label to node to avoid situation during cluster
   642      # upgrade/downgrade when there are two instances of kube-proxy running on a node.
   643      node_labels="node.kubernetes.io/kube-proxy-ds-ready=true"
   644    fi
   645    if [[ -n "${NODE_LABELS:-}" ]]; then
   646      node_labels="${node_labels:+${node_labels},}${NODE_LABELS}"
   647    fi
   648    if [[ -n "${NON_MASTER_NODE_LABELS:-}" && "${node_type}" != "master" ]]; then
   649      node_labels="${node_labels:+${node_labels},}${NON_MASTER_NODE_LABELS}"
   650    fi
   651    if [[ -n "${MASTER_NODE_LABELS:-}" && "${node_type}" == "master" ]]; then
   652      node_labels="${node_labels:+${node_labels},}${MASTER_NODE_LABELS}"
   653    fi
   654    echo "$node_labels"
   655  }
   656  
   657  function build-windows-node-labels {
   658    local node_labels=""
   659    if [[ -n "${WINDOWS_NODE_LABELS:-}" ]]; then
   660      node_labels="${node_labels:+${node_labels},}${WINDOWS_NODE_LABELS}"
   661    fi
   662    if [[ -n "${WINDOWS_NON_MASTER_NODE_LABELS:-}" ]]; then
   663      node_labels="${node_labels:+${node_labels},}${WINDOWS_NON_MASTER_NODE_LABELS}"
   664    fi
   665    echo "$node_labels"
   666  }
   667  
   668  # yaml-map-string-stringarray converts the encoded structure to yaml format, and echoes the result
   669  # under the provided name. If the encoded structure is empty, echoes nothing.
   670  # 1: name to be output in yaml
   671  # 2: encoded map-string-string (which may contain duplicate keys - resulting in map-string-stringarray)
   672  # 3: key-value separator (defaults to ':')
   673  # 4: item separator (defaults to ',')
   674  function yaml-map-string-stringarray {
   675    declare -r name="${1}"
   676    declare -r encoded="${2}"
   677    declare -r kv_sep="${3:-:}"
   678    declare -r item_sep="${4:-,}"
   679  
   680    declare -a pairs # indexed array
   681    declare -A map # associative array
   682    IFS="${item_sep}" read -ra pairs <<<"${encoded}" # split on item_sep
   683    for pair in "${pairs[@]}"; do
   684      declare key
   685      declare value
   686      IFS="${kv_sep}" read -r key value <<<"${pair}" # split on kv_sep
   687      map[$key]="${map[$key]+${map[$key]}${item_sep}}${value}" # append values from duplicate keys
   688    done
   689    # only output if there is a non-empty map
   690    if [[ ${#map[@]} -gt 0 ]]; then
   691      echo "${name}:"
   692      for k in "${!map[@]}"; do
   693        echo "  ${k}:"
   694        declare -a values
   695        IFS="${item_sep}" read -ra values <<<"${map[$k]}"
   696        for val in "${values[@]}"; do
   697          # declare across two lines so errexit can catch failures
   698          declare v
   699          v=$(yaml-quote "${val}")
   700          echo "    - ${v}"
   701        done
   702      done
   703    fi
   704  }
   705  
   706  # yaml-map-string-string converts the encoded structure to yaml format, and echoes the result
   707  # under the provided name. If the encoded structure is empty, echoes nothing.
   708  # 1: name to be output in yaml
   709  # 2: encoded map-string-string (no duplicate keys)
   710  # 3: bool, whether to yaml-quote the value string in the output (defaults to true)
   711  # 4: key-value separator (defaults to ':')
   712  # 5: item separator (defaults to ',')
   713  function yaml-map-string-string {
   714    declare -r name="${1}"
   715    declare -r encoded="${2}"
   716    declare -r quote_val_string="${3:-true}"
   717    declare -r kv_sep="${4:-:}"
   718    declare -r item_sep="${5:-,}"
   719  
   720    declare -a pairs # indexed array
   721    declare -A map # associative array
   722    IFS="${item_sep}" read -ra pairs <<<"${encoded}" # split on item_sep # TODO(mtaufen): try quoting this too
   723    for pair in "${pairs[@]}"; do
   724      declare key
   725      declare value
   726      IFS="${kv_sep}" read -r key value <<<"${pair}" # split on kv_sep
   727      map[$key]="${value}" # add to associative array
   728    done
   729    # only output if there is a non-empty map
   730    if [[ ${#map[@]} -gt 0 ]]; then
   731      echo "${name}:"
   732      for k in "${!map[@]}"; do
   733        if [[ "${quote_val_string}" == "true" ]]; then
   734          # declare across two lines so errexit can catch failures
   735          declare v
   736          v=$(yaml-quote "${map[$k]}")
   737          echo "  ${k}: ${v}"
   738        else
   739          echo "  ${k}: ${map[$k]}"
   740        fi
   741      done
   742    fi
   743  }
   744  
   745  # Returns kubelet flags used on both Linux and Windows nodes.
   746  function construct-common-kubelet-flags {
   747    local flags="${KUBELET_TEST_LOG_LEVEL:-"--v=2"} ${KUBELET_TEST_ARGS:-}"
   748    flags+=" --cloud-provider=${CLOUD_PROVIDER_FLAG:-external}"
   749    # TODO(mtaufen): ROTATE_CERTIFICATES seems unused; delete it?
   750    if [[ -n "${ROTATE_CERTIFICATES:-}" ]]; then
   751      flags+=" --rotate-certificates=true"
   752    fi
   753    if [[ -n "${MAX_PODS_PER_NODE:-}" ]]; then
   754      flags+=" --max-pods=${MAX_PODS_PER_NODE}"
   755    fi
   756    echo "$flags"
   757  }
   758  
   759  # Sets KUBELET_ARGS with the kubelet flags for Linux nodes.
   760  # $1: if 'true', we're rendering flags for a master, else a node
   761  function construct-linux-kubelet-flags {
   762    local node_type="$1"
   763    local flags
   764    flags="$(construct-common-kubelet-flags)"
   765    # Keep in sync with CONTAINERIZED_MOUNTER_HOME in configure-helper.sh
   766    flags+=" --experimental-mounter-path=/home/kubernetes/containerized_mounter/mounter"
   767    # Keep in sync with the mkdir command in configure-helper.sh (until the TODO is resolved)
   768    flags+=" --cert-dir=/var/lib/kubelet/pki/"
   769  
   770    # If ENABLE_AUTH_PROVIDER_GCP is set to true, kubelet is enabled to use out-of-tree auth 
   771    # credential provider instead of in-tree auth credential provider.
   772    # https://kubernetes.io/docs/tasks/kubelet-credential-provider/kubelet-credential-provider
   773    if [[ "${ENABLE_AUTH_PROVIDER_GCP:-true}" == "true" ]]; then
   774      # Keep the values of --image-credential-provider-config and --image-credential-provider-bin-dir
   775      # in sync with value of auth_config_file and auth_provider_dir set in install-auth-provider-gcp function
   776      # in gci/configure.sh.
   777      flags+="  --image-credential-provider-config=${AUTH_PROVIDER_GCP_LINUX_CONF_FILE}"
   778      flags+="  --image-credential-provider-bin-dir=${AUTH_PROVIDER_GCP_LINUX_BIN_DIR}"
   779    fi
   780  
   781    if [[ "${node_type}" == "master" ]]; then
   782      flags+=" ${MASTER_KUBELET_TEST_ARGS:-}"
   783      if [[ "${REGISTER_MASTER_KUBELET:-false}" == "true" ]]; then
   784        #TODO(mikedanese): allow static pods to start before creating a client
   785        #flags+=" --bootstrap-kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig"
   786        #flags+=" --kubeconfig=/var/lib/kubelet/kubeconfig"
   787        flags+=" --register-with-taints=node-role.kubernetes.io/control-plane=:NoSchedule"
   788        flags+=" --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig"
   789        flags+=" --register-schedulable=false"
   790      fi
   791      if [[ "${MASTER_OS_DISTRIBUTION}" == "ubuntu" ]]; then
   792        # Configure the file path for host dns configuration
   793        # as ubuntu uses systemd-resolved
   794        flags+=" --resolv-conf=/run/systemd/resolve/resolv.conf"
   795      fi
   796    else # For nodes
   797      flags+=" ${NODE_KUBELET_TEST_ARGS:-}"
   798      flags+=" --bootstrap-kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig"
   799      flags+=" --kubeconfig=/var/lib/kubelet/kubeconfig"
   800      if [[ "${node_type}" == "heapster" ]]; then
   801          flags+=" ${HEAPSTER_KUBELET_TEST_ARGS:-}"
   802      fi
   803      if [[ "${NODE_OS_DISTRIBUTION}" == "ubuntu" ]]; then
   804        # Configure the file path for host dns configuration
   805        # as ubuntu uses systemd-resolved
   806        flags+=" --resolv-conf=/run/systemd/resolve/resolv.conf"
   807      fi
   808    fi
   809    flags+=" --volume-plugin-dir=${VOLUME_PLUGIN_DIR}"
   810    local node_labels
   811    node_labels="$(build-linux-node-labels "${node_type}")"
   812    if [[ -n "${node_labels:-}" ]]; then
   813      flags+=" --node-labels=${node_labels}"
   814    fi
   815    if [[ -n "${NODE_TAINTS:-}" ]]; then
   816      flags+=" --register-with-taints=${NODE_TAINTS}"
   817    fi
   818  
   819    CONTAINER_RUNTIME_ENDPOINT=${KUBE_CONTAINER_RUNTIME_ENDPOINT:-unix:///run/containerd/containerd.sock}
   820    flags+=" --container-runtime-endpoint=${CONTAINER_RUNTIME_ENDPOINT}"
   821  
   822    if [[ "${CONTAINER_RUNTIME_ENDPOINT}" =~ /containerd.sock$ ]]; then
   823      flags+=" --runtime-cgroups=/system.slice/containerd.service"
   824    fi
   825  
   826    KUBELET_ARGS="${flags}"
   827  }
   828  
   829  # Sets KUBELET_ARGS with the kubelet flags for Windows nodes.
   830  # Note that to configure flags with explicit empty string values, we can't escape
   831  # double-quotes, because they still break sc.exe after expansion in the
   832  # binPath parameter, and single-quotes get parsed as characters instead of
   833  # string delimiters.
   834  function construct-windows-kubelet-flags {
   835    local flags
   836    flags="$(construct-common-kubelet-flags)"
   837  
   838    # Note: NODE_KUBELET_TEST_ARGS is empty in typical kube-up runs.
   839    flags+=" ${NODE_KUBELET_TEST_ARGS:-}"
   840  
   841    local node_labels
   842    node_labels="$(build-windows-node-labels)"
   843    if [[ -n "${node_labels:-}" ]]; then
   844      flags+=" --node-labels=${node_labels}"
   845    fi
   846  
   847    # Concatenate common and windows-only node taints and apply them.
   848    local node_taints="${NODE_TAINTS:-}"
   849    if [[ -n "${node_taints}" && -n "${WINDOWS_NODE_TAINTS:-}" ]]; then
   850      node_taints+=":${WINDOWS_NODE_TAINTS}"
   851    else
   852      node_taints="${WINDOWS_NODE_TAINTS:-}"
   853    fi
   854    if [[ -n "${node_taints}" ]]; then
   855      flags+=" --register-with-taints=${node_taints}"
   856    fi
   857  
   858    # Many of these flags were adapted from
   859    # https://github.com/Microsoft/SDN/blob/master/Kubernetes/windows/start-kubelet.ps1.
   860    flags+=" --config=${WINDOWS_KUBELET_CONFIG_FILE}"
   861    flags+=" --kubeconfig=${WINDOWS_KUBECONFIG_FILE}"
   862  
   863    # The directory where the TLS certs are located.
   864    flags+=" --cert-dir=${WINDOWS_PKI_DIR}"
   865    flags+=" --pod-manifest-path=${WINDOWS_MANIFESTS_DIR}"
   866  
   867    # Configure kubelet to run as a windows service.
   868    flags+=" --windows-service=true"
   869  
   870    # Configure the file path for host dns configuration
   871    flags+=" --resolv-conf=${WINDOWS_CNI_DIR}\hostdns.conf"
   872  
   873    # Both --cgroups-per-qos and --enforce-node-allocatable should be disabled on
   874    # windows; the latter requires the former to be enabled to work.
   875    flags+=" --cgroups-per-qos=false --enforce-node-allocatable="
   876  
   877    # Turn off kernel memory cgroup notification.
   878    flags+=" --kernel-memcg-notification=false"
   879  
   880    WINDOWS_CONTAINER_RUNTIME_ENDPOINT=${KUBE_WINDOWS_CONTAINER_RUNTIME_ENDPOINT:-npipe:////./pipe/containerd-containerd}
   881    flags+=" --container-runtime-endpoint=${WINDOWS_CONTAINER_RUNTIME_ENDPOINT}"
   882  
   883    # If ENABLE_AUTH_PROVIDER_GCP is set to true, kubelet is enabled to use out-of-tree auth
   884    # credential provider. https://kubernetes.io/docs/tasks/kubelet-credential-provider/kubelet-credential-provider
   885    if [[ "${ENABLE_AUTH_PROVIDER_GCP:-true}" == "true" ]]; then
   886      flags+="  --image-credential-provider-config=${AUTH_PROVIDER_GCP_WINDOWS_CONF_FILE}"
   887      flags+="  --image-credential-provider-bin-dir=${AUTH_PROVIDER_GCP_WINDOWS_BIN_DIR}"
   888    fi
   889  
   890    KUBELET_ARGS="${flags}"
   891  }
   892  
   893  function construct-windows-kubeproxy-flags {
   894    local flags=""
   895  
   896    # Use the same log level as the Kubelet during tests.
   897    flags+=" ${KUBELET_TEST_LOG_LEVEL:-"--v=2"}"
   898  
   899    # Windows uses kernelspace proxymode
   900    flags+=" --proxy-mode=kernelspace"
   901  
   902    # Configure kube-proxy to run as a windows service.
   903    flags+=" --windows-service=true"
   904  
   905    # Enabling Windows DSR mode unlocks newer network features and reduces
   906    # port usage for services.
   907    # https://techcommunity.microsoft.com/t5/networking-blog/direct-server-return-dsr-in-a-nutshell/ba-p/693710
   908    if [[ "${WINDOWS_ENABLE_DSR:-}" == "true" ]]; then
   909      flags+=" --feature-gates=WinDSR=true --enable-dsr=true "
   910    fi
   911  
   912    # Configure flags with explicit empty string values. We can't escape
   913    # double-quotes, because they still break sc.exe after expansion in the
   914    # binPath parameter, and single-quotes get parsed as characters instead
   915    # of string delimiters.
   916  
   917    KUBEPROXY_ARGS="${flags}"
   918  }
   919  
   920  # $1: if 'true', we're rendering config for a master, else a node
   921  function build-kubelet-config {
   922    local master="$1"
   923    local os="$2"
   924    local file="$3"
   925  
   926    rm -f "${file}"
   927    {
   928      print-common-kubelet-config
   929      if [[ "${master}" == "true" ]]; then
   930        print-master-kubelet-config
   931      else
   932        print-common-node-kubelet-config
   933        if [[ "${os}" == "linux" ]]; then
   934          print-linux-node-kubelet-config
   935        elif [[ "${os}" == "windows" ]]; then
   936          print-windows-node-kubelet-config
   937        else
   938          echo "Unknown OS ${os}" >&2
   939          exit 1
   940        fi
   941      fi
   942    } > "${file}"
   943  }
   944  
   945  # cat the Kubelet config yaml in common between masters, linux nodes, and
   946  # windows nodes
   947  function print-common-kubelet-config {
   948    declare quoted_dns_server_ip
   949    declare quoted_dns_domain
   950    quoted_dns_server_ip=$(yaml-quote "${DNS_SERVER_IP}")
   951    quoted_dns_domain=$(yaml-quote "${DNS_DOMAIN}")
   952    cat <<EOF
   953  kind: KubeletConfiguration
   954  apiVersion: kubelet.config.k8s.io/v1beta1
   955  cgroupRoot: /
   956  clusterDNS:
   957    - ${quoted_dns_server_ip}
   958  clusterDomain: ${quoted_dns_domain}
   959  readOnlyPort: 10255
   960  EOF
   961  
   962    # Note: ENABLE_MANIFEST_URL is used by GKE.
   963    # TODO(mtaufen): remove this since it's not used in kubernetes/kubernetes nor
   964    # kubernetes/test-infra.
   965    if [[ "${ENABLE_MANIFEST_URL:-}" == "true" ]]; then
   966      declare quoted_manifest_url
   967      quoted_manifest_url=$(yaml-quote "${MANIFEST_URL}")
   968      cat <<EOF
   969  staticPodURL: ${quoted_manifest_url}
   970  EOF
   971      yaml-map-string-stringarray 'staticPodURLHeader' "${MANIFEST_URL_HEADER}"
   972    fi
   973  
   974    if [[ -n "${EVICTION_HARD:-}" ]]; then
   975      yaml-map-string-string 'evictionHard' "${EVICTION_HARD}" true '<'
   976    fi
   977  
   978    if [[ -n "${FEATURE_GATES:-}" ]]; then
   979      yaml-map-string-string 'featureGates' "${FEATURE_GATES}" false '='
   980    fi
   981  }
   982  
   983  # cat the Kubelet config yaml for masters
   984  function print-master-kubelet-config {
   985    cat <<EOF
   986  enableDebuggingHandlers: ${MASTER_KUBELET_ENABLE_DEBUGGING_HANDLERS:-false}
   987  hairpinMode: none
   988  staticPodPath: /etc/kubernetes/manifests
   989  authentication:
   990    webhook:
   991      enabled: false
   992    anonymous:
   993      enabled: true
   994  authorization:
   995    mode: AlwaysAllow
   996  EOF
   997    if [[ "${REGISTER_MASTER_KUBELET:-false}" == "false" ]]; then
   998       # Note: Standalone mode is used by GKE
   999      declare quoted_master_ip_range
  1000      quoted_master_ip_range=$(yaml-quote "${MASTER_IP_RANGE}")
  1001       cat <<EOF
  1002  podCidr: ${quoted_master_ip_range}
  1003  EOF
  1004    fi
  1005  }
  1006  
  1007  # cat the Kubelet config yaml in common between linux nodes and windows nodes
  1008  function print-common-node-kubelet-config {
  1009    cat <<EOF
  1010  enableDebuggingHandlers: ${KUBELET_ENABLE_DEBUGGING_HANDLERS:-true}
  1011  EOF
  1012    if [[ "${HAIRPIN_MODE:-}" == "promiscuous-bridge" ]] || \
  1013       [[ "${HAIRPIN_MODE:-}" == "hairpin-veth" ]] || \
  1014       [[ "${HAIRPIN_MODE:-}" == "none" ]]; then
  1015        declare quoted_hairpin_mode
  1016        quoted_hairpin_mode=$(yaml-quote "${HAIRPIN_MODE}")
  1017        cat <<EOF
  1018  hairpinMode: ${quoted_hairpin_mode}
  1019  EOF
  1020    fi
  1021  }
  1022  
  1023  # cat the Kubelet config yaml for linux nodes
  1024  function print-linux-node-kubelet-config {
  1025    # Keep authentication.x509.clientCAFile in sync with CA_CERT_BUNDLE_PATH in configure-helper.sh
  1026    cat <<EOF
  1027  staticPodPath: /etc/kubernetes/manifests
  1028  authentication:
  1029    x509:
  1030      clientCAFile: /etc/srv/kubernetes/pki/ca-certificates.crt
  1031  EOF
  1032  }
  1033  
  1034  # cat the Kubelet config yaml for windows nodes
  1035  function print-windows-node-kubelet-config {
  1036    # Notes:
  1037    # - We don't run any static pods on Windows nodes yet.
  1038  
  1039    # TODO(mtaufen): Does it make any sense to set eviction thresholds for inodes
  1040    # on Windows?
  1041  
  1042    # TODO(pjh, mtaufen): It may make sense to use a different hairpin mode on
  1043    # Windows. We're currently using hairpin-veth, but
  1044    # https://github.com/Microsoft/SDN/blob/master/Kubernetes/windows/start-kubelet.ps1#L121
  1045    # uses promiscuous-bridge.
  1046  
  1047    # TODO(pjh, mtaufen): Does cgroupRoot make sense for Windows?
  1048  
  1049    # Keep authentication.x509.clientCAFile in sync with CA_CERT_BUNDLE_PATH in
  1050    # k8s-node-setup.psm1.
  1051    cat <<EOF
  1052  authentication:
  1053    x509:
  1054      clientCAFile: '${WINDOWS_CA_FILE}'
  1055  EOF
  1056  }
  1057  
  1058  function build-kube-master-certs {
  1059    local file=$1
  1060    rm -f "$file"
  1061    cat >"$file" <<EOF
  1062  KUBEAPISERVER_CERT: $(yaml-quote "${KUBEAPISERVER_CERT_BASE64:-}")
  1063  KUBEAPISERVER_KEY: $(yaml-quote "${KUBEAPISERVER_KEY_BASE64:-}")
  1064  CA_KEY: $(yaml-quote "${CA_KEY_BASE64:-}")
  1065  AGGREGATOR_CA_KEY: $(yaml-quote "${AGGREGATOR_CA_KEY_BASE64:-}")
  1066  REQUESTHEADER_CA_CERT: $(yaml-quote "${REQUESTHEADER_CA_CERT_BASE64:-}")
  1067  PROXY_CLIENT_CERT: $(yaml-quote "${PROXY_CLIENT_CERT_BASE64:-}")
  1068  PROXY_CLIENT_KEY: $(yaml-quote "${PROXY_CLIENT_KEY_BASE64:-}")
  1069  ETCD_APISERVER_CA_KEY: $(yaml-quote "${ETCD_APISERVER_CA_KEY_BASE64:-}")
  1070  ETCD_APISERVER_CA_CERT: $(yaml-quote "${ETCD_APISERVER_CA_CERT_BASE64:-}")
  1071  ETCD_APISERVER_SERVER_KEY: $(yaml-quote "${ETCD_APISERVER_SERVER_KEY_BASE64:-}")
  1072  ETCD_APISERVER_SERVER_CERT: $(yaml-quote "${ETCD_APISERVER_SERVER_CERT_BASE64:-}")
  1073  ETCD_APISERVER_CLIENT_KEY: $(yaml-quote "${ETCD_APISERVER_CLIENT_KEY_BASE64:-}")
  1074  ETCD_APISERVER_CLIENT_CERT: $(yaml-quote "${ETCD_APISERVER_CLIENT_CERT_BASE64:-}")
  1075  CLOUD_PVL_ADMISSION_CA_KEY: $(yaml-quote "${CLOUD_PVL_ADMISSION_CA_KEY_BASE64:-}")
  1076  CLOUD_PVL_ADMISSION_CA_CERT: $(yaml-quote "${CLOUD_PVL_ADMISSION_CA_CERT_BASE64:-}")
  1077  CLOUD_PVL_ADMISSION_CERT: $(yaml-quote "${CLOUD_PVL_ADMISSION_CERT_BASE64:-}")
  1078  CLOUD_PVL_ADMISSION_KEY: $(yaml-quote "${CLOUD_PVL_ADMISSION_KEY_BASE64:-}")
  1079  KONNECTIVITY_SERVER_CA_KEY: $(yaml-quote "${KONNECTIVITY_SERVER_CA_KEY_BASE64:-}")
  1080  KONNECTIVITY_SERVER_CA_CERT: $(yaml-quote "${KONNECTIVITY_SERVER_CA_CERT_BASE64:-}")
  1081  KONNECTIVITY_SERVER_CERT: $(yaml-quote "${KONNECTIVITY_SERVER_CERT_BASE64:-}")
  1082  KONNECTIVITY_SERVER_KEY: $(yaml-quote "${KONNECTIVITY_SERVER_KEY_BASE64:-}")
  1083  KONNECTIVITY_SERVER_CLIENT_CERT: $(yaml-quote "${KONNECTIVITY_SERVER_CLIENT_CERT_BASE64:-}")
  1084  KONNECTIVITY_SERVER_CLIENT_KEY: $(yaml-quote "${KONNECTIVITY_SERVER_CLIENT_KEY_BASE64:-}")
  1085  KONNECTIVITY_AGENT_CA_KEY: $(yaml-quote "${KONNECTIVITY_AGENT_CA_KEY_BASE64:-}")
  1086  KONNECTIVITY_AGENT_CA_CERT: $(yaml-quote "${KONNECTIVITY_AGENT_CA_CERT_BASE64:-}")
  1087  KONNECTIVITY_AGENT_CERT: $(yaml-quote "${KONNECTIVITY_AGENT_CERT_BASE64:-}")
  1088  KONNECTIVITY_AGENT_KEY: $(yaml-quote "${KONNECTIVITY_AGENT_KEY_BASE64:-}")
  1089  KONNECTIVITY_AGENT_CLIENT_CERT: $(yaml-quote "${KONNECTIVITY_AGENT_CLIENT_CERT_BASE64:-}")
  1090  KONNECTIVITY_AGENT_CLIENT_KEY: $(yaml-quote "${KONNECTIVITY_AGENT_CLIENT_KEY_BASE64:-}")
  1091  EOF
  1092  }
  1093  
  1094  # $1: if 'true', we're building a master yaml, else a node
  1095  function build-linux-kube-env {
  1096    local master="$1"
  1097    local file="$2"
  1098  
  1099    local server_binary_tar_url=$SERVER_BINARY_TAR_URL
  1100    local kube_manifests_tar_url="${KUBE_MANIFESTS_TAR_URL:-}"
  1101    if [[ "${master}" == "true" && "${MASTER_OS_DISTRIBUTION}" == "ubuntu" ]] || \
  1102       [[ "${master}" == "false" && ("${NODE_OS_DISTRIBUTION}" == "ubuntu" || "${NODE_OS_DISTRIBUTION}" == "custom") ]]; then
  1103      # TODO: Support fallback .tar.gz settings on Container Linux
  1104      server_binary_tar_url=$(split_csv "${SERVER_BINARY_TAR_URL}")
  1105      kube_manifests_tar_url=$(split_csv "${KUBE_MANIFESTS_TAR_URL}")
  1106    fi
  1107  
  1108    rm -f "$file"
  1109    cat >"$file" <<EOF
  1110  CLUSTER_NAME: $(yaml-quote "${CLUSTER_NAME}")
  1111  ENV_TIMESTAMP: $(yaml-quote "$(date -u +%Y-%m-%dT%T%z)")
  1112  INSTANCE_PREFIX: $(yaml-quote "${INSTANCE_PREFIX}")
  1113  NODE_INSTANCE_PREFIX: $(yaml-quote "${NODE_INSTANCE_PREFIX}")
  1114  NODE_TAGS: $(yaml-quote "${NODE_TAGS:-}")
  1115  NODE_NETWORK: $(yaml-quote "${NETWORK:-}")
  1116  NODE_SUBNETWORK: $(yaml-quote "${SUBNETWORK:-}")
  1117  CLUSTER_IP_RANGE: $(yaml-quote "${CLUSTER_IP_RANGE:-10.244.0.0/16}")
  1118  SERVER_BINARY_TAR_URL: $(yaml-quote "${server_binary_tar_url}")
  1119  SERVER_BINARY_TAR_HASH: $(yaml-quote "${SERVER_BINARY_TAR_HASH}")
  1120  PROJECT_ID: $(yaml-quote "${PROJECT}")
  1121  NETWORK_PROJECT_ID: $(yaml-quote "${NETWORK_PROJECT}")
  1122  SERVICE_CLUSTER_IP_RANGE: $(yaml-quote "${SERVICE_CLUSTER_IP_RANGE}")
  1123  KUBERNETES_MASTER_NAME: $(yaml-quote "${KUBERNETES_MASTER_NAME}")
  1124  ALLOCATE_NODE_CIDRS: $(yaml-quote "${ALLOCATE_NODE_CIDRS:-false}")
  1125  ENABLE_METRICS_SERVER: $(yaml-quote "${ENABLE_METRICS_SERVER:-false}")
  1126  ENABLE_METADATA_AGENT: $(yaml-quote "${ENABLE_METADATA_AGENT:-none}")
  1127  METADATA_AGENT_CPU_REQUEST: $(yaml-quote "${METADATA_AGENT_CPU_REQUEST:-}")
  1128  METADATA_AGENT_MEMORY_REQUEST: $(yaml-quote "${METADATA_AGENT_MEMORY_REQUEST:-}")
  1129  METADATA_AGENT_CLUSTER_LEVEL_CPU_REQUEST: $(yaml-quote "${METADATA_AGENT_CLUSTER_LEVEL_CPU_REQUEST:-}")
  1130  METADATA_AGENT_CLUSTER_LEVEL_MEMORY_REQUEST: $(yaml-quote "${METADATA_AGENT_CLUSTER_LEVEL_MEMORY_REQUEST:-}")
  1131  DOCKER_REGISTRY_MIRROR_URL: $(yaml-quote "${DOCKER_REGISTRY_MIRROR_URL:-}")
  1132  ENABLE_L7_LOADBALANCING: $(yaml-quote "${ENABLE_L7_LOADBALANCING:-none}")
  1133  ENABLE_CLUSTER_LOGGING: $(yaml-quote "${ENABLE_CLUSTER_LOGGING:-false}")
  1134  ENABLE_AUTH_PROVIDER_GCP: $(yaml-quote "${ENABLE_AUTH_PROVIDER_GCP:-true}")
  1135  ENABLE_NODE_PROBLEM_DETECTOR: $(yaml-quote "${ENABLE_NODE_PROBLEM_DETECTOR:-none}")
  1136  NODE_PROBLEM_DETECTOR_VERSION: $(yaml-quote "${NODE_PROBLEM_DETECTOR_VERSION:-}")
  1137  NODE_PROBLEM_DETECTOR_TAR_HASH: $(yaml-quote "${NODE_PROBLEM_DETECTOR_TAR_HASH:-}")
  1138  NODE_PROBLEM_DETECTOR_RELEASE_PATH: $(yaml-quote "${NODE_PROBLEM_DETECTOR_RELEASE_PATH:-}")
  1139  NODE_PROBLEM_DETECTOR_CUSTOM_FLAGS: $(yaml-quote "${NODE_PROBLEM_DETECTOR_CUSTOM_FLAGS:-}")
  1140  CNI_STORAGE_URL_BASE: $(yaml-quote "${CNI_STORAGE_URL_BASE:-}")
  1141  CNI_TAR_PREFIX: $(yaml-quote "${CNI_TAR_PREFIX:-}")
  1142  CNI_VERSION: $(yaml-quote "${CNI_VERSION:-}")
  1143  CNI_HASH: $(yaml-quote "${CNI_HASH:-}")
  1144  ENABLE_NODE_LOGGING: $(yaml-quote "${ENABLE_NODE_LOGGING:-false}")
  1145  LOGGING_DESTINATION: $(yaml-quote "${LOGGING_DESTINATION:-}")
  1146  ELASTICSEARCH_LOGGING_REPLICAS: $(yaml-quote "${ELASTICSEARCH_LOGGING_REPLICAS:-}")
  1147  ENABLE_CLUSTER_DNS: $(yaml-quote "${ENABLE_CLUSTER_DNS:-false}")
  1148  CLUSTER_DNS_CORE_DNS: $(yaml-quote "${CLUSTER_DNS_CORE_DNS:-true}")
  1149  ENABLE_NODELOCAL_DNS: $(yaml-quote "${ENABLE_NODELOCAL_DNS:-false}")
  1150  DNS_SERVER_IP: $(yaml-quote "${DNS_SERVER_IP:-}")
  1151  LOCAL_DNS_IP: $(yaml-quote "${LOCAL_DNS_IP:-}")
  1152  DNS_DOMAIN: $(yaml-quote "${DNS_DOMAIN:-}")
  1153  DNS_MEMORY_LIMIT: $(yaml-quote "${DNS_MEMORY_LIMIT:-}")
  1154  ENABLE_DNS_HORIZONTAL_AUTOSCALER: $(yaml-quote "${ENABLE_DNS_HORIZONTAL_AUTOSCALER:-false}")
  1155  KUBE_PROXY_DAEMONSET: $(yaml-quote "${KUBE_PROXY_DAEMONSET:-false}")
  1156  KUBE_PROXY_TOKEN: $(yaml-quote "${KUBE_PROXY_TOKEN:-}")
  1157  KUBE_PROXY_MODE: $(yaml-quote "${KUBE_PROXY_MODE:-iptables}")
  1158  DETECT_LOCAL_MODE: $(yaml-quote "${DETECT_LOCAL_MODE:-}")
  1159  NODE_PROBLEM_DETECTOR_TOKEN: $(yaml-quote "${NODE_PROBLEM_DETECTOR_TOKEN:-}")
  1160  ADMISSION_CONTROL: $(yaml-quote "${ADMISSION_CONTROL:-}")
  1161  MASTER_IP_RANGE: $(yaml-quote "${MASTER_IP_RANGE}")
  1162  RUNTIME_CONFIG: $(yaml-quote "${RUNTIME_CONFIG}")
  1163  CA_CERT: $(yaml-quote "${CA_CERT_BASE64:-}")
  1164  KUBELET_CERT: $(yaml-quote "${KUBELET_CERT_BASE64:-}")
  1165  KUBELET_KEY: $(yaml-quote "${KUBELET_KEY_BASE64:-}")
  1166  NETWORK_PROVIDER: $(yaml-quote "${NETWORK_PROVIDER:-}")
  1167  NETWORK_POLICY_PROVIDER: $(yaml-quote "${NETWORK_POLICY_PROVIDER:-}")
  1168  HAIRPIN_MODE: $(yaml-quote "${HAIRPIN_MODE:-}")
  1169  E2E_STORAGE_TEST_ENVIRONMENT: $(yaml-quote "${E2E_STORAGE_TEST_ENVIRONMENT:-}")
  1170  KUBE_DOCKER_REGISTRY: $(yaml-quote "${KUBE_DOCKER_REGISTRY:-}")
  1171  KUBE_ADDON_REGISTRY: $(yaml-quote "${KUBE_ADDON_REGISTRY:-}")
  1172  MULTIZONE: $(yaml-quote "${MULTIZONE:-}")
  1173  MULTIMASTER: $(yaml-quote "${MULTIMASTER:-}")
  1174  NON_MASQUERADE_CIDR: $(yaml-quote "${NON_MASQUERADE_CIDR:-}")
  1175  ENABLE_DEFAULT_STORAGE_CLASS: $(yaml-quote "${ENABLE_DEFAULT_STORAGE_CLASS:-}")
  1176  ENABLE_VOLUME_SNAPSHOTS: $(yaml-quote "${ENABLE_VOLUME_SNAPSHOTS:-}")
  1177  ENABLE_APISERVER_ADVANCED_AUDIT: $(yaml-quote "${ENABLE_APISERVER_ADVANCED_AUDIT:-}")
  1178  ENABLE_APISERVER_DYNAMIC_AUDIT: $(yaml-quote "${ENABLE_APISERVER_DYNAMIC_AUDIT:-}")
  1179  ENABLE_CACHE_MUTATION_DETECTOR: $(yaml-quote "${ENABLE_CACHE_MUTATION_DETECTOR:-false}")
  1180  ENABLE_KUBE_WATCHLIST_INCONSISTENCY_DETECTOR: $(yaml-quote "${ENABLE_KUBE_WATCHLIST_INCONSISTENCY_DETECTOR:-false}")
  1181  ENABLE_PATCH_CONVERSION_DETECTOR: $(yaml-quote "${ENABLE_PATCH_CONVERSION_DETECTOR:-false}")
  1182  ADVANCED_AUDIT_POLICY: $(yaml-quote "${ADVANCED_AUDIT_POLICY:-}")
  1183  ADVANCED_AUDIT_BACKEND: $(yaml-quote "${ADVANCED_AUDIT_BACKEND:-log}")
  1184  ADVANCED_AUDIT_TRUNCATING_BACKEND: $(yaml-quote "${ADVANCED_AUDIT_TRUNCATING_BACKEND:-true}")
  1185  ADVANCED_AUDIT_LOG_MODE: $(yaml-quote "${ADVANCED_AUDIT_LOG_MODE:-}")
  1186  ADVANCED_AUDIT_LOG_BUFFER_SIZE: $(yaml-quote "${ADVANCED_AUDIT_LOG_BUFFER_SIZE:-}")
  1187  ADVANCED_AUDIT_LOG_MAX_BATCH_SIZE: $(yaml-quote "${ADVANCED_AUDIT_LOG_MAX_BATCH_SIZE:-}")
  1188  ADVANCED_AUDIT_LOG_MAX_BATCH_WAIT: $(yaml-quote "${ADVANCED_AUDIT_LOG_MAX_BATCH_WAIT:-}")
  1189  ADVANCED_AUDIT_LOG_THROTTLE_QPS: $(yaml-quote "${ADVANCED_AUDIT_LOG_THROTTLE_QPS:-}")
  1190  ADVANCED_AUDIT_LOG_THROTTLE_BURST: $(yaml-quote "${ADVANCED_AUDIT_LOG_THROTTLE_BURST:-}")
  1191  ADVANCED_AUDIT_LOG_INITIAL_BACKOFF: $(yaml-quote "${ADVANCED_AUDIT_LOG_INITIAL_BACKOFF:-}")
  1192  ADVANCED_AUDIT_WEBHOOK_MODE: $(yaml-quote "${ADVANCED_AUDIT_WEBHOOK_MODE:-}")
  1193  ADVANCED_AUDIT_WEBHOOK_BUFFER_SIZE: $(yaml-quote "${ADVANCED_AUDIT_WEBHOOK_BUFFER_SIZE:-}")
  1194  ADVANCED_AUDIT_WEBHOOK_MAX_BATCH_SIZE: $(yaml-quote "${ADVANCED_AUDIT_WEBHOOK_MAX_BATCH_SIZE:-}")
  1195  ADVANCED_AUDIT_WEBHOOK_MAX_BATCH_WAIT: $(yaml-quote "${ADVANCED_AUDIT_WEBHOOK_MAX_BATCH_WAIT:-}")
  1196  ADVANCED_AUDIT_WEBHOOK_THROTTLE_QPS: $(yaml-quote "${ADVANCED_AUDIT_WEBHOOK_THROTTLE_QPS:-}")
  1197  ADVANCED_AUDIT_WEBHOOK_THROTTLE_BURST: $(yaml-quote "${ADVANCED_AUDIT_WEBHOOK_THROTTLE_BURST:-}")
  1198  ADVANCED_AUDIT_WEBHOOK_INITIAL_BACKOFF: $(yaml-quote "${ADVANCED_AUDIT_WEBHOOK_INITIAL_BACKOFF:-}")
  1199  GCE_API_ENDPOINT: $(yaml-quote "${GCE_API_ENDPOINT:-}")
  1200  GCE_GLBC_IMAGE: $(yaml-quote "${GCE_GLBC_IMAGE:-}")
  1201  CUSTOM_INGRESS_YAML: |
  1202  ${CUSTOM_INGRESS_YAML//\'/\'\'}
  1203  ENABLE_NODE_JOURNAL: $(yaml-quote "${ENABLE_NODE_JOURNAL:-false}")
  1204  PROMETHEUS_TO_SD_ENDPOINT: $(yaml-quote "${PROMETHEUS_TO_SD_ENDPOINT:-}")
  1205  PROMETHEUS_TO_SD_PREFIX: $(yaml-quote "${PROMETHEUS_TO_SD_PREFIX:-}")
  1206  ENABLE_PROMETHEUS_TO_SD: $(yaml-quote "${ENABLE_PROMETHEUS_TO_SD:-false}")
  1207  DISABLE_PROMETHEUS_TO_SD_IN_DS: $(yaml-quote "${DISABLE_PROMETHEUS_TO_SD_IN_DS:-false}")
  1208  CONTAINER_RUNTIME_ENDPOINT: $(yaml-quote "${CONTAINER_RUNTIME_ENDPOINT:-}")
  1209  CONTAINER_RUNTIME_NAME: $(yaml-quote "${CONTAINER_RUNTIME_NAME:-}")
  1210  CONTAINER_RUNTIME_TEST_HANDLER: $(yaml-quote "${CONTAINER_RUNTIME_TEST_HANDLER:-}")
  1211  CONTAINERD_INFRA_CONTAINER: $(yaml-quote "${CONTAINER_INFRA_CONTAINER:-}")
  1212  UBUNTU_INSTALL_CONTAINERD_VERSION: $(yaml-quote "${UBUNTU_INSTALL_CONTAINERD_VERSION:-}")
  1213  UBUNTU_INSTALL_RUNC_VERSION: $(yaml-quote "${UBUNTU_INSTALL_RUNC_VERSION:-}")
  1214  COS_INSTALL_CONTAINERD_VERSION: $(yaml-quote "${COS_INSTALL_CONTAINERD_VERSION:-}")
  1215  COS_INSTALL_RUNC_VERSION: $(yaml-quote "${COS_INSTALL_RUNC_VERSION:-}")
  1216  NODE_LOCAL_SSDS_EXT: $(yaml-quote "${NODE_LOCAL_SSDS_EXT:-}")
  1217  NODE_LOCAL_SSDS_EPHEMERAL: $(yaml-quote "${NODE_LOCAL_SSDS_EPHEMERAL:-}")
  1218  LOAD_IMAGE_COMMAND: $(yaml-quote "${LOAD_IMAGE_COMMAND:-}")
  1219  ZONE: $(yaml-quote "${ZONE}")
  1220  REGION: $(yaml-quote "${REGION}")
  1221  VOLUME_PLUGIN_DIR: $(yaml-quote "${VOLUME_PLUGIN_DIR}")
  1222  KUBELET_ARGS: $(yaml-quote "${KUBELET_ARGS}")
  1223  REQUIRE_METADATA_KUBELET_CONFIG_FILE: $(yaml-quote true)
  1224  ENABLE_NETD: $(yaml-quote "${ENABLE_NETD:-false}")
  1225  CUSTOM_NETD_YAML: |
  1226  ${CUSTOM_NETD_YAML//\'/\'\'}
  1227  CUSTOM_CALICO_NODE_DAEMONSET_YAML: |
  1228  ${CUSTOM_CALICO_NODE_DAEMONSET_YAML//\'/\'\'}
  1229  CUSTOM_TYPHA_DEPLOYMENT_YAML: |
  1230  ${CUSTOM_TYPHA_DEPLOYMENT_YAML//\'/\'\'}
  1231  CONCURRENT_SERVICE_SYNCS: $(yaml-quote "${CONCURRENT_SERVICE_SYNCS:-}")
  1232  AUTH_PROVIDER_GCP_STORAGE_PATH: $(yaml-quote "${AUTH_PROVIDER_GCP_STORAGE_PATH}")
  1233  AUTH_PROVIDER_GCP_VERSION: $(yaml-quote "${AUTH_PROVIDER_GCP_VERSION}")
  1234  AUTH_PROVIDER_GCP_LINUX_BIN_DIR: $(yaml-quote "${AUTH_PROVIDER_GCP_LINUX_BIN_DIR}")
  1235  AUTH_PROVIDER_GCP_LINUX_CONF_FILE: $(yaml-quote "${AUTH_PROVIDER_GCP_LINUX_CONF_FILE}")
  1236  EOF
  1237    if [[ "${master}" == "true" && "${MASTER_OS_DISTRIBUTION}" == "gci" ]] || \
  1238       [[ "${master}" == "false" && "${NODE_OS_DISTRIBUTION}" == "gci" ]]  || \
  1239       [[ "${master}" == "true" && "${MASTER_OS_DISTRIBUTION}" == "cos" ]] || \
  1240       [[ "${master}" == "false" && "${NODE_OS_DISTRIBUTION}" == "cos" ]]; then
  1241      cat >>"$file" <<EOF
  1242  REMOUNT_VOLUME_PLUGIN_DIR: $(yaml-quote "${REMOUNT_VOLUME_PLUGIN_DIR:-true}")
  1243  EOF
  1244    fi
  1245    if [[ "${master}" == "false" ]]; then
  1246      cat >>"$file" <<EOF
  1247  KONNECTIVITY_AGENT_CA_CERT: $(yaml-quote "${KONNECTIVITY_AGENT_CA_CERT_BASE64:-}")
  1248  KONNECTIVITY_AGENT_CLIENT_KEY: $(yaml-quote "${KONNECTIVITY_AGENT_CLIENT_KEY_BASE64:-}")
  1249  KONNECTIVITY_AGENT_CLIENT_CERT: $(yaml-quote "${KONNECTIVITY_AGENT_CLIENT_CERT_BASE64:-}")
  1250  EOF
  1251    fi
  1252    if [ -n "${KUBE_APISERVER_REQUEST_TIMEOUT:-}" ]; then
  1253      cat >>"$file" <<EOF
  1254  KUBE_APISERVER_REQUEST_TIMEOUT: $(yaml-quote "${KUBE_APISERVER_REQUEST_TIMEOUT}")
  1255  EOF
  1256    fi
  1257    if [ -n "${TERMINATED_POD_GC_THRESHOLD:-}" ]; then
  1258      cat >>"$file" <<EOF
  1259  TERMINATED_POD_GC_THRESHOLD: $(yaml-quote "${TERMINATED_POD_GC_THRESHOLD}")
  1260  EOF
  1261    fi
  1262    if [[ "${master}" == "true" && ("${MASTER_OS_DISTRIBUTION}" == "trusty" || "${MASTER_OS_DISTRIBUTION}" == "gci" || "${MASTER_OS_DISTRIBUTION}" == "ubuntu") ]] || \
  1263       [[ "${master}" == "false" && ("${NODE_OS_DISTRIBUTION}" == "trusty" || "${NODE_OS_DISTRIBUTION}" == "gci" || "${NODE_OS_DISTRIBUTION}" = "ubuntu" || "${NODE_OS_DISTRIBUTION}" = "custom") ]] ; then
  1264      cat >>"$file" <<EOF
  1265  KUBE_MANIFESTS_TAR_URL: $(yaml-quote "${kube_manifests_tar_url}")
  1266  KUBE_MANIFESTS_TAR_HASH: $(yaml-quote "${KUBE_MANIFESTS_TAR_HASH}")
  1267  EOF
  1268    fi
  1269    if [ -n "${TEST_CLUSTER:-}" ]; then
  1270      cat >>"$file" <<EOF
  1271  TEST_CLUSTER: $(yaml-quote "${TEST_CLUSTER}")
  1272  EOF
  1273    fi
  1274    if [ -n "${DOCKER_TEST_LOG_LEVEL:-}" ]; then
  1275        cat >>"$file" <<EOF
  1276  DOCKER_TEST_LOG_LEVEL: $(yaml-quote "${DOCKER_TEST_LOG_LEVEL}")
  1277  EOF
  1278    fi
  1279    if [ -n "${DOCKER_LOG_DRIVER:-}" ]; then
  1280        cat >>"$file" <<EOF
  1281  DOCKER_LOG_DRIVER: $(yaml-quote "${DOCKER_LOG_DRIVER}")
  1282  EOF
  1283    fi
  1284    if [ -n "${DOCKER_LOG_MAX_SIZE:-}" ]; then
  1285        cat >>"$file" <<EOF
  1286  DOCKER_LOG_MAX_SIZE: $(yaml-quote "${DOCKER_LOG_MAX_SIZE}")
  1287  EOF
  1288    fi
  1289    if [ -n "${DOCKER_LOG_MAX_FILE:-}" ]; then
  1290        cat >>"$file" <<EOF
  1291  DOCKER_LOG_MAX_FILE: $(yaml-quote "${DOCKER_LOG_MAX_FILE}")
  1292  EOF
  1293    fi
  1294    if [ -n "${CLOUD_PROVIDER_FLAG:-}" ]; then
  1295      cat >>"$file" <<EOF
  1296  CLOUD_PROVIDER_FLAG: $(yaml-quote "${CLOUD_PROVIDER_FLAG}")
  1297  EOF
  1298    fi
  1299    if [ -n "${FEATURE_GATES:-}" ]; then
  1300      cat >>"$file" <<EOF
  1301  FEATURE_GATES: $(yaml-quote "${FEATURE_GATES}")
  1302  EOF
  1303    fi
  1304    if [ -n "${RUN_CONTROLLERS:-}" ]; then
  1305      cat >>"$file" <<EOF
  1306  RUN_CONTROLLERS: $(yaml-quote "${RUN_CONTROLLERS}")
  1307  EOF
  1308    fi
  1309    if [ -n "${RUN_CCM_CONTROLLERS:-}" ]; then
  1310      cat >>"$file" <<EOF
  1311  RUN_CCM_CONTROLLERS: $(yaml-quote "${RUN_CCM_CONTROLLERS}")
  1312  EOF
  1313    fi
  1314    if [ -n "${PROVIDER_VARS:-}" ]; then
  1315      local var_name
  1316      local var_value
  1317  
  1318      for var_name in ${PROVIDER_VARS}; do
  1319        eval "local var_value=\$(yaml-quote \${${var_name}})"
  1320        cat >>"$file" <<EOF
  1321  ${var_name}: ${var_value}
  1322  EOF
  1323      done
  1324    fi
  1325  
  1326    if [[ "${master}" == "true" ]]; then
  1327      # Master-only env vars.
  1328      cat >>"$file" <<EOF
  1329  KUBERNETES_MASTER: $(yaml-quote 'true')
  1330  KUBE_USER: $(yaml-quote "${KUBE_USER}")
  1331  KUBE_PASSWORD: $(yaml-quote "${KUBE_PASSWORD}")
  1332  KUBE_BEARER_TOKEN: $(yaml-quote "${KUBE_BEARER_TOKEN}")
  1333  MASTER_CERT: $(yaml-quote "${MASTER_CERT_BASE64:-}")
  1334  MASTER_KEY: $(yaml-quote "${MASTER_KEY_BASE64:-}")
  1335  KUBECFG_CERT: $(yaml-quote "${KUBECFG_CERT_BASE64:-}")
  1336  KUBECFG_KEY: $(yaml-quote "${KUBECFG_KEY_BASE64:-}")
  1337  KUBELET_APISERVER: $(yaml-quote "${KUBELET_APISERVER:-}")
  1338  NUM_NODES: $(yaml-quote "${NUM_NODES}")
  1339  STORAGE_BACKEND: $(yaml-quote "${STORAGE_BACKEND:-etcd3}")
  1340  STORAGE_MEDIA_TYPE: $(yaml-quote "${STORAGE_MEDIA_TYPE:-}")
  1341  ENABLE_GARBAGE_COLLECTOR: $(yaml-quote "${ENABLE_GARBAGE_COLLECTOR:-}")
  1342  ENABLE_LEGACY_ABAC: $(yaml-quote "${ENABLE_LEGACY_ABAC:-}")
  1343  MASTER_ADVERTISE_ADDRESS: $(yaml-quote "${MASTER_ADVERTISE_ADDRESS:-}")
  1344  ETCD_CA_KEY: $(yaml-quote "${ETCD_CA_KEY_BASE64:-}")
  1345  ETCD_CA_CERT: $(yaml-quote "${ETCD_CA_CERT_BASE64:-}")
  1346  ETCD_PEER_KEY: $(yaml-quote "${ETCD_PEER_KEY_BASE64:-}")
  1347  ETCD_PEER_CERT: $(yaml-quote "${ETCD_PEER_CERT_BASE64:-}")
  1348  SERVICEACCOUNT_ISSUER: $(yaml-quote "${SERVICEACCOUNT_ISSUER:-}")
  1349  KUBECTL_PRUNE_WHITELIST_OVERRIDE: $(yaml-quote "${KUBECTL_PRUNE_WHITELIST_OVERRIDE:-}")
  1350  CCM_FEATURE_GATES:  $(yaml-quote "${CCM_FEATURE_GATES:-}")
  1351  KUBE_SCHEDULER_RUNASUSER: 2001
  1352  KUBE_SCHEDULER_RUNASGROUP: 2001
  1353  KUBE_ADDON_MANAGER_RUNASUSER: 2002
  1354  KUBE_ADDON_MANAGER_RUNASGROUP: 2002
  1355  KUBE_CONTROLLER_MANAGER_RUNASUSER: 2003
  1356  KUBE_CONTROLLER_MANAGER_RUNASGROUP: 2003
  1357  KUBE_API_SERVER_RUNASUSER: 2004
  1358  KUBE_API_SERVER_RUNASGROUP: 2004
  1359  KUBE_PKI_READERS_GROUP: 2005
  1360  ETCD_RUNASUSER: 2006
  1361  ETCD_RUNASGROUP: 2006
  1362  KUBE_POD_LOG_READERS_GROUP: 2007
  1363  KONNECTIVITY_SERVER_RUNASUSER: 2008
  1364  KONNECTIVITY_SERVER_RUNASGROUP: 2008
  1365  KONNECTIVITY_SERVER_SOCKET_WRITER_GROUP: 2008
  1366  CLOUD_CONTROLLER_MANAGER_RUNASUSER: 2009
  1367  CLOUD_CONTROLLER_MANAGER_RUNASGROUP: 2009
  1368  CLUSTER_AUTOSCALER_RUNASUSER: 2010
  1369  CLUSTER_AUTOSCALER_RUNASGROUP: 2010
  1370  
  1371  EOF
  1372      # KUBE_APISERVER_REQUEST_TIMEOUT_SEC (if set) controls the --request-timeout
  1373      # flag
  1374      if [ -n "${KUBE_APISERVER_REQUEST_TIMEOUT_SEC:-}" ]; then
  1375        cat >>"$file" <<EOF
  1376  KUBE_APISERVER_REQUEST_TIMEOUT_SEC: $(yaml-quote "${KUBE_APISERVER_REQUEST_TIMEOUT_SEC}")
  1377  EOF
  1378      fi
  1379      # KUBE_APISERVER_GODEBUG (if set) controls the value of GODEBUG env var for kube-apiserver.
  1380      if [ -n "${KUBE_APISERVER_GODEBUG:-}" ]; then
  1381        cat >>"$file" <<EOF
  1382  KUBE_APISERVER_GODEBUG: $(yaml-quote "${KUBE_APISERVER_GODEBUG}")
  1383  EOF
  1384      fi
  1385      # ETCD_IMAGE (if set) allows to use a custom etcd image.
  1386      if [ -n "${ETCD_IMAGE:-}" ]; then
  1387        cat >>"$file" <<EOF
  1388  ETCD_IMAGE: $(yaml-quote "${ETCD_IMAGE}")
  1389  EOF
  1390      fi
  1391      # ETCD_DOCKER_REPOSITORY (if set) allows to use a custom etcd docker repository to pull the etcd image from.
  1392      if [ -n "${ETCD_DOCKER_REPOSITORY:-}" ]; then
  1393        cat >>"$file" <<EOF
  1394  ETCD_DOCKER_REPOSITORY: $(yaml-quote "${ETCD_DOCKER_REPOSITORY}")
  1395  EOF
  1396      fi
  1397      # ETCD_VERSION (if set) allows you to use custom version of etcd.
  1398      # The main purpose of using it may be rollback of etcd v3 API,
  1399      # where we need 3.0.* image, but are rolling back to 2.3.7.
  1400      if [ -n "${ETCD_VERSION:-}" ]; then
  1401        cat >>"$file" <<EOF
  1402  ETCD_VERSION: $(yaml-quote "${ETCD_VERSION}")
  1403  EOF
  1404      fi
  1405      if [ -n "${ETCD_HOSTNAME:-}" ]; then
  1406        cat >>"$file" <<EOF
  1407  ETCD_HOSTNAME: $(yaml-quote "${ETCD_HOSTNAME}")
  1408  EOF
  1409      fi
  1410      if [ -n "${ETCD_LIVENESS_PROBE_INITIAL_DELAY_SEC:-}" ]; then
  1411        cat >>"$file" <<EOF
  1412  ETCD_LIVENESS_PROBE_INITIAL_DELAY_SEC: $(yaml-quote "${ETCD_LIVENESS_PROBE_INITIAL_DELAY_SEC}")
  1413  EOF
  1414      fi
  1415      if [ -n "${KUBE_APISERVER_LIVENESS_PROBE_INITIAL_DELAY_SEC:-}" ]; then
  1416        cat >>"$file" <<EOF
  1417  KUBE_APISERVER_LIVENESS_PROBE_INITIAL_DELAY_SEC: $(yaml-quote "${KUBE_APISERVER_LIVENESS_PROBE_INITIAL_DELAY_SEC}")
  1418  EOF
  1419      fi
  1420      if [ -n "${ETCD_COMPACTION_INTERVAL_SEC:-}" ]; then
  1421        cat >>"$file" <<EOF
  1422  ETCD_COMPACTION_INTERVAL_SEC: $(yaml-quote "${ETCD_COMPACTION_INTERVAL_SEC}")
  1423  EOF
  1424      fi
  1425      if [ -n "${ETCD_QUOTA_BACKEND_BYTES:-}" ]; then
  1426        cat >>"$file" <<EOF
  1427  ETCD_QUOTA_BACKEND_BYTES: $(yaml-quote "${ETCD_QUOTA_BACKEND_BYTES}")
  1428  EOF
  1429      fi
  1430      if [ -n "${ETCD_EXTRA_ARGS:-}" ]; then
  1431      cat >>"$file" <<EOF
  1432  ETCD_EXTRA_ARGS: $(yaml-quote "${ETCD_EXTRA_ARGS}")
  1433  EOF
  1434      fi
  1435      if [ -n "${ETCD_SERVERS:-}" ]; then
  1436      cat >>"$file" <<EOF
  1437  ETCD_SERVERS: $(yaml-quote "${ETCD_SERVERS}")
  1438  EOF
  1439      fi
  1440      if [ -n "${ETCD_SERVERS_OVERRIDES:-}" ]; then
  1441      cat >>"$file" <<EOF
  1442  ETCD_SERVERS_OVERRIDES: $(yaml-quote "${ETCD_SERVERS_OVERRIDES}")
  1443  EOF
  1444      fi
  1445      if [ -n "${APISERVER_TEST_ARGS:-}" ]; then
  1446        cat >>"$file" <<EOF
  1447  APISERVER_TEST_ARGS: $(yaml-quote "${APISERVER_TEST_ARGS}")
  1448  EOF
  1449      fi
  1450      if [ -n "${CONTROLLER_MANAGER_TEST_ARGS:-}" ]; then
  1451        cat >>"$file" <<EOF
  1452  CONTROLLER_MANAGER_TEST_ARGS: $(yaml-quote "${CONTROLLER_MANAGER_TEST_ARGS}")
  1453  EOF
  1454      fi
  1455      if [ -n "${KUBE_CONTROLLER_MANAGER_TEST_ARGS:-}" ]; then
  1456        cat >>"$file" <<EOF
  1457  KUBE_CONTROLLER_MANAGER_TEST_ARGS: $(yaml-quote "${KUBE_CONTROLLER_MANAGER_TEST_ARGS}")
  1458  EOF
  1459      fi
  1460      if [ -n "${CONTROLLER_MANAGER_TEST_LOG_LEVEL:-}" ]; then
  1461        cat >>"$file" <<EOF
  1462  CONTROLLER_MANAGER_TEST_LOG_LEVEL: $(yaml-quote "${CONTROLLER_MANAGER_TEST_LOG_LEVEL}")
  1463  EOF
  1464      fi
  1465      if [ -n "${SCHEDULER_TEST_ARGS:-}" ]; then
  1466        cat >>"$file" <<EOF
  1467  SCHEDULER_TEST_ARGS: $(yaml-quote "${SCHEDULER_TEST_ARGS}")
  1468  EOF
  1469      fi
  1470      if [ -n "${SCHEDULER_TEST_LOG_LEVEL:-}" ]; then
  1471        cat >>"$file" <<EOF
  1472  SCHEDULER_TEST_LOG_LEVEL: $(yaml-quote "${SCHEDULER_TEST_LOG_LEVEL}")
  1473  EOF
  1474      fi
  1475      if [ -n "${INITIAL_ETCD_CLUSTER:-}" ]; then
  1476        cat >>"$file" <<EOF
  1477  INITIAL_ETCD_CLUSTER: $(yaml-quote "${INITIAL_ETCD_CLUSTER}")
  1478  EOF
  1479      fi
  1480      if [ -n "${INITIAL_ETCD_CLUSTER_STATE:-}" ]; then
  1481        cat >>"$file" <<EOF
  1482  INITIAL_ETCD_CLUSTER_STATE: $(yaml-quote "${INITIAL_ETCD_CLUSTER_STATE}")
  1483  EOF
  1484      fi
  1485      if [ -n "${CLUSTER_SIGNING_DURATION:-}" ]; then
  1486        cat >>"$file" <<EOF
  1487  CLUSTER_SIGNING_DURATION: $(yaml-quote "${CLUSTER_SIGNING_DURATION}")
  1488  EOF
  1489      fi
  1490      if [[ "${NODE_ACCELERATORS:-}" == *"type=nvidia"* ]]; then
  1491        cat >>"$file" <<EOF
  1492  ENABLE_NVIDIA_GPU_DEVICE_PLUGIN: $(yaml-quote "true")
  1493  EOF
  1494      fi
  1495      if [ -n "${ADDON_MANAGER_LEADER_ELECTION:-}" ]; then
  1496        cat >>"$file" <<EOF
  1497  ADDON_MANAGER_LEADER_ELECTION: $(yaml-quote "${ADDON_MANAGER_LEADER_ELECTION}")
  1498  EOF
  1499      fi
  1500      if [ -n "${API_SERVER_TEST_LOG_LEVEL:-}" ]; then
  1501        cat >>"$file" <<EOF
  1502  API_SERVER_TEST_LOG_LEVEL: $(yaml-quote "${API_SERVER_TEST_LOG_LEVEL}")
  1503  EOF
  1504      fi
  1505      if [ -n "${ETCD_LISTEN_CLIENT_IP:-}" ]; then
  1506        cat >>"$file" <<EOF
  1507  ETCD_LISTEN_CLIENT_IP: $(yaml-quote "${ETCD_LISTEN_CLIENT_IP}")
  1508  EOF
  1509      fi
  1510      if [ -n "${ETCD_PROGRESS_NOTIFY_INTERVAL:-}" ]; then
  1511        cat >>"$file" <<EOF
  1512  ETCD_PROGRESS_NOTIFY_INTERVAL: $(yaml-quote "${ETCD_PROGRESS_NOTIFY_INTERVAL}")
  1513  EOF
  1514      fi
  1515  
  1516    else
  1517      # Node-only env vars.
  1518      cat >>"$file" <<EOF
  1519  KUBERNETES_MASTER: $(yaml-quote "false")
  1520  EXTRA_DOCKER_OPTS: $(yaml-quote "${EXTRA_DOCKER_OPTS:-}")
  1521  EOF
  1522      if [ -n "${KUBEPROXY_TEST_ARGS:-}" ]; then
  1523        cat >>"$file" <<EOF
  1524  KUBEPROXY_TEST_ARGS: $(yaml-quote "${KUBEPROXY_TEST_ARGS}")
  1525  EOF
  1526      fi
  1527      if [ -n "${KUBEPROXY_TEST_LOG_LEVEL:-}" ]; then
  1528        cat >>"$file" <<EOF
  1529  KUBEPROXY_TEST_LOG_LEVEL: $(yaml-quote "${KUBEPROXY_TEST_LOG_LEVEL}")
  1530  EOF
  1531      fi
  1532    fi
  1533    if [[ "${ENABLE_CLUSTER_AUTOSCALER}" == "true" ]]; then
  1534        cat >>"$file" <<EOF
  1535  ENABLE_CLUSTER_AUTOSCALER: $(yaml-quote "${ENABLE_CLUSTER_AUTOSCALER}")
  1536  AUTOSCALER_MIG_CONFIG: $(yaml-quote "${AUTOSCALER_MIG_CONFIG}")
  1537  AUTOSCALER_EXPANDER_CONFIG: $(yaml-quote "${AUTOSCALER_EXPANDER_CONFIG}")
  1538  EOF
  1539        if [[ "${master}" == "false" ]]; then
  1540            # TODO(kubernetes/autoscaler#718): AUTOSCALER_ENV_VARS is a hotfix for cluster autoscaler,
  1541            # which reads the kube-env to determine the shape of a node and was broken by #60020.
  1542            # This should be removed as soon as a more reliable source of information is available!
  1543            local node_labels
  1544            local node_taints
  1545            local autoscaler_env_vars
  1546            node_labels="$(build-linux-node-labels node)"
  1547            node_taints="${NODE_TAINTS:-}"
  1548            autoscaler_env_vars="node_labels=${node_labels};node_taints=${node_taints}"
  1549            cat >>"$file" <<EOF
  1550  AUTOSCALER_ENV_VARS: $(yaml-quote "${autoscaler_env_vars}")
  1551  EOF
  1552        fi
  1553    fi
  1554    if [ -n "${SCHEDULING_ALGORITHM_PROVIDER:-}" ]; then
  1555      cat >>"$file" <<EOF
  1556  SCHEDULING_ALGORITHM_PROVIDER: $(yaml-quote "${SCHEDULING_ALGORITHM_PROVIDER}")
  1557  EOF
  1558    fi
  1559    if [ -n "${MAX_PODS_PER_NODE:-}" ]; then
  1560      cat >>"$file" <<EOF
  1561  MAX_PODS_PER_NODE: $(yaml-quote "${MAX_PODS_PER_NODE}")
  1562  EOF
  1563    fi
  1564    if [[ "${PREPARE_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
  1565        cat >>"$file" <<EOF
  1566  PREPARE_KONNECTIVITY_SERVICE: $(yaml-quote "${PREPARE_KONNECTIVITY_SERVICE}")
  1567  EOF
  1568    fi
  1569    if [[ "${EGRESS_VIA_KONNECTIVITY:-false}" == "true" ]]; then
  1570        cat >>"$file" <<EOF
  1571  EGRESS_VIA_KONNECTIVITY: $(yaml-quote "${EGRESS_VIA_KONNECTIVITY}")
  1572  EOF
  1573    fi
  1574    if [[ "${RUN_KONNECTIVITY_PODS:-false}" == "true" ]]; then
  1575        cat >>"$file" <<EOF
  1576  RUN_KONNECTIVITY_PODS: $(yaml-quote "${RUN_KONNECTIVITY_PODS}")
  1577  EOF
  1578    fi
  1579    if [[ -n "${KONNECTIVITY_SERVICE_PROXY_PROTOCOL_MODE:-}" ]]; then
  1580        cat >>"$file" <<EOF
  1581  KONNECTIVITY_SERVICE_PROXY_PROTOCOL_MODE: $(yaml-quote "${KONNECTIVITY_SERVICE_PROXY_PROTOCOL_MODE}")
  1582  EOF
  1583    fi
  1584  }
  1585  
  1586  
  1587  function build-windows-kube-env {
  1588    local file="$1"
  1589    # For now the Windows kube-env is a superset of the Linux kube-env.
  1590    build-linux-kube-env false "$file"
  1591  
  1592    cat >>"$file" <<EOF
  1593  WINDOWS_NODE_INSTANCE_PREFIX: $(yaml-quote "${WINDOWS_NODE_INSTANCE_PREFIX}")
  1594  NODE_BINARY_TAR_URL: $(yaml-quote "${NODE_BINARY_TAR_URL}")
  1595  NODE_BINARY_TAR_HASH: $(yaml-quote "${NODE_BINARY_TAR_HASH}")
  1596  CSI_PROXY_STORAGE_PATH: $(yaml-quote "${CSI_PROXY_STORAGE_PATH}")
  1597  CSI_PROXY_VERSION: $(yaml-quote "${CSI_PROXY_VERSION}")
  1598  CSI_PROXY_FLAGS: $(yaml-quote "${CSI_PROXY_FLAGS}")
  1599  ENABLE_CSI_PROXY: $(yaml-quote "${ENABLE_CSI_PROXY}")
  1600  K8S_DIR: $(yaml-quote "${WINDOWS_K8S_DIR}")
  1601  NODE_DIR: $(yaml-quote "${WINDOWS_NODE_DIR}")
  1602  LOGS_DIR: $(yaml-quote "${WINDOWS_LOGS_DIR}")
  1603  CNI_DIR: $(yaml-quote "${WINDOWS_CNI_DIR}")
  1604  CNI_CONFIG_DIR: $(yaml-quote "${WINDOWS_CNI_CONFIG_DIR}")
  1605  WINDOWS_CNI_STORAGE_PATH: $(yaml-quote "${WINDOWS_CNI_STORAGE_PATH}")
  1606  WINDOWS_CNI_VERSION: $(yaml-quote "${WINDOWS_CNI_VERSION}")
  1607  WINDOWS_CONTAINER_RUNTIME: $(yaml-quote "${WINDOWS_CONTAINER_RUNTIME}")
  1608  WINDOWS_CONTAINER_RUNTIME_ENDPOINT: $(yaml-quote "${WINDOWS_CONTAINER_RUNTIME_ENDPOINT:-}")
  1609  MANIFESTS_DIR: $(yaml-quote "${WINDOWS_MANIFESTS_DIR}")
  1610  PKI_DIR: $(yaml-quote "${WINDOWS_PKI_DIR}")
  1611  CA_FILE_PATH: $(yaml-quote "${WINDOWS_CA_FILE}")
  1612  KUBELET_CONFIG_FILE: $(yaml-quote "${WINDOWS_KUBELET_CONFIG_FILE}")
  1613  KUBEPROXY_ARGS: $(yaml-quote "${KUBEPROXY_ARGS}")
  1614  KUBECONFIG_FILE: $(yaml-quote "${WINDOWS_KUBECONFIG_FILE}")
  1615  BOOTSTRAP_KUBECONFIG_FILE: $(yaml-quote "${WINDOWS_BOOTSTRAP_KUBECONFIG_FILE}")
  1616  KUBEPROXY_KUBECONFIG_FILE: $(yaml-quote "${WINDOWS_KUBEPROXY_KUBECONFIG_FILE}")
  1617  WINDOWS_INFRA_CONTAINER: $(yaml-quote "${WINDOWS_INFRA_CONTAINER}")
  1618  WINDOWS_ENABLE_PIGZ: $(yaml-quote "${WINDOWS_ENABLE_PIGZ}")
  1619  WINDOWS_ENABLE_HYPERV: $(yaml-quote "${WINDOWS_ENABLE_HYPERV}")
  1620  ENABLE_AUTH_PROVIDER_GCP: $(yaml-quote "${ENABLE_AUTH_PROVIDER_GCP}")
  1621  ENABLE_NODE_PROBLEM_DETECTOR: $(yaml-quote "${WINDOWS_ENABLE_NODE_PROBLEM_DETECTOR}")
  1622  NODE_PROBLEM_DETECTOR_VERSION: $(yaml-quote "${NODE_PROBLEM_DETECTOR_VERSION}")
  1623  NODE_PROBLEM_DETECTOR_TAR_HASH: $(yaml-quote "${NODE_PROBLEM_DETECTOR_TAR_HASH}")
  1624  NODE_PROBLEM_DETECTOR_RELEASE_PATH: $(yaml-quote "${NODE_PROBLEM_DETECTOR_RELEASE_PATH}")
  1625  NODE_PROBLEM_DETECTOR_CUSTOM_FLAGS: $(yaml-quote "${WINDOWS_NODE_PROBLEM_DETECTOR_CUSTOM_FLAGS}")
  1626  NODE_PROBLEM_DETECTOR_TOKEN: $(yaml-quote "${NODE_PROBLEM_DETECTOR_TOKEN:-}")
  1627  WINDOWS_NODEPROBLEMDETECTOR_KUBECONFIG_FILE: $(yaml-quote "${WINDOWS_NODEPROBLEMDETECTOR_KUBECONFIG_FILE}")
  1628  AUTH_PROVIDER_GCP_STORAGE_PATH: $(yaml-quote "${AUTH_PROVIDER_GCP_STORAGE_PATH}")
  1629  AUTH_PROVIDER_GCP_VERSION: $(yaml-quote "${AUTH_PROVIDER_GCP_VERSION}")
  1630  AUTH_PROVIDER_GCP_HASH_WINDOWS_AMD64: $(yaml-quote "${AUTH_PROVIDER_GCP_HASH_WINDOWS_AMD64}")
  1631  AUTH_PROVIDER_GCP_WINDOWS_BIN_DIR: $(yaml-quote "${AUTH_PROVIDER_GCP_WINDOWS_BIN_DIR}")
  1632  AUTH_PROVIDER_GCP_WINDOWS_CONF_FILE: $(yaml-quote "${AUTH_PROVIDER_GCP_WINDOWS_CONF_FILE}")
  1633  EOF
  1634  }
  1635  
  1636  function sha512sum-file() {
  1637    local shasum
  1638    if which sha512sum >/dev/null 2>&1; then
  1639      shasum=$(sha512sum "$1")
  1640    else
  1641      shasum=$(shasum -a512 "$1")
  1642    fi
  1643    echo "${shasum%%[[:blank:]]*}"
  1644  }
  1645  
  1646  # Create certificate pairs for the cluster.
  1647  # $1: The public IP for the master.
  1648  #
  1649  # These are used for static cert distribution (e.g. static clustering) at
  1650  # cluster creation time. This will be obsoleted once we implement dynamic
  1651  # clustering.
  1652  #
  1653  # The following certificate pairs are created:
  1654  #
  1655  #  - ca (the cluster's certificate authority)
  1656  #  - server
  1657  #  - kubelet
  1658  #  - kubecfg (for kubectl)
  1659  #
  1660  # TODO(roberthbailey): Replace easyrsa with a simple Go program to generate
  1661  # the certs that we need.
  1662  #
  1663  # Assumed vars
  1664  #   KUBE_TEMP
  1665  #   MASTER_NAME
  1666  #
  1667  # Vars set:
  1668  #   CERT_DIR
  1669  #   CA_CERT_BASE64
  1670  #   MASTER_CERT_BASE64
  1671  #   MASTER_KEY_BASE64
  1672  #   KUBELET_CERT_BASE64
  1673  #   KUBELET_KEY_BASE64
  1674  #   KUBECFG_CERT_BASE64
  1675  #   KUBECFG_KEY_BASE64
  1676  function create-certs {
  1677    local -r primary_cn="${1}"
  1678  
  1679    # Determine extra certificate names for master
  1680  
  1681    # Create service_ip by stripping the network mask part from
  1682    # SERVICE_CLUSTER_IP_RANGE and incrementing the host part with 1
  1683    service_ip=${SERVICE_CLUSTER_IP_RANGE%/*}
  1684    service_ip="${service_ip%.*}.$((${service_ip##*.} + 1))"
  1685    local sans=""
  1686    for extra in "$@"; do
  1687      if [[ -n "${extra}" ]]; then
  1688        sans="${sans}IP:${extra},"
  1689      fi
  1690    done
  1691    sans="${sans}IP:${service_ip},DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.${DNS_DOMAIN},DNS:${MASTER_NAME}"
  1692  
  1693    echo "Generating certs for alternate-names: ${sans}"
  1694  
  1695    setup-easyrsa
  1696    PRIMARY_CN="${primary_cn}" SANS="${sans}" generate-certs
  1697    AGGREGATOR_PRIMARY_CN="${primary_cn}" AGGREGATOR_SANS="${sans}" generate-aggregator-certs
  1698    KONNECTIVITY_SERVER_PRIMARY_CN="${primary_cn}" KONNECTIVITY_SERVER_SANS="${sans}" generate-konnectivity-server-certs
  1699    CLOUD_PVL_ADMISSION_PRIMARY_CN="${primary_cn}" CLOUD_PVL_ADMISSION_SANS="${sans}" generate-cloud-pvl-admission-certs
  1700  
  1701    # By default, linux wraps base64 output every 76 cols, so we use 'tr -d' to remove whitespaces.
  1702    # Note 'base64 -w0' doesn't work on Mac OS X, which has different flags.
  1703    CA_KEY_BASE64=$(base64 "${CERT_DIR}/pki/private/ca.key" | tr -d '\r\n')
  1704    CA_CERT_BASE64=$(base64 "${CERT_DIR}/pki/ca.crt" | tr -d '\r\n')
  1705    MASTER_CERT_BASE64=$(base64 "${CERT_DIR}/pki/issued/${MASTER_NAME}.crt" | tr -d '\r\n')
  1706    MASTER_KEY_BASE64=$(base64 "${CERT_DIR}/pki/private/${MASTER_NAME}.key" | tr -d '\r\n')
  1707    KUBELET_CERT_BASE64=$(base64 "${CERT_DIR}/pki/issued/kubelet.crt" | tr -d '\r\n')
  1708    KUBELET_KEY_BASE64=$(base64 "${CERT_DIR}/pki/private/kubelet.key" | tr -d '\r\n')
  1709    KUBECFG_CERT_BASE64=$(base64 "${CERT_DIR}/pki/issued/kubecfg.crt" | tr -d '\r\n')
  1710    KUBECFG_KEY_BASE64=$(base64 "${CERT_DIR}/pki/private/kubecfg.key" | tr -d '\r\n')
  1711    KUBEAPISERVER_CERT_BASE64=$(base64 "${CERT_DIR}/pki/issued/kube-apiserver.crt" | tr -d '\r\n')
  1712    KUBEAPISERVER_KEY_BASE64=$(base64 "${CERT_DIR}/pki/private/kube-apiserver.key" | tr -d '\r\n')
  1713  
  1714    # Setting up an addition directory (beyond pki) as it is the simplest way to
  1715    # ensure we get a different CA pair to sign the proxy-client certs and which
  1716    # we can send CA public key to the user-apiserver to validate communication.
  1717    AGGREGATOR_CA_KEY_BASE64=$(base64 "${AGGREGATOR_CERT_DIR}/pki/private/ca.key" | tr -d '\r\n')
  1718    REQUESTHEADER_CA_CERT_BASE64=$(base64 "${AGGREGATOR_CERT_DIR}/pki/ca.crt" | tr -d '\r\n')
  1719    PROXY_CLIENT_CERT_BASE64=$(base64 "${AGGREGATOR_CERT_DIR}/pki/issued/proxy-client.crt" | tr -d '\r\n')
  1720    PROXY_CLIENT_KEY_BASE64=$(base64 "${AGGREGATOR_CERT_DIR}/pki/private/proxy-client.key" | tr -d '\r\n')
  1721  
  1722    # Setting up the Kubernetes API Server Konnectivity Server auth.
  1723    # This includes certs for both API Server to Konnectivity Server and
  1724    # Konnectivity Agent to Konnectivity Server.
  1725    KONNECTIVITY_SERVER_CA_KEY_BASE64=$(base64 "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/ca.key" | tr -d '\r\n')
  1726    KONNECTIVITY_SERVER_CA_CERT_BASE64=$(base64 "${KONNECTIVITY_SERVER_CERT_DIR}/pki/ca.crt" | tr -d '\r\n')
  1727    KONNECTIVITY_SERVER_CERT_BASE64=$(base64 "${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/server.crt" | tr -d '\r\n')
  1728    KONNECTIVITY_SERVER_KEY_BASE64=$(base64 "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/server.key" | tr -d '\r\n')
  1729    KONNECTIVITY_SERVER_CLIENT_CERT_BASE64=$(base64 "${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/client.crt" | tr -d '\r\n')
  1730    KONNECTIVITY_SERVER_CLIENT_KEY_BASE64=$(base64 "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/client.key" | tr -d '\r\n')
  1731    KONNECTIVITY_AGENT_CA_KEY_BASE64=$(base64 "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/ca.key" | tr -d '\r\n')
  1732    KONNECTIVITY_AGENT_CA_CERT_BASE64=$(base64 "${KONNECTIVITY_AGENT_CERT_DIR}/pki/ca.crt" | tr -d '\r\n')
  1733    KONNECTIVITY_AGENT_CERT_BASE64=$(base64 "${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/server.crt" | tr -d '\r\n')
  1734    KONNECTIVITY_AGENT_KEY_BASE64=$(base64 "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/server.key" | tr -d '\r\n')
  1735    KONNECTIVITY_AGENT_CLIENT_CERT_BASE64=$(base64 "${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/client.crt" | tr -d '\r\n')
  1736    KONNECTIVITY_AGENT_CLIENT_KEY_BASE64=$(base64 "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/client.key" | tr -d '\r\n')
  1737  
  1738    CLOUD_PVL_ADMISSION_CA_KEY_BASE64=$(base64 "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/private/ca.key" | tr -d '\r\n')
  1739    CLOUD_PVL_ADMISSION_CA_CERT_BASE64=$(base64 "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/ca.crt" | tr -d '\r\n')
  1740    CLOUD_PVL_ADMISSION_CERT_BASE64=$(base64 "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/issued/server.crt" | tr -d '\r\n')
  1741    CLOUD_PVL_ADMISSION_KEY_BASE64=$(base64 "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/private/server.key" | tr -d '\r\n')
  1742  }
  1743  
  1744  # Set up easy-rsa directory structure.
  1745  #
  1746  # Assumed vars
  1747  #   KUBE_TEMP
  1748  #
  1749  # Vars set:
  1750  #   CERT_DIR
  1751  #   AGGREGATOR_CERT_DIR
  1752  function setup-easyrsa {
  1753    local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX")
  1754    # Note: This was heavily cribbed from make-ca-cert.sh
  1755    (set -x
  1756      cd "${KUBE_TEMP}"
  1757      curl -L -O --connect-timeout 20 --retry 6 --retry-delay 2 https://dl.k8s.io/easy-rsa/easy-rsa.tar.gz
  1758      tar xzf easy-rsa.tar.gz
  1759      mkdir easy-rsa-master/kubelet
  1760      cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/kubelet
  1761      mkdir easy-rsa-master/aggregator
  1762      cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/aggregator
  1763      mkdir easy-rsa-master/cloud-pvl-admission
  1764      cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/cloud-pvl-admission
  1765      mkdir easy-rsa-master/konnectivity-server
  1766      cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/konnectivity-server
  1767      mkdir easy-rsa-master/konnectivity-agent
  1768      cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/konnectivity-agent) &>"${cert_create_debug_output}" || true
  1769    CERT_DIR="${KUBE_TEMP}/easy-rsa-master/easyrsa3"
  1770    AGGREGATOR_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/aggregator"
  1771    CLOUD_PVL_ADMISSION_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/cloud-pvl-admission"
  1772    KONNECTIVITY_SERVER_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/konnectivity-server"
  1773    KONNECTIVITY_AGENT_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/konnectivity-agent"
  1774    if [ ! -x "${CERT_DIR}/easyrsa" ] || [ ! -x "${AGGREGATOR_CERT_DIR}/easyrsa" ]; then
  1775      # TODO(roberthbailey,porridge): add better error handling here,
  1776      # see https://github.com/kubernetes/kubernetes/issues/55229
  1777      cat "${cert_create_debug_output}" >&2
  1778      echo "=== Failed to setup easy-rsa: Aborting ===" >&2
  1779      exit 2
  1780    fi
  1781  }
  1782  
  1783  # Runs the easy RSA commands to generate certificate files.
  1784  # The generated files are IN ${CERT_DIR}
  1785  #
  1786  # Assumed vars (see shellcheck disable directives below)
  1787  #   KUBE_TEMP
  1788  #   MASTER_NAME
  1789  #   CERT_DIR
  1790  #   PRIMARY_CN: Primary canonical name
  1791  #   SANS: Subject alternate names
  1792  #
  1793  #
  1794  function generate-certs {
  1795    local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX")
  1796    # Note: This was heavily cribbed from make-ca-cert.sh
  1797    (set -x
  1798      cd "${CERT_DIR}"
  1799      ./easyrsa init-pki
  1800      # this puts the cert into pki/ca.crt and the key into pki/private/ca.key
  1801      # PRIMARY_CN (expected to be) defined by caller
  1802      # shellcheck disable=SC2153
  1803      ./easyrsa --batch "--req-cn=${PRIMARY_CN}@$(date +%s)" build-ca nopass
  1804      # SANS (expected to be) defined by caller
  1805      # shellcheck disable=SC2153
  1806      ./easyrsa --subject-alt-name="${SANS}" build-server-full "${MASTER_NAME}" nopass
  1807      ./easyrsa build-client-full kube-apiserver nopass
  1808  
  1809      kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl"
  1810  
  1811      # make the config for the signer
  1812      echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json"
  1813      # create the kubelet client cert with the correct groups
  1814      echo '{"CN":"kubelet","names":[{"O":"system:nodes"}],"hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare kubelet
  1815      mv "kubelet-key.pem" "pki/private/kubelet.key"
  1816      mv "kubelet.pem" "pki/issued/kubelet.crt"
  1817      rm -f "kubelet.csr"
  1818  
  1819      # Make a superuser client cert with subject "O=system:masters, CN=kubecfg"
  1820      ./easyrsa --dn-mode=org \
  1821        --req-cn=kubecfg --req-org=system:masters \
  1822        --req-c= --req-st= --req-city= --req-email= --req-ou= \
  1823        build-client-full kubecfg nopass) &>"${cert_create_debug_output}" || true
  1824    local output_file_missing=0
  1825    local output_file
  1826    for output_file in \
  1827      "${CERT_DIR}/pki/private/ca.key" \
  1828      "${CERT_DIR}/pki/ca.crt" \
  1829      "${CERT_DIR}/pki/issued/${MASTER_NAME}.crt" \
  1830      "${CERT_DIR}/pki/private/${MASTER_NAME}.key" \
  1831      "${CERT_DIR}/pki/issued/kubelet.crt" \
  1832      "${CERT_DIR}/pki/private/kubelet.key" \
  1833      "${CERT_DIR}/pki/issued/kubecfg.crt" \
  1834      "${CERT_DIR}/pki/private/kubecfg.key" \
  1835      "${CERT_DIR}/pki/issued/kube-apiserver.crt" \
  1836      "${CERT_DIR}/pki/private/kube-apiserver.key"
  1837    do
  1838      if [[ ! -s "${output_file}" ]]; then
  1839        echo "Expected file ${output_file} not created" >&2
  1840        output_file_missing=1
  1841      fi
  1842    done
  1843    if [ $output_file_missing -ne 0 ]; then
  1844      # TODO(roberthbailey,porridge): add better error handling here,
  1845      # see https://github.com/kubernetes/kubernetes/issues/55229
  1846      cat "${cert_create_debug_output}" >&2
  1847      echo "=== Failed to generate master certificates: Aborting ===" >&2
  1848      exit 2
  1849    fi
  1850  }
  1851  
  1852  # Runs the easy RSA commands to generate aggregator certificate files.
  1853  # The generated files are in ${AGGREGATOR_CERT_DIR}
  1854  #
  1855  # Assumed vars
  1856  #   KUBE_TEMP
  1857  #   AGGREGATOR_MASTER_NAME
  1858  #   AGGREGATOR_CERT_DIR
  1859  #   AGGREGATOR_PRIMARY_CN: Primary canonical name
  1860  #   AGGREGATOR_SANS: Subject alternate names
  1861  #
  1862  #
  1863  function generate-aggregator-certs {
  1864    local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX")
  1865    # Note: This was heavily cribbed from make-ca-cert.sh
  1866    (set -x
  1867      cd "${KUBE_TEMP}/easy-rsa-master/aggregator"
  1868      ./easyrsa init-pki
  1869      # this puts the cert into pki/ca.crt and the key into pki/private/ca.key
  1870      ./easyrsa --batch "--req-cn=${AGGREGATOR_PRIMARY_CN}@$(date +%s)" build-ca nopass
  1871      ./easyrsa --subject-alt-name="${AGGREGATOR_SANS}" build-server-full "${AGGREGATOR_MASTER_NAME}" nopass
  1872      ./easyrsa build-client-full aggregator-apiserver nopass
  1873  
  1874      kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl"
  1875  
  1876      # make the config for the signer
  1877      echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json"
  1878      # create the aggregator client cert with the correct groups
  1879      echo '{"CN":"aggregator","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare proxy-client
  1880      mv "proxy-client-key.pem" "pki/private/proxy-client.key"
  1881      mv "proxy-client.pem" "pki/issued/proxy-client.crt"
  1882      rm -f "proxy-client.csr"
  1883  
  1884      # Make a superuser client cert with subject "O=system:masters, CN=kubecfg"
  1885      ./easyrsa --dn-mode=org \
  1886        --req-cn=proxy-clientcfg --req-org=system:aggregator \
  1887        --req-c= --req-st= --req-city= --req-email= --req-ou= \
  1888        build-client-full proxy-clientcfg nopass) &>"${cert_create_debug_output}" || true
  1889    local output_file_missing=0
  1890    local output_file
  1891    for output_file in \
  1892      "${AGGREGATOR_CERT_DIR}/pki/private/ca.key" \
  1893      "${AGGREGATOR_CERT_DIR}/pki/ca.crt" \
  1894      "${AGGREGATOR_CERT_DIR}/pki/issued/proxy-client.crt" \
  1895      "${AGGREGATOR_CERT_DIR}/pki/private/proxy-client.key"
  1896    do
  1897      if [[ ! -s "${output_file}" ]]; then
  1898        echo "Expected file ${output_file} not created" >&2
  1899        output_file_missing=1
  1900      fi
  1901    done
  1902    if [ $output_file_missing -ne 0 ]; then
  1903      # TODO(roberthbailey,porridge): add better error handling here,
  1904      # see https://github.com/kubernetes/kubernetes/issues/55229
  1905      cat "${cert_create_debug_output}" >&2
  1906      echo "=== Failed to generate aggregator certificates: Aborting ===" >&2
  1907      exit 2
  1908    fi
  1909  }
  1910  
  1911  # Runs the easy RSA commands to generate server side certificate files
  1912  # for the konnectivity server. This includes both server side to both
  1913  # konnectivity-server and konnectivity-agent.
  1914  # The generated files are in ${KONNECTIVITY_SERVER_CERT_DIR} and
  1915  # ${KONNECTIVITY_AGENT_CERT_DIR}
  1916  #
  1917  # Assumed vars
  1918  #   KUBE_TEMP
  1919  #   KONNECTIVITY_SERVER_CERT_DIR
  1920  #   KONNECTIVITY_SERVER_PRIMARY_CN: Primary canonical name
  1921  #   KONNECTIVITY_SERVER_SANS: Subject alternate names
  1922  #
  1923  function generate-konnectivity-server-certs {
  1924    local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX")
  1925    # Note: This was heavily cribbed from make-ca-cert.sh
  1926    (set -x
  1927      # Make the client <-> konnectivity server side certificates.
  1928      cd "${KUBE_TEMP}/easy-rsa-master/konnectivity-server"
  1929      ./easyrsa init-pki
  1930      # this puts the cert into pki/ca.crt and the key into pki/private/ca.key
  1931      ./easyrsa --batch "--req-cn=${KONNECTIVITY_SERVER_PRIMARY_CN}@$(date +%s)" build-ca nopass
  1932      ./easyrsa --subject-alt-name="IP:127.0.0.1,${KONNECTIVITY_SERVER_SANS}" build-server-full server nopass
  1933      ./easyrsa build-client-full client nopass
  1934  
  1935      kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl"
  1936  
  1937      # make the config for the signer
  1938      echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json"
  1939      # create the konnectivity server cert with the correct groups
  1940      echo '{"CN":"konnectivity-server","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare konnectivity-server
  1941      rm -f "konnectivity-server.csr"
  1942  
  1943      # Make the agent <-> konnectivity server side certificates.
  1944      cd "${KUBE_TEMP}/easy-rsa-master/konnectivity-agent"
  1945      ./easyrsa init-pki
  1946      # this puts the cert into pki/ca.crt and the key into pki/private/ca.key
  1947      ./easyrsa --batch "--req-cn=${KONNECTIVITY_SERVER_PRIMARY_CN}@$(date +%s)" build-ca nopass
  1948      ./easyrsa --subject-alt-name="${KONNECTIVITY_SERVER_SANS}" build-server-full server nopass
  1949      ./easyrsa build-client-full client nopass
  1950  
  1951      kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl"
  1952  
  1953      # make the config for the signer
  1954      echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","agent auth"]}}}' > "ca-config.json"
  1955      # create the konnectivity server cert with the correct groups
  1956      echo '{"CN":"koonectivity-server","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare konnectivity-agent
  1957      rm -f "konnectivity-agent.csr"
  1958  
  1959      echo "completed main certificate section") &>"${cert_create_debug_output}" || true
  1960  
  1961    local output_file_missing=0
  1962    local output_file
  1963    for output_file in \
  1964      "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/ca.key" \
  1965      "${KONNECTIVITY_SERVER_CERT_DIR}/pki/ca.crt" \
  1966      "${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/server.crt" \
  1967      "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/server.key" \
  1968      "${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/client.crt" \
  1969      "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/client.key" \
  1970      "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/ca.key" \
  1971      "${KONNECTIVITY_AGENT_CERT_DIR}/pki/ca.crt" \
  1972      "${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/server.crt" \
  1973      "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/server.key" \
  1974      "${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/client.crt" \
  1975      "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/client.key"
  1976    do
  1977      if [[ ! -s "${output_file}" ]]; then
  1978        echo "Expected file ${output_file} not created" >&2
  1979        output_file_missing=1
  1980      fi
  1981    done
  1982    if (( output_file_missing )); then
  1983      # TODO(roberthbailey,porridge): add better error handling here,
  1984      # see https://github.com/kubernetes/kubernetes/issues/55229
  1985      cat "${cert_create_debug_output}" >&2
  1986      echo "=== Failed to generate konnectivity-server certificates: Aborting ===" >&2
  1987      exit 2
  1988    fi
  1989  }
  1990  
  1991  # Runs the easy RSA commands to generate server side certificate files
  1992  # for the cloud-pvl-admission webhook.
  1993  # The generated files are in ${CLOUD_PVL_ADMISSION_CERT_DIR}
  1994  #
  1995  # Assumed vars
  1996  #   KUBE_TEMP
  1997  #   CLOUD_PVL_ADMISSION_CERT_DIR
  1998  #   CLOUD_PVL_ADMISSION_PRIMARY_CN: Primary canonical name
  1999  #   CLOUD_PVL_ADMISSION_SANS: Subject alternate names
  2000  #
  2001  function generate-cloud-pvl-admission-certs {
  2002    local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX")
  2003    # Note: This was heavily cribbed from make-ca-cert.sh
  2004    (set -x
  2005      # Make the client <-> cloud-pvl-admission server side certificates.
  2006      cd "${KUBE_TEMP}/easy-rsa-master/cloud-pvl-admission"
  2007      ./easyrsa init-pki
  2008      # this puts the cert into pki/ca.crt and the key into pki/private/ca.key
  2009      ./easyrsa --batch "--req-cn=${CLOUD_PVL_ADMISSION_PRIMARY_CN}@$(date +%s)" build-ca nopass
  2010      ./easyrsa --subject-alt-name="IP:127.0.0.1,${CLOUD_PVL_ADMISSION_SANS}" build-server-full server nopass
  2011      ./easyrsa build-client-full client nopass
  2012  
  2013      kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl"
  2014  
  2015      # make the config for the signer
  2016      echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json"
  2017      # create the cloud-pvl-admission cert with the correct groups
  2018      echo '{"CN":"cloud-pvl-admission","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare cloud-pvl-admission
  2019      rm -f "cloud-pvl-admission.csr"
  2020  
  2021      # Make the cloud-pvl-admission server side certificates.
  2022      cd "${KUBE_TEMP}/easy-rsa-master/cloud-pvl-admission"
  2023      ./easyrsa init-pki
  2024      # this puts the cert into pki/ca.crt and the key into pki/private/ca.key
  2025      ./easyrsa --batch "--req-cn=${CLOUD_PVL_ADMISSION_PRIMARY_CN}@$(date +%s)" build-ca nopass
  2026      ./easyrsa --subject-alt-name="${CLOUD_PVL_ADMISSION_SANS}" build-server-full server nopass
  2027      ./easyrsa build-client-full client nopass
  2028  
  2029      kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl"
  2030  
  2031      # make the config for the signer
  2032      echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","agent auth"]}}}' > "ca-config.json"
  2033      # create the cloud-pvl-admission server cert with the correct groups
  2034      echo '{"CN":"cloud-pvl-admission","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare konnectivity-agent
  2035      rm -f "konnectivity-agent.csr"
  2036  
  2037      echo "completed main certificate section") &>"${cert_create_debug_output}" || true
  2038  
  2039    local output_file_missing=0
  2040    local output_file
  2041    for output_file in \
  2042      "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/private/ca.key" \
  2043      "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/ca.crt" \
  2044      "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/issued/server.crt" \
  2045      "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/private/server.key" \
  2046      "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/issued/client.crt" \
  2047      "${CLOUD_PVL_ADMISSION_CERT_DIR}/pki/private/client.key"
  2048    do
  2049      if [[ ! -s "${output_file}" ]]; then
  2050        echo "Expected file ${output_file} not created" >&2
  2051        output_file_missing=1
  2052      fi
  2053    done
  2054    if (( output_file_missing )); then
  2055      # TODO(roberthbailey,porridge): add better error handling here,
  2056      # see https://github.com/kubernetes/kubernetes/issues/55229
  2057      cat "${cert_create_debug_output}" >&2
  2058      echo "=== Failed to generate cloud-pvl-admission certificates: Aborting ===" >&2
  2059      exit 2
  2060    fi
  2061  }
  2062  
  2063  # Using provided master env, extracts value from provided key.
  2064  #
  2065  # Args:
  2066  # $1 master env (kube-env of master; result of calling get-master-env)
  2067  # $2 env key to use
  2068  function get-env-val() {
  2069    local match
  2070    match=$( (echo "${1}" | grep -E "^${2}:") || echo '')
  2071    if [[ -z "${match}" ]]; then
  2072      echo ""
  2073    fi
  2074    echo "${match}" | cut -d : -f 2 | cut -d \' -f 2
  2075  }
  2076  
  2077  # Load the master env by calling get-master-env, and extract important values
  2078  function parse-master-env() {
  2079    # Get required master env vars
  2080    local master_env
  2081    master_env=$(get-master-env)
  2082    KUBE_PROXY_TOKEN=$(get-env-val "${master_env}" "KUBE_PROXY_TOKEN")
  2083    NODE_PROBLEM_DETECTOR_TOKEN=$(get-env-val "${master_env}" "NODE_PROBLEM_DETECTOR_TOKEN")
  2084    CA_CERT_BASE64=$(get-env-val "${master_env}" "CA_CERT")
  2085    CA_KEY_BASE64=$(get-env-val "${master_env}" "CA_KEY")
  2086    KUBEAPISERVER_CERT_BASE64=$(get-env-val "${master_env}" "KUBEAPISERVER_CERT")
  2087    KUBEAPISERVER_KEY_BASE64=$(get-env-val "${master_env}" "KUBEAPISERVER_KEY")
  2088    EXTRA_DOCKER_OPTS=$(get-env-val "${master_env}" "EXTRA_DOCKER_OPTS")
  2089    KUBELET_CERT_BASE64=$(get-env-val "${master_env}" "KUBELET_CERT")
  2090    KUBELET_KEY_BASE64=$(get-env-val "${master_env}" "KUBELET_KEY")
  2091    MASTER_CERT_BASE64=$(get-env-val "${master_env}" "MASTER_CERT")
  2092    MASTER_KEY_BASE64=$(get-env-val "${master_env}" "MASTER_KEY")
  2093    AGGREGATOR_CA_KEY_BASE64=$(get-env-val "${master_env}" "AGGREGATOR_CA_KEY")
  2094    REQUESTHEADER_CA_CERT_BASE64=$(get-env-val "${master_env}" "REQUESTHEADER_CA_CERT")
  2095    PROXY_CLIENT_CERT_BASE64=$(get-env-val "${master_env}" "PROXY_CLIENT_CERT")
  2096    PROXY_CLIENT_KEY_BASE64=$(get-env-val "${master_env}" "PROXY_CLIENT_KEY")
  2097    ENABLE_LEGACY_ABAC=$(get-env-val "${master_env}" "ENABLE_LEGACY_ABAC")
  2098    ETCD_APISERVER_CA_KEY_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_CA_KEY")
  2099    ETCD_APISERVER_CA_CERT_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_CA_CERT")
  2100    ETCD_APISERVER_SERVER_KEY_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_SERVER_KEY")
  2101    ETCD_APISERVER_SERVER_CERT_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_SERVER_CERT")
  2102    ETCD_APISERVER_CLIENT_KEY_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_CLIENT_KEY")
  2103    ETCD_APISERVER_CLIENT_CERT_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_CLIENT_CERT")
  2104    CLOUD_PVL_ADMISSION_CA_KEY_BASE64=$(get-env-val "${master_env}" "CLOUD_PVL_ADMISSION_CA_KEY")
  2105    CLOUD_PVL_ADMISSION_CA_CERT_BASE64=$(get-env-val "${master_env}" "CLOUD_PVL_ADMISSION_CA_CERT")
  2106    CLOUD_PVL_ADMISSION_CERT_BASE64=$(get-env-val "${master_env}" "CLOUD_PVL_ADMISSION_CERT")
  2107    CLOUD_PVL_ADMISSION_KEY_BASE64=$(get-env-val "${master_env}" "CLOUD_PVL_ADMISSION_KEY")
  2108    KONNECTIVITY_SERVER_CA_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CA_KEY")
  2109    KONNECTIVITY_SERVER_CA_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CA_CERT")
  2110    KONNECTIVITY_SERVER_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CERT")
  2111    KONNECTIVITY_SERVER_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_KEY")
  2112    KONNECTIVITY_SERVER_CLIENT_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CLIENT_CERT")
  2113    KONNECTIVITY_SERVER_CLIENT_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CLIENT_KEY")
  2114    KONNECTIVITY_AGENT_CA_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_AGENT_CA_KEY")
  2115    KONNECTIVITY_AGENT_CA_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_AGENT_CA_CERT")
  2116    KONNECTIVITY_AGENT_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_AGENT_CERT")
  2117    KONNECTIVITY_AGENT_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_AGENT_KEY")
  2118  }
  2119  
  2120  # Update or verify required gcloud components are installed
  2121  # at minimum required version.
  2122  # Assumed vars
  2123  #   KUBE_PROMPT_FOR_UPDATE
  2124  function update-or-verify-gcloud() {
  2125    local sudo_prefix=""
  2126    if [ ! -w "$(dirname "$(which gcloud)")" ]; then
  2127      sudo_prefix="sudo"
  2128    fi
  2129    # update and install components as needed
  2130    # (deliberately word split $gcloud_prompt)
  2131    # shellcheck disable=SC2086
  2132    if [[ "${KUBE_PROMPT_FOR_UPDATE}" == "y" ]]; then
  2133      ${sudo_prefix} gcloud ${gcloud_prompt:-} components install alpha
  2134      ${sudo_prefix} gcloud ${gcloud_prompt:-} components install beta
  2135      ${sudo_prefix} gcloud ${gcloud_prompt:-} components update
  2136    else
  2137      local version
  2138      version=$(gcloud version --format=json)
  2139      python3 -c"
  2140  import json,sys
  2141  from distutils import version
  2142  
  2143  minVersion = version.LooseVersion('1.3.0')
  2144  required = [ 'alpha', 'beta', 'core' ]
  2145  data = json.loads(sys.argv[1])
  2146  rel = data.get('Google Cloud SDK')
  2147  if 'CL @' in rel:
  2148    print('Using dev version of gcloud: %s' %rel)
  2149    exit(0)
  2150  if rel != 'HEAD' and version.LooseVersion(rel) < minVersion:
  2151    print('gcloud version out of date ( < %s )' % minVersion)
  2152    exit(1)
  2153  missing = []
  2154  for c in required:
  2155    if not data.get(c):
  2156      missing += [c]
  2157  if missing:
  2158    for c in missing:
  2159      print ('missing required gcloud component \"{0}\"'.format(c))
  2160      print ('Try running \$(gcloud components install {0})'.format(c))
  2161    exit(1)
  2162      " "${version}"
  2163    fi
  2164  }
  2165  
  2166  # Robustly try to create a static ip.
  2167  # $1: The name of the ip to create
  2168  # $2: The name of the region to create the ip in.
  2169  function create-static-ip() {
  2170    detect-project
  2171    local attempt=0
  2172    local REGION="$2"
  2173    while true; do
  2174      if gcloud compute addresses create "$1" \
  2175        --project "${PROJECT}" \
  2176        --region "${REGION}" -q > /dev/null; then
  2177        # successful operation - wait until it's visible
  2178        start="$(date +%s)"
  2179        while true; do
  2180          now="$(date +%s)"
  2181          # Timeout set to 15 minutes
  2182          if [[ $((now - start)) -gt 900 ]]; then
  2183            echo "Timeout while waiting for master IP visibility"
  2184            exit 2
  2185          fi
  2186          if gcloud compute addresses describe "$1" --project "${PROJECT}" --region "${REGION}" >/dev/null 2>&1; then
  2187            break
  2188          fi
  2189          echo "Master IP not visible yet. Waiting..."
  2190          sleep 5
  2191        done
  2192        break
  2193      fi
  2194  
  2195      if gcloud compute addresses describe "$1" \
  2196        --project "${PROJECT}" \
  2197        --region "${REGION}" >/dev/null 2>&1; then
  2198        # it exists - postcondition satisfied
  2199        break
  2200      fi
  2201  
  2202      if (( attempt > 4 )); then
  2203        echo -e "${color_red}Failed to create static ip $1 ${color_norm}" >&2
  2204        exit 2
  2205      fi
  2206      attempt=$((attempt + 1))
  2207      echo -e "${color_yellow:-}Attempt $attempt failed to create static ip $1. Retrying.${color_norm:-}" >&2
  2208      sleep $((attempt * 5))
  2209    done
  2210  }
  2211  
  2212  # Robustly try to create a firewall rule.
  2213  # $1: The name of firewall rule.
  2214  # $2: IP ranges.
  2215  # $3: Target tags for this firewall rule.
  2216  function create-firewall-rule() {
  2217    detect-project
  2218    local attempt=0
  2219    while true; do
  2220      if ! gcloud compute firewall-rules create "$1" \
  2221        --project "${NETWORK_PROJECT}" \
  2222        --network "${NETWORK}" \
  2223        --source-ranges "$2" \
  2224        --target-tags "$3" \
  2225        --allow tcp,udp,icmp,esp,ah,sctp; then
  2226        if (( attempt > 4 )); then
  2227          echo -e "${color_red}Failed to create firewall rule $1 ${color_norm}" >&2
  2228          exit 2
  2229        fi
  2230        echo -e "${color_yellow}Attempt $((attempt + 1)) failed to create firewall rule $1. Retrying.${color_norm}" >&2
  2231        attempt=$((attempt + 1))
  2232        sleep $((attempt * 5))
  2233      else
  2234          break
  2235      fi
  2236    done
  2237  }
  2238  
  2239  # Format the string argument for gcloud network.
  2240  function make-gcloud-network-argument() {
  2241    local network_project="$1"
  2242    local region="$2"
  2243    local network="$3"
  2244    local subnet="$4"
  2245    local address="$5"          # optional
  2246    local enable_ip_alias="$6"  # optional
  2247    local alias_size="$7"       # optional
  2248  
  2249    local networkURL="projects/${network_project}/global/networks/${network}"
  2250    local subnetURL="projects/${network_project}/regions/${region}/subnetworks/${subnet:-}"
  2251  
  2252    local ret=""
  2253  
  2254    if [[ "${enable_ip_alias}" == 'true' ]]; then
  2255      ret="--network-interface"
  2256      ret="${ret} network=${networkURL}"
  2257      if [[ "${address:-}" == "no-address" ]]; then
  2258        ret="${ret},no-address"
  2259      else
  2260        ret="${ret},address=${address:-}"
  2261      fi
  2262      ret="${ret},subnet=${subnetURL}"
  2263      ret="${ret},aliases=pods-default:${alias_size}"
  2264      ret="${ret} --no-can-ip-forward"
  2265    else
  2266      if [[ -n ${subnet:-} ]]; then
  2267        ret="${ret} --subnet ${subnetURL}"
  2268      else
  2269        ret="${ret} --network ${networkURL}"
  2270      fi
  2271  
  2272      ret="${ret} --can-ip-forward"
  2273      if [[ -n ${address:-} ]] && [[ "$address" != "no-address" ]]; then
  2274        ret="${ret} --address ${address}"
  2275      fi
  2276    fi
  2277  
  2278    echo "${ret}"
  2279  }
  2280  
  2281  # $1: version (required)
  2282  # $2: Prefix for the template name, i.e. NODE_INSTANCE_PREFIX or
  2283  #     WINDOWS_NODE_INSTANCE_PREFIX.
  2284  function get-template-name-from-version() {
  2285    local -r version=${1}
  2286    local -r template_prefix=${2}
  2287    # trim template name to pass gce name validation
  2288    echo "${template_prefix}-template-${version}" | cut -c 1-63 | sed 's/[\.\+]/-/g;s/-*$//g'
  2289  }
  2290  
  2291  # validates the NODE_LOCAL_SSDS_EXT variable
  2292  function validate-node-local-ssds-ext(){
  2293    ssdopts="${1}"
  2294  
  2295    if [[ -z "${ssdopts[0]}" || -z "${ssdopts[1]}" || -z "${ssdopts[2]}" ]]; then
  2296      echo -e "${color_red}Local SSD: NODE_LOCAL_SSDS_EXT is malformed, found ${ssdopts[0]-_},${ssdopts[1]-_},${ssdopts[2]-_} ${color_norm}" >&2
  2297      exit 2
  2298    fi
  2299    if [[ "${ssdopts[1]}" != "scsi" && "${ssdopts[1]}" != "nvme" ]]; then
  2300      echo -e "${color_red}Local SSD: Interface must be scsi or nvme, found: ${ssdopts[1]} ${color_norm}" >&2
  2301      exit 2
  2302    fi
  2303    if [[ "${ssdopts[2]}" != "fs" && "${ssdopts[2]}" != "block" ]]; then
  2304      echo -e "${color_red}Local SSD: Filesystem type must be fs or block, found: ${ssdopts[2]} ${color_norm}"  >&2
  2305      exit 2
  2306    fi
  2307    local_ssd_ext_count=$((local_ssd_ext_count+ssdopts[0]))
  2308    if [[ "${local_ssd_ext_count}" -gt "${GCE_MAX_LOCAL_SSD}" || "${local_ssd_ext_count}" -lt 1 ]]; then
  2309      echo -e "${color_red}Local SSD: Total number of local ssds must range from 1 to 8, found: ${local_ssd_ext_count} ${color_norm}" >&2
  2310      exit 2
  2311    fi
  2312  }
  2313  
  2314  # Robustly try to create an instance template.
  2315  # $1: The name of the instance template.
  2316  # $2: The scopes flag.
  2317  # $3: String of comma-separated metadata-from-file entries.
  2318  # $4: String of comma-separated metadata (key=value) entries.
  2319  # $5: the node OS ("linux" or "windows").
  2320  function create-node-template() {
  2321    detect-project
  2322    detect-subnetworks
  2323    local template_name="$1"
  2324    local metadata_values="$4"
  2325    local os="$5"
  2326    local machine_type="$6"
  2327  
  2328    # First, ensure the template doesn't exist.
  2329    # TODO(zmerlynn): To make this really robust, we need to parse the output and
  2330    #                 add retries. Just relying on a non-zero exit code doesn't
  2331    #                 distinguish an ephemeral failed call from a "not-exists".
  2332    if gcloud compute instance-templates describe "${template_name}" --project "${PROJECT}" &>/dev/null; then
  2333      echo "Instance template ${1} already exists; deleting." >&2
  2334      if ! gcloud compute instance-templates delete "${template_name}" --project "${PROJECT}" --quiet &>/dev/null; then
  2335        echo -e "${color_yellow}Failed to delete existing instance template${color_norm}" >&2
  2336        exit 2
  2337      fi
  2338    fi
  2339  
  2340    local gcloud="gcloud"
  2341  
  2342    local accelerator_args=()
  2343    # VMs with Accelerators cannot be live migrated.
  2344    # More details here - https://cloud.google.com/compute/docs/gpus/add-gpus#create-new-gpu-instance
  2345    if [[ -n "${NODE_ACCELERATORS}" ]]; then
  2346      accelerator_args+=(--maintenance-policy TERMINATE --restart-on-failure --accelerator "${NODE_ACCELERATORS}")
  2347      gcloud="gcloud beta"
  2348    fi
  2349  
  2350    local preemptible_minions=()
  2351    if [[ "${PREEMPTIBLE_NODE}" == "true" ]]; then
  2352      preemptible_minions+=(--preemptible --maintenance-policy TERMINATE)
  2353    fi
  2354  
  2355    local local_ssds=()
  2356    local_ssd_ext_count=0
  2357    if [[ -n "${NODE_LOCAL_SSDS_EXT:-}" ]]; then
  2358      IFS=";" read -r -a ssdgroups <<< "${NODE_LOCAL_SSDS_EXT:-}"
  2359      for ssdgroup in "${ssdgroups[@]}"
  2360      do
  2361        IFS="," read -r -a ssdopts <<< "${ssdgroup}"
  2362        validate-node-local-ssds-ext "${ssdopts[@]}"
  2363        for ((i=1; i<=ssdopts[0]; i++)); do
  2364          local_ssds+=("--local-ssd=interface=${ssdopts[1]}")
  2365        done
  2366      done
  2367    fi
  2368  
  2369    if [[ -n ${NODE_LOCAL_SSDS+x} ]]; then
  2370      # The NODE_LOCAL_SSDS check below fixes issue #49171
  2371      for ((i=1; i<=NODE_LOCAL_SSDS; i++)); do
  2372        local_ssds+=('--local-ssd=interface=SCSI')
  2373      done
  2374    fi
  2375  
  2376    local address=""
  2377    if [[ ${GCE_PRIVATE_CLUSTER:-} == "true" ]]; then
  2378      address="no-address"
  2379    fi
  2380  
  2381    local network
  2382    network=$(make-gcloud-network-argument \
  2383      "${NETWORK_PROJECT}" \
  2384      "${REGION}" \
  2385      "${NETWORK}" \
  2386      "${SUBNETWORK:-}" \
  2387      "${address}" \
  2388      "${ENABLE_IP_ALIASES:-}" \
  2389      "${IP_ALIAS_SIZE:-}")
  2390  
  2391    local node_image_flags=()
  2392    if [[ "${os}" == 'linux' ]]; then
  2393        node_image_flags+=(--image-project "${NODE_IMAGE_PROJECT}" --image "${NODE_IMAGE}")
  2394    elif [[ "${os}" == 'windows' ]]; then
  2395        node_image_flags+=(--image-project "${WINDOWS_NODE_IMAGE_PROJECT}" --image "${WINDOWS_NODE_IMAGE}")
  2396    else
  2397        echo "Unknown OS ${os}" >&2
  2398        exit 1
  2399    fi
  2400  
  2401    local metadata_flag="${metadata_values:+--metadata ${metadata_values}}"
  2402  
  2403    local attempt=1
  2404    while true; do
  2405      echo "Attempt ${attempt} to create ${1}" >&2
  2406      # Deliberately word split ${network}, $2 and ${metadata_flag}
  2407      # shellcheck disable=SC2086
  2408      if ! ${gcloud} compute instance-templates create \
  2409        "${template_name}" \
  2410        --project "${PROJECT}" \
  2411        --machine-type "${machine_type}" \
  2412        --boot-disk-type "${NODE_DISK_TYPE}" \
  2413        --boot-disk-size "${NODE_DISK_SIZE}" \
  2414        "${node_image_flags[@]}" \
  2415        --service-account "${NODE_SERVICE_ACCOUNT}" \
  2416        --tags "${NODE_TAG}" \
  2417        "${accelerator_args[@]}" \
  2418        "${local_ssds[@]}" \
  2419        --region "${REGION}" \
  2420        ${network} \
  2421        "${preemptible_minions[@]}" \
  2422        $2 \
  2423        --metadata-from-file "$3" \
  2424        ${metadata_flag} >&2; then
  2425          if (( attempt > 5 )); then
  2426            echo -e "${color_red}Failed to create instance template ${template_name} ${color_norm}" >&2
  2427            exit 2
  2428          fi
  2429          echo -e "${color_yellow}Attempt ${attempt} failed to create instance template ${template_name}. Retrying.${color_norm}" >&2
  2430          attempt=$((attempt + 1))
  2431          sleep $((attempt * 5))
  2432  
  2433          # In case the previous attempt failed with something like a
  2434          # Backend Error and left the entry laying around, delete it
  2435          # before we try again.
  2436          gcloud compute instance-templates delete "${template_name}" --project "${PROJECT}" &>/dev/null || true
  2437      else
  2438          break
  2439      fi
  2440    done
  2441  }
  2442  
  2443  # Instantiate a kubernetes cluster
  2444  #
  2445  # Assumed vars
  2446  #   KUBE_ROOT
  2447  #   <Various vars set in config file>
  2448  function kube-up() {
  2449    kube::util::ensure-temp-dir
  2450    detect-project
  2451  
  2452    load-or-gen-kube-basicauth
  2453    load-or-gen-kube-bearertoken
  2454  
  2455    # Make sure we have the tar files staged on Google Storage
  2456    find-release-tars
  2457    upload-tars
  2458  
  2459    # ensure that environmental variables specifying number of migs to create
  2460    set_num_migs
  2461  
  2462    if [[ ${KUBE_USE_EXISTING_MASTER:-} == "true" ]]; then
  2463      detect-master
  2464      parse-master-env
  2465      create-subnetworks
  2466      detect-subnetworks
  2467      # Windows nodes take longer to boot and setup so create them first.
  2468      create-windows-nodes
  2469      create-linux-nodes
  2470    elif [[ ${KUBE_REPLICATE_EXISTING_MASTER:-} == "true" ]]; then
  2471      detect-master
  2472      if  [[ "${MASTER_OS_DISTRIBUTION}" != "gci" && "${MASTER_OS_DISTRIBUTION}" != "ubuntu" ]]; then
  2473        echo "Master replication supported only for gci and ubuntu"
  2474        return 1
  2475      fi
  2476      if [[ ${GCE_PRIVATE_CLUSTER:-} == "true" ]]; then
  2477        create-internal-loadbalancer
  2478      fi
  2479      create-loadbalancer
  2480      # If replication of master fails, we need to ensure that the replica is removed from etcd clusters.
  2481      if ! replicate-master; then
  2482        remove-replica-from-etcd 2379 true || true
  2483        remove-replica-from-etcd 4002 false || true
  2484      fi
  2485    else
  2486      check-existing
  2487      create-network
  2488      create-subnetworks
  2489      detect-subnetworks
  2490      create-cloud-nat-router
  2491      write-cluster-location
  2492      write-cluster-name
  2493      create-autoscaler-config
  2494      create-master
  2495      create-nodes-firewall
  2496      create-nodes-template
  2497      if [[ "${KUBE_CREATE_NODES}" == "true" ]]; then
  2498        # Windows nodes take longer to boot and setup so create them first.
  2499        create-windows-nodes
  2500        create-linux-nodes
  2501      fi
  2502      check-cluster
  2503    fi
  2504  }
  2505  
  2506  function check-existing() {
  2507    local running_in_terminal=false
  2508    # May be false if tty is not allocated (for example with ssh -T).
  2509    if [[ -t 1 ]]; then
  2510      running_in_terminal=true
  2511    fi
  2512  
  2513    if [[ ${running_in_terminal} == "true" || ${KUBE_UP_AUTOMATIC_CLEANUP} == "true" ]]; then
  2514      if ! check-resources; then
  2515        local run_kube_down="n"
  2516        echo "${KUBE_RESOURCE_FOUND} found." >&2
  2517        # Get user input only if running in terminal.
  2518        if [[ ${running_in_terminal} == "true" && ${KUBE_UP_AUTOMATIC_CLEANUP} == "false" ]]; then
  2519          read -r -p "Would you like to shut down the old cluster (call kube-down)? [y/N] " run_kube_down
  2520        fi
  2521        if [[ ${run_kube_down} == "y" || ${run_kube_down} == "Y" || ${KUBE_UP_AUTOMATIC_CLEANUP} == "true" ]]; then
  2522          echo "... calling kube-down" >&2
  2523          kube-down
  2524        fi
  2525      fi
  2526    fi
  2527  }
  2528  
  2529  function check-network-mode() {
  2530    local mode
  2531    mode=$(gcloud compute networks list --filter="name=('${NETWORK}')" --project "${NETWORK_PROJECT}" --format='value(x_gcloud_subnet_mode)' || true)
  2532    # The deprecated field uses lower case. Convert to upper case for consistency.
  2533    echo "$mode" | tr '[:lower:]' '[:upper:]'
  2534  }
  2535  
  2536  function create-network() {
  2537    if ! gcloud compute networks --project "${NETWORK_PROJECT}" describe "${NETWORK}" &>/dev/null; then
  2538      # The network needs to be created synchronously or we have a race. The
  2539      # firewalls can be added concurrent with instance creation.
  2540      local network_mode="auto"
  2541      if [[ "${CREATE_CUSTOM_NETWORK:-}" == "true" ]]; then
  2542        network_mode="custom"
  2543      fi
  2544      echo "Creating new ${network_mode} network: ${NETWORK}"
  2545      gcloud compute networks create --project "${NETWORK_PROJECT}" "${NETWORK}" --subnet-mode="${network_mode}"
  2546    else
  2547      PREEXISTING_NETWORK=true
  2548      PREEXISTING_NETWORK_MODE="$(check-network-mode)"
  2549      echo "Found existing network ${NETWORK} in ${PREEXISTING_NETWORK_MODE} mode."
  2550    fi
  2551  
  2552    if ! gcloud compute firewall-rules --project "${NETWORK_PROJECT}" describe "${CLUSTER_NAME}-default-internal-master" &>/dev/null; then
  2553      gcloud compute firewall-rules create "${CLUSTER_NAME}-default-internal-master" \
  2554        --project "${NETWORK_PROJECT}" \
  2555        --network "${NETWORK}" \
  2556        --source-ranges "10.0.0.0/8" \
  2557        --allow "tcp:1-2379,tcp:2382-65535,udp:1-65535,icmp" \
  2558        --target-tags "${MASTER_TAG}"&
  2559    fi
  2560  
  2561    if ! gcloud compute firewall-rules --project "${NETWORK_PROJECT}" describe "${CLUSTER_NAME}-default-internal-node" &>/dev/null; then
  2562      gcloud compute firewall-rules create "${CLUSTER_NAME}-default-internal-node" \
  2563        --project "${NETWORK_PROJECT}" \
  2564        --network "${NETWORK}" \
  2565        --source-ranges "10.0.0.0/8" \
  2566        --allow "tcp:1-65535,udp:1-65535,icmp" \
  2567        --target-tags "${NODE_TAG}"&
  2568    fi
  2569  
  2570    if ! gcloud compute firewall-rules describe --project "${NETWORK_PROJECT}" "${NETWORK}-default-ssh" &>/dev/null; then
  2571      gcloud compute firewall-rules create "${NETWORK}-default-ssh" \
  2572        --project "${NETWORK_PROJECT}" \
  2573        --network "${NETWORK}" \
  2574        --source-ranges "0.0.0.0/0" \
  2575        --allow "tcp:22" &
  2576    fi
  2577  
  2578    # Open up TCP 3389 to allow RDP connections.
  2579    if [[ ${NUM_WINDOWS_NODES} -gt 0 ]]; then
  2580      if ! gcloud compute firewall-rules describe --project "${NETWORK_PROJECT}" "${NETWORK}-default-rdp" &>/dev/null; then
  2581        gcloud compute firewall-rules create "${NETWORK}-default-rdp" \
  2582          --project "${NETWORK_PROJECT}" \
  2583          --network "${NETWORK}" \
  2584          --source-ranges "0.0.0.0/0" \
  2585          --allow "tcp:3389" &
  2586      fi
  2587    fi
  2588  
  2589    kube::util::wait-for-jobs || {
  2590      code=$?
  2591      echo -e "${color_red}Failed to create firewall rules.${color_norm}" >&2
  2592      exit $code
  2593    }
  2594  }
  2595  
  2596  function expand-default-subnetwork() {
  2597    gcloud compute networks update "${NETWORK}" \
  2598      --switch-to-custom-subnet-mode \
  2599      --project "${NETWORK_PROJECT}" \
  2600      --quiet || true
  2601    gcloud compute networks subnets expand-ip-range "${NETWORK}" \
  2602      --region="${REGION}" \
  2603      --project "${NETWORK_PROJECT}" \
  2604      --prefix-length=19 \
  2605      --quiet
  2606  }
  2607  
  2608  function create-subnetworks() {
  2609    case ${ENABLE_IP_ALIASES} in
  2610      true) echo "IP aliases are enabled. Creating subnetworks.";;
  2611      false)
  2612        echo "IP aliases are disabled."
  2613        if [[ "${ENABLE_BIG_CLUSTER_SUBNETS}" = "true" ]]; then
  2614          if [[  "${PREEXISTING_NETWORK}" != "true" ]]; then
  2615            expand-default-subnetwork
  2616          else
  2617            echo "${color_yellow}Using pre-existing network ${NETWORK}, subnets won't be expanded to /19!${color_norm}"
  2618          fi
  2619        elif [[ "${CREATE_CUSTOM_NETWORK:-}" == "true" && "${PREEXISTING_NETWORK}" != "true" ]]; then
  2620            gcloud compute networks subnets create "${SUBNETWORK}" --project "${NETWORK_PROJECT}" --region "${REGION}" --network "${NETWORK}" --range "${NODE_IP_RANGE}"
  2621        fi
  2622        return;;
  2623      *) echo "${color_red}Invalid argument to ENABLE_IP_ALIASES${color_norm}"
  2624         exit 1;;
  2625    esac
  2626  
  2627    # Look for the alias subnet, it must exist and have a secondary
  2628    # range configured.
  2629    local subnet
  2630    subnet=$(gcloud compute networks subnets describe \
  2631      --project "${NETWORK_PROJECT}" \
  2632      --region "${REGION}" \
  2633      "${IP_ALIAS_SUBNETWORK}" 2>/dev/null || true)
  2634    if [[ -z "${subnet}" ]]; then
  2635      echo "Creating subnet ${NETWORK}:${IP_ALIAS_SUBNETWORK}"
  2636      gcloud compute networks subnets create \
  2637        "${IP_ALIAS_SUBNETWORK}" \
  2638        --description "Automatically generated subnet for ${INSTANCE_PREFIX} cluster. This will be removed on cluster teardown." \
  2639        --project "${NETWORK_PROJECT}" \
  2640        --network "${NETWORK}" \
  2641        --region "${REGION}" \
  2642        --range "${NODE_IP_RANGE}" \
  2643        --secondary-range "pods-default=${CLUSTER_IP_RANGE}" \
  2644        --secondary-range "services-default=${SERVICE_CLUSTER_IP_RANGE}"
  2645      echo "Created subnetwork ${IP_ALIAS_SUBNETWORK}"
  2646    else
  2647      if ! echo "${subnet}" | grep --quiet secondaryIpRanges; then
  2648        echo "${color_red}Subnet ${IP_ALIAS_SUBNETWORK} does not have a secondary range${color_norm}"
  2649        exit 1
  2650      fi
  2651    fi
  2652  }
  2653  
  2654  # detect-subnetworks sets the SUBNETWORK var if not already set
  2655  # Assumed vars:
  2656  #   NETWORK
  2657  #   REGION
  2658  #   NETWORK_PROJECT
  2659  #
  2660  # Optional vars:
  2661  #   SUBNETWORK
  2662  #   IP_ALIAS_SUBNETWORK
  2663  function detect-subnetworks() {
  2664    if [[ -n ${SUBNETWORK:-} ]]; then
  2665      echo "Using subnet ${SUBNETWORK}"
  2666      return 0
  2667    fi
  2668  
  2669    if [[ -n ${IP_ALIAS_SUBNETWORK:-} ]]; then
  2670      SUBNETWORK=${IP_ALIAS_SUBNETWORK}
  2671      echo "Using IP Alias subnet ${SUBNETWORK}"
  2672      return 0
  2673    fi
  2674  
  2675    SUBNETWORK=$(gcloud compute networks subnets list \
  2676      --network="${NETWORK}" \
  2677      --regions="${REGION}" \
  2678      --project="${NETWORK_PROJECT}" \
  2679      --limit=1 \
  2680      --format='value(name)' 2>/dev/null)
  2681  
  2682    if [[ -n ${SUBNETWORK:-} ]]; then
  2683      echo "Found subnet for region ${REGION} in network ${NETWORK}: ${SUBNETWORK}"
  2684      return 0
  2685    fi
  2686  
  2687    echo "${color_red}Could not find subnetwork with region ${REGION}, network ${NETWORK}, and project ${NETWORK_PROJECT}"
  2688  }
  2689  
  2690  # Sets up Cloud NAT for the network.
  2691  # Assumed vars:
  2692  #   NETWORK_PROJECT
  2693  #   REGION
  2694  #   NETWORK
  2695  function create-cloud-nat-router() {
  2696    if [[ ${GCE_PRIVATE_CLUSTER:-} == "true" ]]; then
  2697      if gcloud compute routers describe "$NETWORK-nat-router" --project "$NETWORK_PROJECT" --region "$REGION" &>/dev/null; then
  2698        echo "Cloud nat already exists"
  2699        return 0
  2700      fi
  2701      gcloud compute routers create "$NETWORK-nat-router" \
  2702        --project "$NETWORK_PROJECT" \
  2703        --region "$REGION" \
  2704        --network "$NETWORK"
  2705      gcloud compute routers nats create "$NETWORK-nat-config" \
  2706        --project "$NETWORK_PROJECT" \
  2707        --router-region "$REGION" \
  2708        --router "$NETWORK-nat-router" \
  2709        --nat-primary-subnet-ip-ranges \
  2710        --auto-allocate-nat-external-ips \
  2711        ${GCE_PRIVATE_CLUSTER_PORTS_PER_VM:+--min-ports-per-vm ${GCE_PRIVATE_CLUSTER_PORTS_PER_VM}}
  2712    fi
  2713  }
  2714  
  2715  function delete-all-firewall-rules() {
  2716    local -a fws
  2717    kube::util::read-array fws < <(gcloud compute firewall-rules list --project "${NETWORK_PROJECT}" --filter="network=${NETWORK}" --format="value(name)")
  2718    if (( "${#fws[@]}" > 0 )); then
  2719      echo "Deleting firewall rules remaining in network ${NETWORK}: ${fws[*]}"
  2720      delete-firewall-rules "${fws[@]}"
  2721    else
  2722      echo "No firewall rules in network ${NETWORK}"
  2723    fi
  2724  }
  2725  
  2726  # Ignores firewall rule arguments that do not exist in NETWORK_PROJECT.
  2727  function delete-firewall-rules() {
  2728    for fw in "$@"; do
  2729      if [[ -n $(gcloud compute firewall-rules --project "${NETWORK_PROJECT}" describe "${fw}" --format='value(name)' 2>/dev/null || true) ]]; then
  2730        gcloud compute firewall-rules delete --project "${NETWORK_PROJECT}" --quiet "${fw}" &
  2731      fi
  2732    done
  2733    kube::util::wait-for-jobs || {
  2734      echo -e "${color_red}Failed to delete firewall rules.${color_norm}" >&2
  2735    }
  2736  }
  2737  
  2738  function delete-network() {
  2739    if [[ -n $(gcloud compute networks --project "${NETWORK_PROJECT}" describe "${NETWORK}" --format='value(name)' 2>/dev/null || true) ]]; then
  2740      if ! gcloud compute networks delete --project "${NETWORK_PROJECT}" --quiet "${NETWORK}"; then
  2741        echo "Failed to delete network '${NETWORK}'. Listing firewall-rules:"
  2742        gcloud compute firewall-rules --project "${NETWORK_PROJECT}" list --filter="network=${NETWORK}"
  2743        return 1
  2744      fi
  2745    fi
  2746  }
  2747  
  2748  function delete-cloud-nat-router() {
  2749    if [[ ${GCE_PRIVATE_CLUSTER:-} == "true" ]]; then
  2750      if [[ -n $(gcloud compute routers describe --project "${NETWORK_PROJECT}" --region "${REGION}" "${NETWORK}-nat-router" --format='value(name)' 2>/dev/null || true) ]]; then
  2751        echo "Deleting Cloud NAT router..."
  2752        gcloud compute routers delete --project "${NETWORK_PROJECT}" --region "${REGION}" --quiet "${NETWORK}-nat-router"
  2753      fi
  2754    fi
  2755  }
  2756  
  2757  function delete-subnetworks() {
  2758    # If running in custom mode network we need to delete subnets manually.
  2759    mode="$(check-network-mode)"
  2760    if [[ "${mode}" == "CUSTOM" ]]; then
  2761      if [[ "${ENABLE_BIG_CLUSTER_SUBNETS}" = "true" ]]; then
  2762        echo "Deleting default subnets..."
  2763        # This value should be kept in sync with number of regions.
  2764        local parallelism=9
  2765        gcloud compute networks subnets list --network="${NETWORK}" --project "${NETWORK_PROJECT}" --format='value(region.basename())' | \
  2766          xargs -I {} -P ${parallelism} gcloud --quiet compute networks subnets delete "${NETWORK}" --project "${NETWORK_PROJECT}" --region="{}" || true
  2767      elif [[ "${CREATE_CUSTOM_NETWORK:-}" == "true" ]]; then
  2768        echo "Deleting custom subnet..."
  2769        gcloud --quiet compute networks subnets delete "${SUBNETWORK}" --project "${NETWORK_PROJECT}" --region="${REGION}" || true
  2770      fi
  2771      return
  2772    fi
  2773  
  2774    # If we reached here, it means we're not using custom network.
  2775    # So the only thing we need to check is if IP-aliases was turned
  2776    # on and we created a subnet for it. If so, we should delete it.
  2777    if [[ ${ENABLE_IP_ALIASES:-} == "true" ]]; then
  2778      # Only delete the subnet if we created it (i.e it's not pre-existing).
  2779      if [[ -z "${KUBE_GCE_IP_ALIAS_SUBNETWORK:-}" ]]; then
  2780        echo "Removing auto-created subnet ${NETWORK}:${IP_ALIAS_SUBNETWORK}"
  2781        if [[ -n $(gcloud compute networks subnets describe \
  2782              --project "${NETWORK_PROJECT}" \
  2783              --region "${REGION}" \
  2784              "${IP_ALIAS_SUBNETWORK}" 2>/dev/null) ]]; then
  2785          gcloud --quiet compute networks subnets delete \
  2786            --project "${NETWORK_PROJECT}" \
  2787            --region "${REGION}" \
  2788            "${IP_ALIAS_SUBNETWORK}"
  2789        fi
  2790      fi
  2791    fi
  2792  }
  2793  
  2794  # Generates SSL certificates for etcd cluster peer to peer communication. Uses cfssl program.
  2795  #
  2796  # Assumed vars:
  2797  #   KUBE_TEMP: temporary directory
  2798  #
  2799  # Args:
  2800  #  $1: host name
  2801  #  $2: CA certificate
  2802  #  $3: CA key
  2803  #
  2804  # If CA cert/key is empty, the function will also generate certs for CA.
  2805  #
  2806  # Vars set:
  2807  #   ETCD_CA_KEY_BASE64
  2808  #   ETCD_CA_CERT_BASE64
  2809  #   ETCD_PEER_KEY_BASE64
  2810  #   ETCD_PEER_CERT_BASE64
  2811  #
  2812  function create-etcd-certs {
  2813    local host=${1}
  2814    local ca_cert=${2:-}
  2815    local ca_key=${3:-}
  2816  
  2817    GEN_ETCD_CA_CERT="${ca_cert}" GEN_ETCD_CA_KEY="${ca_key}" \
  2818      generate-etcd-cert "${KUBE_TEMP}/cfssl" "${host}" "peer" "peer"
  2819  
  2820    pushd "${KUBE_TEMP}/cfssl"
  2821    ETCD_CA_KEY_BASE64=$(base64 "ca-key.pem" | tr -d '\r\n')
  2822    ETCD_CA_CERT_BASE64=$(gzip -c "ca.pem" | base64 | tr -d '\r\n')
  2823    ETCD_PEER_KEY_BASE64=$(base64 "peer-key.pem" | tr -d '\r\n')
  2824    ETCD_PEER_CERT_BASE64=$(gzip -c "peer.pem" | base64 | tr -d '\r\n')
  2825    popd
  2826  }
  2827  
  2828  # Generates SSL certificates for etcd-client and kube-apiserver communication. Uses cfssl program.
  2829  #
  2830  # Assumed vars:
  2831  #   KUBE_TEMP: temporary directory
  2832  #
  2833  # Args:
  2834  #  $1: host server name
  2835  #  $2: host client name
  2836  #  $3: CA certificate
  2837  #  $4: CA key
  2838  #
  2839  # If CA cert/key is empty, the function will also generate certs for CA.
  2840  #
  2841  # Vars set:
  2842  #   ETCD_APISERVER_CA_KEY_BASE64
  2843  #   ETCD_APISERVER_CA_CERT_BASE64
  2844  #   ETCD_APISERVER_SERVER_KEY_BASE64
  2845  #   ETCD_APISERVER_SERVER_CERT_BASE64
  2846  #   ETCD_APISERVER_CLIENT_KEY_BASE64
  2847  #   ETCD_APISERVER_CLIENT_CERT_BASE64
  2848  #
  2849  function create-etcd-apiserver-certs {
  2850    local hostServer=${1}
  2851    local hostClient=${2}
  2852    local etcd_apiserver_ca_cert=${3:-}
  2853    local etcd_apiserver_ca_key=${4:-}
  2854  
  2855    GEN_ETCD_CA_CERT="${etcd_apiserver_ca_cert}" GEN_ETCD_CA_KEY="${etcd_apiserver_ca_key}" \
  2856      generate-etcd-cert "${KUBE_TEMP}/cfssl" "${hostServer}" "server" "etcd-apiserver-server"
  2857      generate-etcd-cert "${KUBE_TEMP}/cfssl" "${hostClient}" "client" "etcd-apiserver-client"
  2858  
  2859    pushd "${KUBE_TEMP}/cfssl"
  2860    ETCD_APISERVER_CA_KEY_BASE64=$(base64 "ca-key.pem" | tr -d '\r\n')
  2861    ETCD_APISERVER_CA_CERT_BASE64=$(gzip -c "ca.pem" | base64 | tr -d '\r\n')
  2862    ETCD_APISERVER_SERVER_KEY_BASE64=$(base64 "etcd-apiserver-server-key.pem" | tr -d '\r\n')
  2863    ETCD_APISERVER_SERVER_CERT_BASE64=$(gzip -c "etcd-apiserver-server.pem" | base64 | tr -d '\r\n')
  2864    ETCD_APISERVER_CLIENT_KEY_BASE64=$(base64 "etcd-apiserver-client-key.pem" | tr -d '\r\n')
  2865    ETCD_APISERVER_CLIENT_CERT_BASE64=$(gzip -c "etcd-apiserver-client.pem" | base64 | tr -d '\r\n')
  2866    popd
  2867  }
  2868  
  2869  
  2870  function create-master() {
  2871    echo "Starting master and configuring firewalls"
  2872    gcloud compute firewall-rules create "${MASTER_NAME}-https" \
  2873      --project "${NETWORK_PROJECT}" \
  2874      --network "${NETWORK}" \
  2875      --target-tags "${MASTER_TAG}" \
  2876      --allow tcp:443 &
  2877  
  2878    echo "Configuring firewall for apiserver konnectivity server"
  2879    if [[ "${PREPARE_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
  2880      gcloud compute firewall-rules create "${MASTER_NAME}-konnectivity-server" \
  2881        --project "${NETWORK_PROJECT}" \
  2882        --network "${NETWORK}" \
  2883        --target-tags "${MASTER_TAG}" \
  2884        --allow tcp:8132 &
  2885    fi
  2886  
  2887    # We have to make sure the disk is created before creating the master VM, so
  2888    # run this in the foreground.
  2889    gcloud compute disks create "${MASTER_NAME}-pd" \
  2890      --project "${PROJECT}" \
  2891      --zone "${ZONE}" \
  2892      --type "${MASTER_DISK_TYPE}" \
  2893      --size "${MASTER_DISK_SIZE}"
  2894  
  2895    # Create rule for accessing and securing etcd servers.
  2896    if ! gcloud compute firewall-rules --project "${NETWORK_PROJECT}" describe "${MASTER_NAME}-etcd" &>/dev/null; then
  2897      gcloud compute firewall-rules create "${MASTER_NAME}-etcd" \
  2898        --project "${NETWORK_PROJECT}" \
  2899        --network "${NETWORK}" \
  2900        --source-tags "${MASTER_TAG}" \
  2901        --allow "tcp:2380,tcp:2381" \
  2902        --target-tags "${MASTER_TAG}" &
  2903    fi
  2904  
  2905    # Generate a bearer token for this cluster. We push this separately
  2906    # from the other cluster variables so that the client (this
  2907    # computer) can forget it later. This should disappear with
  2908    # http://issue.k8s.io/3168
  2909    KUBE_PROXY_TOKEN=$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64 | tr -d "=+/" | dd bs=32 count=1 2>/dev/null)
  2910    if [[ "${ENABLE_NODE_PROBLEM_DETECTOR:-}" == "standalone" ]]; then
  2911      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)
  2912    fi
  2913  
  2914    # Reserve the master's IP so that it can later be transferred to another VM
  2915    # without disrupting the kubelets.
  2916    create-static-ip "${MASTER_NAME}-ip" "${REGION}"
  2917    MASTER_RESERVED_IP=$(gcloud compute addresses describe "${MASTER_NAME}-ip" \
  2918      --project "${PROJECT}" --region "${REGION}" -q --format='value(address)')
  2919  
  2920    if [[ "${REGISTER_MASTER_KUBELET:-}" == "true" ]]; then
  2921      KUBELET_APISERVER="${MASTER_RESERVED_IP}"
  2922    fi
  2923  
  2924    KUBERNETES_MASTER_NAME="${MASTER_RESERVED_IP}"
  2925    MASTER_ADVERTISE_ADDRESS="${MASTER_RESERVED_IP}"
  2926  
  2927    MASTER_INTERNAL_IP=""
  2928    if [[ ${GCE_PRIVATE_CLUSTER:-} == "true" ]]; then
  2929      gcloud compute addresses create "${MASTER_NAME}-internal-ip" --project "${PROJECT}" --region "$REGION" --subnet "$SUBNETWORK"
  2930      MASTER_INTERNAL_IP=$(gcloud compute addresses describe "${MASTER_NAME}-internal-ip" --project "${PROJECT}" --region "${REGION}" -q --format='value(address)')
  2931      echo "Master internal ip is: $MASTER_INTERNAL_IP"
  2932      KUBERNETES_MASTER_NAME="${MASTER_INTERNAL_IP}"
  2933      MASTER_ADVERTISE_ADDRESS="${MASTER_INTERNAL_IP}"
  2934    fi
  2935  
  2936    create-certs "${MASTER_RESERVED_IP}" "${MASTER_INTERNAL_IP}"
  2937    create-etcd-certs "${MASTER_NAME}"
  2938    create-etcd-apiserver-certs "etcd-${MASTER_NAME}" "${MASTER_NAME}"
  2939  
  2940    if [[ "$(get-num-nodes)" -ge "50" ]]; then
  2941      # We block on master creation for large clusters to avoid doing too much
  2942      # unnecessary work in case master start-up fails (like creation of nodes).
  2943      create-master-instance "${MASTER_RESERVED_IP}" "${MASTER_INTERNAL_IP}"
  2944    else
  2945      create-master-instance "${MASTER_RESERVED_IP}" "${MASTER_INTERNAL_IP}" &
  2946    fi
  2947  
  2948  }
  2949  
  2950  # Adds master replica to etcd cluster.
  2951  #
  2952  # Assumed vars:
  2953  #   REPLICA_NAME
  2954  #   PROJECT
  2955  #   EXISTING_MASTER_NAME
  2956  #   EXISTING_MASTER_ZONE
  2957  #
  2958  # $1: etcd client port
  2959  # $2: etcd internal port
  2960  # $3: whether etcd communication should use mtls
  2961  # returns the result of ssh command which adds replica
  2962  function add-replica-to-etcd() {
  2963    local -r client_port="${1}"
  2964    local -r internal_port="${2}"
  2965    local -r use_mtls="${3}"
  2966  
  2967    TLSARG=""
  2968    PROTO="http://"
  2969    if [[ "${use_mtls}" == "true" ]]; then
  2970      # Keep in sync with ETCD_APISERVER_CA_CERT_PATH, ETCD_APISERVER_CLIENT_CERT_PATH and ETCD_APISERVER_CLIENT_KEY_PATH in configure-helper.sh.
  2971      TLSARG="--cacert /etc/srv/kubernetes/pki/etcd-apiserver-ca.crt --cert /etc/srv/kubernetes/pki/etcd-apiserver-client.crt --key /etc/srv/kubernetes/pki/etcd-apiserver-client.key"
  2972      PROTO="https://"
  2973    fi
  2974    run-gcloud-command "${EXISTING_MASTER_NAME}" "${EXISTING_MASTER_ZONE}" "curl ${TLSARG} ${PROTO}127.0.0.1:${client_port}/v2/members -XPOST -H \"Content-Type: application/json\" -d '{\"peerURLs\":[\"https://${REPLICA_NAME}:${internal_port}\"]}' -s"
  2975    return $?
  2976  }
  2977  
  2978  # Sets EXISTING_MASTER_NAME and EXISTING_MASTER_ZONE variables.
  2979  #
  2980  # Assumed vars:
  2981  #   PROJECT
  2982  #
  2983  # NOTE: Must be in sync with get-replica-name-regexp
  2984  function set-existing-master() {
  2985    local existing_master
  2986    existing_master=$(gcloud compute instances list \
  2987      --project "${PROJECT}" \
  2988      --filter "name ~ '$(get-replica-name-regexp)'" \
  2989      --format "value(name,zone)" | head -n1)
  2990    EXISTING_MASTER_NAME="$(echo "${existing_master}" | cut -f1)"
  2991    EXISTING_MASTER_ZONE="$(echo "${existing_master}" | cut -f2)"
  2992  }
  2993  
  2994  function replicate-master() {
  2995    set-replica-name
  2996    set-existing-master
  2997  
  2998    echo "Experimental: replicating existing master ${EXISTING_MASTER_ZONE}/${EXISTING_MASTER_NAME} as ${ZONE}/${REPLICA_NAME}"
  2999  
  3000    # Before we do anything else, we should configure etcd to expect more replicas.
  3001    if ! add-replica-to-etcd 2379 2380 true; then
  3002      echo "Failed to add master replica to etcd cluster."
  3003      return 1
  3004    fi
  3005    if ! add-replica-to-etcd 4002 2381 false; then
  3006      echo "Failed to add master replica to etcd events cluster."
  3007      return 1
  3008    fi
  3009  
  3010    # We have to make sure the disk is created before creating the master VM, so
  3011    # run this in the foreground.
  3012    gcloud compute disks create "${REPLICA_NAME}-pd" \
  3013      --project "${PROJECT}" \
  3014      --zone "${ZONE}" \
  3015      --type "${MASTER_DISK_TYPE}" \
  3016      --size "${MASTER_DISK_SIZE}"
  3017  
  3018    local existing_master_replicas
  3019    existing_master_replicas="$(get-all-replica-names)"
  3020    replicate-master-instance "${EXISTING_MASTER_ZONE}" "${EXISTING_MASTER_NAME}" "${existing_master_replicas}"
  3021  
  3022    # Add new replica to the load balancer.
  3023    gcloud compute target-pools add-instances "${MASTER_NAME}" \
  3024      --project "${PROJECT}" \
  3025      --zone "${ZONE}" \
  3026      --instances "${REPLICA_NAME}"
  3027  
  3028    if [[ "${GCE_PRIVATE_CLUSTER:-}" == "true" ]]; then
  3029      add-to-internal-loadbalancer "${REPLICA_NAME}" "${ZONE}"
  3030    fi
  3031  }
  3032  
  3033  # Detaches old and ataches new external IP to a VM.
  3034  #
  3035  # Arguments:
  3036  #   $1 - VM name
  3037  #   $2 - VM zone
  3038  #   $3 - external static IP; if empty will use an ephemeral IP address.
  3039  function attach-external-ip() {
  3040    local NAME=${1}
  3041    local ZONE=${2}
  3042    local IP_ADDR=${3:-}
  3043    local ACCESS_CONFIG_NAME
  3044    ACCESS_CONFIG_NAME=$(gcloud compute instances describe "${NAME}" \
  3045      --project "${PROJECT}" --zone "${ZONE}" \
  3046      --format="value(networkInterfaces[0].accessConfigs[0].name)")
  3047    gcloud compute instances delete-access-config "${NAME}" \
  3048      --project "${PROJECT}" --zone "${ZONE}" \
  3049      --access-config-name "${ACCESS_CONFIG_NAME}"
  3050    if [[ -z "${IP_ADDR}" ]]; then
  3051      gcloud compute instances add-access-config "${NAME}" \
  3052        --project "${PROJECT}" --zone "${ZONE}" \
  3053        --access-config-name "${ACCESS_CONFIG_NAME}"
  3054    else
  3055      gcloud compute instances add-access-config "${NAME}" \
  3056        --project "${PROJECT}" --zone "${ZONE}" \
  3057        --access-config-name "${ACCESS_CONFIG_NAME}" \
  3058        --address "${IP_ADDR}"
  3059    fi
  3060  }
  3061  
  3062  # Creates load balancer in front of apiserver if it doesn't exists already. Assumes there's only one
  3063  # existing master replica.
  3064  #
  3065  # Assumes:
  3066  #   PROJECT
  3067  #   MASTER_NAME
  3068  #   ZONE
  3069  #   REGION
  3070  function create-loadbalancer() {
  3071    # Step 0: Return early if LB is already configured.
  3072    if gcloud compute forwarding-rules describe "${MASTER_NAME}" \
  3073      --project "${PROJECT}" --region "${REGION}" > /dev/null 2>&1; then
  3074      echo "Load balancer already exists"
  3075      return
  3076    fi
  3077  
  3078    local EXISTING_MASTER_NAME
  3079    local EXISTING_MASTER_ZONE
  3080    EXISTING_MASTER_NAME="$(get-all-replica-names)"
  3081    EXISTING_MASTER_ZONE=$(gcloud compute instances list "${EXISTING_MASTER_NAME}" \
  3082      --project "${PROJECT}" --format='value(zone)')
  3083  
  3084    echo "Creating load balancer in front of an already existing master in ${EXISTING_MASTER_ZONE}"
  3085  
  3086    # Step 1: Detach master IP address and attach ephemeral address to the existing master
  3087    attach-external-ip "${EXISTING_MASTER_NAME}" "${EXISTING_MASTER_ZONE}"
  3088  
  3089    # Step 2: Create target pool.
  3090    gcloud compute target-pools create "${MASTER_NAME}" --project "${PROJECT}" --region "${REGION}"
  3091    # TODO: We should also add master instances with suffixes
  3092    gcloud compute target-pools add-instances "${MASTER_NAME}" --instances "${EXISTING_MASTER_NAME}" --project "${PROJECT}" --zone "${EXISTING_MASTER_ZONE}"
  3093  
  3094    # Step 3: Create forwarding rule.
  3095    # TODO: This step can take up to 20 min. We need to speed this up...
  3096    gcloud compute forwarding-rules create "${MASTER_NAME}" \
  3097      --project "${PROJECT}" --region "${REGION}" \
  3098      --target-pool "${MASTER_NAME}" --address="${KUBE_MASTER_IP}" --ports=443
  3099  
  3100    echo -n "Waiting for the load balancer configuration to propagate..."
  3101    local counter=0
  3102    until curl -k -m1 "https://${KUBE_MASTER_IP}" &> /dev/null; do
  3103      counter=$((counter+1))
  3104      echo -n .
  3105      if [[ ${counter} -ge 1800 ]]; then
  3106        echo -e "${color_red}TIMEOUT${color_norm}" >&2
  3107        echo -e "${color_red}Load balancer failed to initialize within ${counter} seconds.${color_norm}" >&2
  3108        exit 2
  3109      fi
  3110    done
  3111    echo "DONE"
  3112  }
  3113  
  3114  
  3115  # attach-internal-master-ip attach internal ip to existing master.
  3116  #
  3117  # Assumes:
  3118  # * PROJECT
  3119  function attach-internal-master-ip() {
  3120    local name="${1}"
  3121    local zone="${2}"
  3122    local ip="${3}"
  3123  
  3124    local aliases
  3125    aliases=$(gcloud compute instances describe "${name}" --project "${PROJECT}" --zone "${zone}" --flatten='networkInterfaces[0].aliasIpRanges[]' --format='value[separator=':'](networkInterfaces[0].aliasIpRanges.subnetworkRangeName,networkInterfaces[0].aliasIpRanges.ipCidrRange)' | sed 's/^://' | paste -s -d';' -)
  3126    aliases="${aliases:+${aliases};}${ip}/32"
  3127    echo "Setting ${name}'s aliases to '${aliases}' (added ${ip})"
  3128    # Attach ${ip} to ${name}
  3129    gcloud compute instances network-interfaces update "${name}" --project "${PROJECT}" --zone "${zone}" --aliases="${aliases}"
  3130    gcloud compute instances add-metadata "${name}" --zone "${zone}" --metadata=kube-master-internal-ip="${ip}"
  3131    run-gcloud-command "${name}" "${zone}" 'sudo /bin/bash /home/kubernetes/bin/kube-master-internal-route.sh' || true
  3132    return $?
  3133  }
  3134  
  3135  
  3136  # detach-internal-master-ip detaches internal ip from existing master.
  3137  #
  3138  # Assumes:
  3139  # * PROJECT
  3140  function detach-internal-master-ip() {
  3141    local name="${1}"
  3142    local zone="${2}"
  3143    local ip="${3}"
  3144  
  3145    local aliases
  3146    aliases=$(gcloud compute instances describe "${name}" --project "${PROJECT}" --zone "${zone}" --flatten='networkInterfaces[0].aliasIpRanges[]' --format='value[separator=':'](networkInterfaces[0].aliasIpRanges.subnetworkRangeName,networkInterfaces[0].aliasIpRanges.ipCidrRange)' | sed 's/^://' | grep -v "${ip}" | paste -s -d';' -)
  3147    echo "Setting ${name}'s aliases to '${aliases}' (removed ${ip})"
  3148    # Detach ${MASTER_NAME}-internal-ip from ${name}
  3149    gcloud compute instances network-interfaces update "${name}" --project "${PROJECT}" --zone "${zone}" --aliases="${aliases}"
  3150    gcloud compute instances remove-metadata "${name}" --zone "${zone}" --keys=kube-master-internal-ip
  3151    # We want `ip route` to be run in the cloud and not this host
  3152    run-gcloud-command "${name}" "${zone}" "sudo ip route del to local ${ip}/32 dev \$(ip route | grep default | while read -r _ _ _ _ dev _; do echo \$dev; done)" || true
  3153    return $?
  3154  }
  3155  
  3156  # create-internal-loadbalancer creates an internal load balacer in front of existing master.
  3157  #
  3158  # Assumes:
  3159  # * MASTER_NAME
  3160  # * PROJECT
  3161  # * REGION
  3162  function create-internal-loadbalancer() {
  3163    if gcloud compute forwarding-rules describe "${MASTER_NAME}-internal" \
  3164      --project "${PROJECT}" --region "${REGION}" > /dev/null 2>&1; then
  3165      echo "Load balancer already exists"
  3166      return
  3167    fi
  3168  
  3169    local EXISTING_MASTER_NAME
  3170    local EXISTING_MASTER_ZONE
  3171    EXISTING_MASTER_NAME="$(get-all-replica-names)"
  3172    EXISTING_MASTER_ZONE=$(gcloud compute instances list "${EXISTING_MASTER_NAME}" \
  3173      --project "${PROJECT}" --format='value(zone)')
  3174  
  3175    echo "Detaching ${KUBE_MASTER_INTERNAL_IP} from ${EXISTING_MASTER_NAME}/${EXISTING_MASTER_ZONE}"
  3176    detach-internal-master-ip "${EXISTING_MASTER_NAME}" "${EXISTING_MASTER_ZONE}" "${KUBE_MASTER_INTERNAL_IP}"
  3177  
  3178    echo "Creating internal load balancer with IP: ${KUBE_MASTER_INTERNAL_IP}"
  3179    gcloud compute health-checks --project "${PROJECT}" create tcp "${MASTER_NAME}-hc" --port=443
  3180  
  3181    gcloud compute backend-services create "${MASTER_NAME}" \
  3182      --project "${PROJECT}" \
  3183      --region "${REGION}" \
  3184      --protocol tcp \
  3185      --region "${REGION}" \
  3186      --load-balancing-scheme internal \
  3187      --health-checks "${MASTER_NAME}-hc"
  3188  
  3189    gcloud compute forwarding-rules create "${MASTER_NAME}-internal" \
  3190      --project "${PROJECT}" \
  3191      --region "${REGION}" \
  3192      --load-balancing-scheme internal \
  3193      --network "${NETWORK}" \
  3194      --subnet "${SUBNETWORK}" \
  3195      --address "${KUBE_MASTER_INTERNAL_IP}" \
  3196      --ip-protocol TCP \
  3197      --ports 443 \
  3198      --backend-service "${MASTER_NAME}" \
  3199      --backend-service-region "${REGION}"
  3200  
  3201    echo "Adding ${EXISTING_MASTER_NAME}/${EXISTING_MASTER_ZONE} to the load balancer"
  3202    add-to-internal-loadbalancer "${EXISTING_MASTER_NAME}" "${EXISTING_MASTER_ZONE}"
  3203  }
  3204  
  3205  # add-to-internal-loadbalancer adds an instance to ILB.
  3206  # Assumes:
  3207  # * MASTER_NAME
  3208  # * PROJECT
  3209  # * REGION
  3210  function add-to-internal-loadbalancer() {
  3211    local name="${1}"
  3212    local zone="${2}"
  3213  
  3214    gcloud compute instance-groups unmanaged create "${name}" --project "${PROJECT}" --zone "${zone}"
  3215    gcloud compute instance-groups unmanaged add-instances "${name}" --project "${PROJECT}" --zone "${zone}" --instances "${name}"
  3216    gcloud compute backend-services add-backend "${MASTER_NAME}" \
  3217      --project "${PROJECT}" \
  3218      --region "${REGION}" \
  3219      --instance-group "${name}" \
  3220      --instance-group-zone "${zone}"
  3221  }
  3222  
  3223  # remove-from-internal-loadbalancer removes an instance from ILB.
  3224  # Assumes:
  3225  # * MASTER_NAME
  3226  # * PROJECT
  3227  # * REGION
  3228  function remove-from-internal-loadbalancer() {
  3229    local name="${1}"
  3230    local zone="${2}"
  3231  
  3232    if gcloud compute instance-groups unmanaged describe "${name}" --project "${PROJECT}" --zone "${zone}" &>/dev/null; then
  3233      gcloud compute backend-services remove-backend "${MASTER_NAME}" \
  3234            --project "${PROJECT}" \
  3235            --region "${REGION}" \
  3236            --instance-group "${name}" \
  3237            --instance-group-zone "${zone}"
  3238      gcloud compute instance-groups unmanaged delete "${name}" --project "${PROJECT}" --zone "${zone}" --quiet
  3239    fi
  3240  }
  3241  
  3242  function delete-internal-loadbalancer() {
  3243    if gcloud compute forwarding-rules describe "${MASTER_NAME}-internal" --project "${PROJECT}" --region "${REGION}" &>/dev/null; then
  3244      gcloud compute forwarding-rules delete "${MASTER_NAME}-internal" --project "${PROJECT}" --region "${REGION}" --quiet
  3245    fi
  3246  
  3247    if gcloud compute backend-services describe "${MASTER_NAME}" --project "${PROJECT}" --region "${REGION}" &>/dev/null; then
  3248      gcloud compute backend-services delete "${MASTER_NAME}" --project "${PROJECT}" --region "${REGION}" --quiet
  3249    fi
  3250    if gcloud compute health-checks describe "${MASTER_NAME}-gc" --project "${PROJECT}" &>/dev/null; then
  3251      gcloud compute health-checks delete "${MASTER_NAME}-gc" --project "${PROJECT}" --quiet
  3252    fi
  3253  }
  3254  
  3255  function create-nodes-firewall() {
  3256    # Create a single firewall rule for all minions.
  3257    create-firewall-rule "${NODE_TAG}-all" "${CLUSTER_IP_RANGE}" "${NODE_TAG}" &
  3258  
  3259    # Report logging choice (if any).
  3260    if [[ "${ENABLE_NODE_LOGGING-}" == "true" ]]; then
  3261      echo "+++ Logging using Fluentd to ${LOGGING_DESTINATION:-unknown}"
  3262    fi
  3263  
  3264    # Wait for last batch of jobs
  3265    kube::util::wait-for-jobs || {
  3266      code=$?
  3267      echo -e "${color_red}Failed to create firewall rule.${color_norm}" >&2
  3268      exit $code
  3269    }
  3270  }
  3271  
  3272  function get-scope-flags() {
  3273    local scope_flags=
  3274    if [[ -n "${NODE_SCOPES}" ]]; then
  3275      scope_flags="--scopes ${NODE_SCOPES}"
  3276    else
  3277      scope_flags="--no-scopes"
  3278    fi
  3279    echo "${scope_flags}"
  3280  }
  3281  
  3282  function create-nodes-template() {
  3283    echo "Creating nodes."
  3284  
  3285    local scope_flags
  3286    scope_flags=$(get-scope-flags)
  3287  
  3288    write-linux-node-env
  3289    write-windows-node-env
  3290  
  3291    # NOTE: these template names and their format must match
  3292    # create-[linux,windows]-nodes() as well as get-template()!
  3293    local linux_template_name="${NODE_INSTANCE_PREFIX}-template"
  3294    local windows_template_name="${WINDOWS_NODE_INSTANCE_PREFIX}-template"
  3295    create-linux-node-instance-template "$linux_template_name"
  3296    create-windows-node-instance-template "$windows_template_name" "${scope_flags[*]}"
  3297    if [[ -n "${ADDITIONAL_MACHINE_TYPE:-}" ]]; then
  3298      local linux_extra_template_name="${NODE_INSTANCE_PREFIX}-extra-template"
  3299      create-linux-node-instance-template "$linux_extra_template_name" "${ADDITIONAL_MACHINE_TYPE}"
  3300    fi
  3301  }
  3302  
  3303  # Assumes:
  3304  # - MAX_INSTANCES_PER_MIG
  3305  # - NUM_NODES
  3306  # - NUM_WINDOWS_NODES
  3307  # exports:
  3308  # - NUM_MIGS
  3309  # - NUM_WINDOWS_MIGS
  3310  function set_num_migs() {
  3311    local defaulted_max_instances_per_mig=${MAX_INSTANCES_PER_MIG:-1000}
  3312  
  3313    if [[ ${defaulted_max_instances_per_mig} -le "0" ]]; then
  3314      echo "MAX_INSTANCES_PER_MIG cannot be negative. Assuming default 1000"
  3315      defaulted_max_instances_per_mig=1000
  3316    fi
  3317    export NUM_MIGS=$(((NUM_NODES + defaulted_max_instances_per_mig - 1) / defaulted_max_instances_per_mig))
  3318    export NUM_WINDOWS_MIGS=$(((NUM_WINDOWS_NODES + defaulted_max_instances_per_mig - 1) / defaulted_max_instances_per_mig))
  3319  }
  3320  
  3321  # Assumes:
  3322  # - NUM_MIGS
  3323  # - NODE_INSTANCE_PREFIX
  3324  # - NUM_NODES
  3325  # - PROJECT
  3326  # - ZONE
  3327  function create-linux-nodes() {
  3328    local template_name="${NODE_INSTANCE_PREFIX}-template"
  3329    local extra_template_name="${NODE_INSTANCE_PREFIX}-extra-template"
  3330  
  3331    local nodes="${NUM_NODES}"
  3332    if [[ -n "${HEAPSTER_MACHINE_TYPE:-}" ]]; then
  3333      echo "Creating a special node for heapster with machine-type ${HEAPSTER_MACHINE_TYPE}"
  3334      create-heapster-node
  3335      nodes=$(( nodes - 1 ))
  3336    fi
  3337  
  3338    if [[ -n "${ADDITIONAL_MACHINE_TYPE:-}" && "${NUM_ADDITIONAL_NODES:-}" -gt 0 ]]; then
  3339      local num_additional="${NUM_ADDITIONAL_NODES}"
  3340      if [[ "${NUM_ADDITIONAL_NODES:-}" -gt "${nodes}" ]]; then
  3341        echo "Capping NUM_ADDITIONAL_NODES to ${nodes}"
  3342        num_additional="${nodes}"
  3343      fi
  3344      if [[ "${num_additional:-}" -gt 0 ]]; then
  3345        echo "Creating ${num_additional} special nodes with machine-type ${ADDITIONAL_MACHINE_TYPE}"
  3346        local extra_group_name="${NODE_INSTANCE_PREFIX}-extra"
  3347        gcloud compute instance-groups managed \
  3348            create "${extra_group_name}" \
  3349            --project "${PROJECT}" \
  3350            --zone "${ZONE}" \
  3351            --base-instance-name "${extra_group_name}" \
  3352            --size "${num_additional}" \
  3353            --template "${extra_template_name}" || true;
  3354        gcloud compute instance-groups managed wait-until --stable \
  3355            "${extra_group_name}" \
  3356            --zone "${ZONE}" \
  3357            --project "${PROJECT}" \
  3358            --timeout "${MIG_WAIT_UNTIL_STABLE_TIMEOUT}" || true
  3359        nodes=$(( nodes - num_additional ))
  3360      fi
  3361    fi
  3362  
  3363    local instances_left=${nodes}
  3364  
  3365    for ((i=1; i<=NUM_MIGS; i++)); do
  3366      local group_name="${NODE_INSTANCE_PREFIX}-group-$i"
  3367      if [[ $i -eq ${NUM_MIGS} ]]; then
  3368        # TODO: We don't add a suffix for the last group to keep backward compatibility when there's only one MIG.
  3369        # We should change it at some point, but note #18545 when changing this.
  3370        group_name="${NODE_INSTANCE_PREFIX}-group"
  3371      fi
  3372      # Spread the remaining number of nodes evenly
  3373      this_mig_size=$((instances_left / (NUM_MIGS - i + 1)))
  3374      instances_left=$((instances_left - this_mig_size))
  3375  
  3376      # Run instance-groups creation in parallel.
  3377      {
  3378        gcloud compute instance-groups managed \
  3379            create "${group_name}" \
  3380            --project "${PROJECT}" \
  3381            --zone "${ZONE}" \
  3382            --base-instance-name "${group_name}" \
  3383            --size "${this_mig_size}" \
  3384            --template "${template_name}" || true;
  3385        gcloud compute instance-groups managed wait-until --stable \
  3386            "${group_name}" \
  3387            --zone "${ZONE}" \
  3388            --project "${PROJECT}" \
  3389            --timeout "${MIG_WAIT_UNTIL_STABLE_TIMEOUT}" || true
  3390      } &
  3391    done
  3392    wait
  3393  }
  3394  
  3395  # Assumes:
  3396  # - NUM_WINDOWS_MIGS
  3397  # - WINDOWS_NODE_INSTANCE_PREFIX
  3398  # - NUM_WINDOWS_NODES
  3399  # - PROJECT
  3400  # - ZONE
  3401  function create-windows-nodes() {
  3402    local template_name="${WINDOWS_NODE_INSTANCE_PREFIX}-template"
  3403  
  3404    local -r nodes="${NUM_WINDOWS_NODES}"
  3405    local instances_left=${nodes}
  3406  
  3407    for ((i=1; i <= NUM_WINDOWS_MIGS; i++)); do
  3408      local group_name="${WINDOWS_NODE_INSTANCE_PREFIX}-group-$i"
  3409      if [[ $i -eq ${NUM_WINDOWS_MIGS} ]]; then
  3410        # TODO: We don't add a suffix for the last group to keep backward compatibility when there's only one MIG.
  3411        # We should change it at some point, but note #18545 when changing this.
  3412        group_name="${WINDOWS_NODE_INSTANCE_PREFIX}-group"
  3413      fi
  3414      # Spread the remaining number of nodes evenly
  3415      this_mig_size=$((instances_left / (NUM_WINDOWS_MIGS - i + 1)))
  3416      instances_left=$((instances_left - this_mig_size))
  3417  
  3418      gcloud compute instance-groups managed \
  3419          create "${group_name}" \
  3420          --project "${PROJECT}" \
  3421          --zone "${ZONE}" \
  3422          --base-instance-name "${group_name}" \
  3423          --size "${this_mig_size}" \
  3424          --template "${template_name}" || true;
  3425      gcloud compute instance-groups managed wait-until --stable \
  3426          "${group_name}" \
  3427          --zone "${ZONE}" \
  3428          --project "${PROJECT}" \
  3429          --timeout "${MIG_WAIT_UNTIL_STABLE_TIMEOUT}" || true;
  3430    done
  3431  }
  3432  
  3433  # Assumes:
  3434  # - NODE_INSTANCE_PREFIX
  3435  # - PROJECT
  3436  # - NETWORK_PROJECT
  3437  # - REGION
  3438  # - ZONE
  3439  # - HEAPSTER_MACHINE_TYPE
  3440  # - NODE_DISK_TYPE
  3441  # - NODE_DISK_SIZE
  3442  # - NODE_IMAGE_PROJECT
  3443  # - NODE_IMAGE
  3444  # - NODE_SERVICE_ACCOUNT
  3445  # - NODE_TAG
  3446  # - NETWORK
  3447  # - ENABLE_IP_ALIASES
  3448  # - SUBNETWORK
  3449  # - IP_ALIAS_SIZE
  3450  function create-heapster-node() {
  3451    local gcloud="gcloud"
  3452  
  3453    local network
  3454    network=$(make-gcloud-network-argument \
  3455        "${NETWORK_PROJECT}" \
  3456        "${REGION}" \
  3457        "${NETWORK}" \
  3458        "${SUBNETWORK:-}" \
  3459        "" \
  3460        "${ENABLE_IP_ALIASES:-}" \
  3461        "${IP_ALIAS_SIZE:-}")
  3462  
  3463    # Deliberately word split ${network} and $(get-scope-flags)
  3464    # shellcheck disable=SC2086 disable=SC2046
  3465    ${gcloud} compute instances \
  3466        create "${NODE_INSTANCE_PREFIX}-heapster" \
  3467        --project "${PROJECT}" \
  3468        --zone "${ZONE}" \
  3469        --machine-type="${HEAPSTER_MACHINE_TYPE}" \
  3470        --boot-disk-type "${NODE_DISK_TYPE}" \
  3471        --boot-disk-size "${NODE_DISK_SIZE}" \
  3472        --image-project="${NODE_IMAGE_PROJECT}" \
  3473        --image "${NODE_IMAGE}" \
  3474        --service-account "${NODE_SERVICE_ACCOUNT}" \
  3475        --tags "${NODE_TAG}" \
  3476        ${network} \
  3477        $(get-scope-flags) \
  3478        --metadata-from-file "$(get-node-instance-metadata-from-file "heapster-kube-env")"
  3479  }
  3480  
  3481  # Assumes:
  3482  # - NUM_MIGS
  3483  # - NODE_INSTANCE_PREFIX
  3484  # - PROJECT
  3485  # - ZONE
  3486  # - AUTOSCALER_MAX_NODES
  3487  # - AUTOSCALER_MIN_NODES
  3488  # Exports
  3489  # - AUTOSCALER_MIG_CONFIG
  3490  function create-cluster-autoscaler-mig-config() {
  3491  
  3492    # Each MIG must have at least one node, so the min number of nodes
  3493    # must be greater or equal to the number of migs.
  3494    if [[ ${AUTOSCALER_MIN_NODES} -lt 0 ]]; then
  3495      echo "AUTOSCALER_MIN_NODES must be greater or equal 0"
  3496      exit 2
  3497    fi
  3498  
  3499    # Each MIG must have at least one node, so the min number of nodes
  3500    # must be greater or equal to the number of migs.
  3501    if [[ ${AUTOSCALER_MAX_NODES} -lt ${NUM_MIGS} ]]; then
  3502      echo "AUTOSCALER_MAX_NODES must be greater or equal ${NUM_MIGS}"
  3503      exit 2
  3504    fi
  3505    if [[ ${NUM_WINDOWS_MIGS} -gt 0 ]]; then
  3506      # TODO(pjh): implement Windows support in this function.
  3507      echo "Not implemented yet: autoscaler config for Windows MIGs"
  3508      exit 2
  3509    fi
  3510  
  3511    # The code assumes that the migs were created with create-nodes
  3512    # function which tries to evenly spread nodes across the migs.
  3513    AUTOSCALER_MIG_CONFIG=""
  3514  
  3515    local left_min=${AUTOSCALER_MIN_NODES}
  3516    local left_max=${AUTOSCALER_MAX_NODES}
  3517  
  3518    for ((i=1; i <= NUM_MIGS; i++)); do
  3519      local group_name="${NODE_INSTANCE_PREFIX}-group-$i"
  3520      if [[ $i -eq ${NUM_MIGS} ]]; then
  3521        # TODO: We don't add a suffix for the last group to keep backward compatibility when there's only one MIG.
  3522        # We should change it at some point, but note #18545 when changing this.
  3523        group_name="${NODE_INSTANCE_PREFIX}-group"
  3524      fi
  3525  
  3526      this_mig_min=$((left_min/(NUM_MIGS-i+1)))
  3527      this_mig_max=$((left_max/(NUM_MIGS-i+1)))
  3528      left_min=$((left_min-this_mig_min))
  3529      left_max=$((left_max-this_mig_max))
  3530  
  3531      local mig_url="https://www.googleapis.com/compute/v1/projects/${PROJECT}/zones/${ZONE}/instanceGroups/${group_name}"
  3532      AUTOSCALER_MIG_CONFIG="${AUTOSCALER_MIG_CONFIG} --nodes=${this_mig_min}:${this_mig_max}:${mig_url}"
  3533    done
  3534  
  3535    AUTOSCALER_MIG_CONFIG="${AUTOSCALER_MIG_CONFIG} --scale-down-enabled=${AUTOSCALER_ENABLE_SCALE_DOWN}"
  3536  }
  3537  
  3538  # Assumes:
  3539  # - NUM_MIGS
  3540  # - NODE_INSTANCE_PREFIX
  3541  # - PROJECT
  3542  # - ZONE
  3543  # - ENABLE_CLUSTER_AUTOSCALER
  3544  # - AUTOSCALER_MAX_NODES
  3545  # - AUTOSCALER_MIN_NODES
  3546  function create-autoscaler-config() {
  3547    # Create autoscaler for nodes configuration if requested
  3548    if [[ "${ENABLE_CLUSTER_AUTOSCALER}" == "true" ]]; then
  3549      create-cluster-autoscaler-mig-config
  3550      echo "Using autoscaler config: ${AUTOSCALER_MIG_CONFIG} ${AUTOSCALER_EXPANDER_CONFIG}"
  3551    fi
  3552  }
  3553  
  3554  function check-cluster() {
  3555    detect-node-names
  3556    detect-master
  3557  
  3558    echo "Waiting up to ${KUBE_CLUSTER_INITIALIZATION_TIMEOUT} seconds for cluster initialization."
  3559    echo
  3560    echo "  This will continually check to see if the API for kubernetes is reachable."
  3561    echo "  This may time out if there was some uncaught error during start up."
  3562    echo
  3563  
  3564    # curl in mavericks is borked.
  3565    secure=""
  3566    if which sw_vers >& /dev/null; then
  3567      if [[ $(sw_vers | grep ProductVersion | awk '{print $2}') = "10.9."* ]]; then
  3568        secure="--insecure"
  3569      fi
  3570    fi
  3571  
  3572    local start_time
  3573    local curl_out
  3574    start_time=$(date +%s)
  3575    curl_out=$(mktemp)
  3576    kube::util::trap_add "rm -f ${curl_out}" EXIT
  3577    until curl -vsS --cacert "${CERT_DIR}/pki/ca.crt" \
  3578            -H "Authorization: Bearer ${KUBE_BEARER_TOKEN}" \
  3579            ${secure} \
  3580            --max-time 5 --fail \
  3581            "https://${KUBE_MASTER_IP}/api/v1/pods?limit=100" > "${curl_out}" 2>&1; do
  3582        local elapsed
  3583        elapsed=$(($(date +%s) - start_time))
  3584        if [[ ${elapsed} -gt ${KUBE_CLUSTER_INITIALIZATION_TIMEOUT} ]]; then
  3585            echo -e "${color_red}Cluster failed to initialize within ${KUBE_CLUSTER_INITIALIZATION_TIMEOUT} seconds.${color_norm}" >&2
  3586            echo "Last output from querying API server follows:" >&2
  3587            echo "-----------------------------------------------------" >&2
  3588            cat "${curl_out}" >&2
  3589            echo "-----------------------------------------------------" >&2
  3590            exit 2
  3591        fi
  3592        printf "."
  3593        sleep 2
  3594    done
  3595  
  3596    echo "Kubernetes cluster created."
  3597  
  3598    export KUBE_CERT="${CERT_DIR}/pki/issued/kubecfg.crt"
  3599    export KUBE_KEY="${CERT_DIR}/pki/private/kubecfg.key"
  3600    export CA_CERT="${CERT_DIR}/pki/ca.crt"
  3601    export CONTEXT="${PROJECT}_${INSTANCE_PREFIX}"
  3602    (
  3603     umask 077
  3604  
  3605     # Update the user's kubeconfig to include credentials for this apiserver.
  3606     create-kubeconfig
  3607    )
  3608  
  3609    # ensures KUBECONFIG is set
  3610    get-kubeconfig-basicauth
  3611  
  3612    if [[ ${GCE_UPLOAD_KUBCONFIG_TO_MASTER_METADATA:-} == "true" ]]; then
  3613      gcloud compute instances add-metadata "${MASTER_NAME}" --project="${PROJECT}" --zone="${ZONE}"  --metadata-from-file="kubeconfig=${KUBECONFIG}" || true
  3614    fi
  3615  
  3616    echo
  3617    echo -e "${color_green:-}Kubernetes cluster is running.  The master is running at:"
  3618    echo
  3619    echo -e "${color_yellow}  https://${KUBE_MASTER_IP}"
  3620    echo
  3621    echo -e "${color_green}The user name and password to use is located in ${KUBECONFIG}.${color_norm}"
  3622    echo
  3623  
  3624  }
  3625  
  3626  # Removes master replica from etcd cluster.
  3627  #
  3628  # Assumed vars:
  3629  #   REPLICA_NAME
  3630  #   PROJECT
  3631  #   EXISTING_MASTER_NAME
  3632  #   EXISTING_MASTER_ZONE
  3633  #
  3634  # $1: etcd client port
  3635  # $2: whether etcd communication should use mtls
  3636  # returns the result of ssh command which removes replica
  3637  function remove-replica-from-etcd() {
  3638    local -r port="${1}"
  3639    local -r use_mtls="${2}"
  3640  
  3641    TLSARG=""
  3642    PROTO="http://"
  3643    if [[ "${use_mtls}" == "true" ]]; then
  3644      # Keep in sync with ETCD_APISERVER_CA_CERT_PATH, ETCD_APISERVER_CLIENT_CERT_PATH and ETCD_APISERVER_CLIENT_KEY_PATH in configure-helper.sh.
  3645      TLSARG="--cacert /etc/srv/kubernetes/pki/etcd-apiserver-ca.crt --cert /etc/srv/kubernetes/pki/etcd-apiserver-client.crt --key /etc/srv/kubernetes/pki/etcd-apiserver-client.key"
  3646      PROTO="https://"
  3647    fi
  3648    [[ -n "${EXISTING_MASTER_NAME}" ]] || return
  3649    run-gcloud-command "${EXISTING_MASTER_NAME}" "${EXISTING_MASTER_ZONE}" "curl -s ${TLSARG} ${PROTO}127.0.0.1:${port}/v2/members/\$(curl -s ${TLSARG} ${PROTO}127.0.0.1:${port}/v2/members -XGET | sed 's/{\\\"id/\n/g' | grep ${REPLICA_NAME}\\\" | cut -f 3 -d \\\") -XDELETE -L 2>/dev/null"
  3650    local -r res=$?
  3651    echo "Removing etcd replica, name: ${REPLICA_NAME}, port: ${port}, result: ${res}"
  3652    return "${res}"
  3653  }
  3654  
  3655  # Delete a kubernetes cluster. This is called from test-teardown.
  3656  #
  3657  # Assumed vars:
  3658  #   MASTER_NAME
  3659  #   NODE_INSTANCE_PREFIX
  3660  #   WINDOWS_NODE_INSTANCE_PREFIX
  3661  #   ZONE
  3662  # This function tears down cluster resources 10 at a time to avoid issuing too many
  3663  # API calls and exceeding API quota. It is important to bring down the instances before bringing
  3664  # down the firewall rules and routes.
  3665  function kube-down() {
  3666    local -r batch=200
  3667  
  3668    detect-project
  3669    detect-node-names # For INSTANCE_GROUPS and WINDOWS_INSTANCE_GROUPS
  3670  
  3671    echo "Bringing down cluster"
  3672    set +e  # Do not stop on error
  3673  
  3674    if [[ "${KUBE_DELETE_NODES:-}" != "false" ]]; then
  3675      # Get the name of the managed instance group template before we delete the
  3676      # managed instance group. (The name of the managed instance group template may
  3677      # change during a cluster upgrade.)
  3678      local templates
  3679      templates=$(get-template "${PROJECT}")
  3680  
  3681      # Deliberately allow globbing, do not change unless a bug is found
  3682      # shellcheck disable=SC2206
  3683      local all_instance_groups=(${INSTANCE_GROUPS[@]:-} ${WINDOWS_INSTANCE_GROUPS[@]:-})
  3684      # Deliberately do not quote, do not change unless a bug is found
  3685      # shellcheck disable=SC2068
  3686      for group in ${all_instance_groups[@]:-}; do
  3687        {
  3688          if gcloud compute instance-groups managed describe "${group}" --project "${PROJECT}" --zone "${ZONE}" &>/dev/null; then
  3689            gcloud compute instance-groups managed delete \
  3690              --project "${PROJECT}" \
  3691              --quiet \
  3692              --zone "${ZONE}" \
  3693              "${group}"
  3694          fi
  3695        } &
  3696      done
  3697  
  3698      # Wait for last batch of jobs
  3699      kube::util::wait-for-jobs || {
  3700        echo -e "Failed to delete instance group(s)." >&2
  3701      }
  3702  
  3703      # Deliberately do not quote, do not change unless a bug is found
  3704      # shellcheck disable=SC2068
  3705      for template in ${templates[@]:-}; do
  3706        {
  3707          if gcloud compute instance-templates describe --project "${PROJECT}" "${template}" &>/dev/null; then
  3708            gcloud compute instance-templates delete \
  3709              --project "${PROJECT}" \
  3710              --quiet \
  3711              "${template}"
  3712          fi
  3713        } &
  3714      done
  3715  
  3716      # Wait for last batch of jobs
  3717      kube::util::wait-for-jobs || {
  3718        echo -e "Failed to delete instance template(s)." >&2
  3719      }
  3720  
  3721      # Delete the special heapster node (if it exists).
  3722      if [[ -n "${HEAPSTER_MACHINE_TYPE:-}" ]]; then
  3723        local -r heapster_machine_name="${NODE_INSTANCE_PREFIX}-heapster"
  3724        if gcloud compute instances describe "${heapster_machine_name}" --zone "${ZONE}" --project "${PROJECT}" &>/dev/null; then
  3725          # Now we can safely delete the VM.
  3726          gcloud compute instances delete \
  3727            --project "${PROJECT}" \
  3728            --quiet \
  3729            --delete-disks all \
  3730            --zone "${ZONE}" \
  3731            "${heapster_machine_name}"
  3732        fi
  3733      fi
  3734    fi
  3735  
  3736    local -r REPLICA_NAME="${KUBE_REPLICA_NAME:-$(get-replica-name)}"
  3737  
  3738    set-existing-master
  3739  
  3740    # Un-register the master replica from etcd and events etcd.
  3741    remove-replica-from-etcd 2379 true
  3742    remove-replica-from-etcd 4002 false
  3743  
  3744    # Delete the master replica (if it exists).
  3745    if gcloud compute instances describe "${REPLICA_NAME}" --zone "${ZONE}" --project "${PROJECT}" &>/dev/null; then
  3746      # If there is a load balancer in front of apiservers we need to first update its configuration.
  3747      if gcloud compute target-pools describe "${MASTER_NAME}" --region "${REGION}" --project "${PROJECT}" &>/dev/null; then
  3748        gcloud compute target-pools remove-instances "${MASTER_NAME}" \
  3749          --project "${PROJECT}" \
  3750          --zone "${ZONE}" \
  3751          --instances "${REPLICA_NAME}"
  3752      fi
  3753      # Detach replica from LB if needed.
  3754      if [[ ${GCE_PRIVATE_CLUSTER:-} == "true" ]]; then
  3755        remove-from-internal-loadbalancer "${REPLICA_NAME}" "${ZONE}"
  3756      fi
  3757      # Now we can safely delete the VM.
  3758      gcloud compute instances delete \
  3759        --project "${PROJECT}" \
  3760        --quiet \
  3761        --delete-disks all \
  3762        --zone "${ZONE}" \
  3763        "${REPLICA_NAME}"
  3764    fi
  3765  
  3766    # Delete the master replica pd (possibly leaked by kube-up if master create failed).
  3767    # TODO(jszczepkowski): remove also possibly leaked replicas' pds
  3768    local -r replica_pd="${REPLICA_NAME:-${MASTER_NAME}}-pd"
  3769    if gcloud compute disks describe "${replica_pd}" --zone "${ZONE}" --project "${PROJECT}" &>/dev/null; then
  3770      gcloud compute disks delete \
  3771        --project "${PROJECT}" \
  3772        --quiet \
  3773        --zone "${ZONE}" \
  3774        "${replica_pd}"
  3775    fi
  3776  
  3777    # Check if this are any remaining master replicas.
  3778    local REMAINING_MASTER_COUNT
  3779    REMAINING_MASTER_COUNT=$(gcloud compute instances list \
  3780      --project "${PROJECT}" \
  3781      --filter="name ~ '$(get-replica-name-regexp)'" \
  3782      --format "value(zone)" | wc -l)
  3783  
  3784    # In the replicated scenario, if there's only a single master left, we should also delete load balancer in front of it.
  3785    if [[ "${REMAINING_MASTER_COUNT}" -eq 1 ]]; then
  3786      detect-master
  3787      local REMAINING_REPLICA_NAME
  3788      local REMAINING_REPLICA_ZONE
  3789      REMAINING_REPLICA_NAME="$(get-all-replica-names)"
  3790      REMAINING_REPLICA_ZONE=$(gcloud compute instances list "${REMAINING_REPLICA_NAME}" \
  3791        --project "${PROJECT}" --format='value(zone)')
  3792      if gcloud compute forwarding-rules describe "${MASTER_NAME}" --region "${REGION}" --project "${PROJECT}" &>/dev/null; then
  3793        gcloud compute forwarding-rules delete \
  3794          --project "${PROJECT}" \
  3795          --region "${REGION}" \
  3796          --quiet \
  3797          "${MASTER_NAME}"
  3798        attach-external-ip "${REMAINING_REPLICA_NAME}" "${REMAINING_REPLICA_ZONE}" "${KUBE_MASTER_IP}"
  3799        gcloud compute target-pools delete \
  3800          --project "${PROJECT}" \
  3801          --region "${REGION}" \
  3802          --quiet \
  3803          "${MASTER_NAME}"
  3804      fi
  3805  
  3806      if [[ ${GCE_PRIVATE_CLUSTER:-} == "true" ]]; then
  3807        remove-from-internal-loadbalancer "${REMAINING_REPLICA_NAME}" "${REMAINING_REPLICA_ZONE}"
  3808        delete-internal-loadbalancer
  3809        attach-internal-master-ip "${REMAINING_REPLICA_NAME}" "${REMAINING_REPLICA_ZONE}" "${KUBE_MASTER_INTERNAL_IP}"
  3810      fi
  3811    fi
  3812  
  3813    # If there are no more remaining master replicas, we should delete all remaining network resources.
  3814    if [[ "${REMAINING_MASTER_COUNT}" -eq 0 ]]; then
  3815      # Delete firewall rule for the master, etcd servers, and nodes.
  3816      delete-firewall-rules "${MASTER_NAME}-https" "${MASTER_NAME}-etcd" "${NODE_TAG}-all" "${MASTER_NAME}-konnectivity-server"
  3817      # Delete the master's reserved IP
  3818      if gcloud compute addresses describe "${MASTER_NAME}-ip" --region "${REGION}" --project "${PROJECT}" &>/dev/null; then
  3819        gcloud compute addresses delete \
  3820          --project "${PROJECT}" \
  3821          --region "${REGION}" \
  3822          --quiet \
  3823          "${MASTER_NAME}-ip"
  3824      fi
  3825  
  3826      if gcloud compute addresses describe "${MASTER_NAME}-internal-ip" --region "${REGION}" --project "${PROJECT}" &>/dev/null; then
  3827        gcloud compute addresses delete \
  3828          --project "${PROJECT}" \
  3829          --region "${REGION}" \
  3830          --quiet \
  3831          "${MASTER_NAME}-internal-ip"
  3832      fi
  3833    fi
  3834  
  3835    if [[ "${KUBE_DELETE_NODES:-}" != "false" ]]; then
  3836      # Find out what minions are running.
  3837      local -a minions
  3838      kube::util::read-array minions < <(gcloud compute instances list \
  3839        --project "${PROJECT}" \
  3840        --filter="(name ~ '${NODE_INSTANCE_PREFIX}-.+' OR name ~ '${WINDOWS_NODE_INSTANCE_PREFIX}-.+') AND zone:(${ZONE})" \
  3841        --format='value(name)')
  3842      # If any minions are running, delete them in batches.
  3843      while (( "${#minions[@]}" > 0 )); do
  3844        echo Deleting nodes "${minions[*]::${batch}}"
  3845        gcloud compute instances delete \
  3846          --project "${PROJECT}" \
  3847          --quiet \
  3848          --delete-disks boot \
  3849          --zone "${ZONE}" \
  3850          "${minions[@]::${batch}}"
  3851        minions=( "${minions[@]:${batch}}" )
  3852      done
  3853    fi
  3854  
  3855    # If there are no more remaining master replicas: delete routes, pd for influxdb and update kubeconfig
  3856    if [[ "${REMAINING_MASTER_COUNT}" -eq 0 ]]; then
  3857      # Delete routes.
  3858      local -a routes
  3859      # Clean up all routes w/ names like "<cluster-name>-<node-GUID>"
  3860      # e.g. "kubernetes-12345678-90ab-cdef-1234-567890abcdef". The name is
  3861      # determined by the node controller on the master.
  3862      # Note that this is currently a noop, as synchronously deleting the node MIG
  3863      # first allows the master to cleanup routes itself.
  3864      local TRUNCATED_PREFIX="${INSTANCE_PREFIX:0:26}"
  3865      kube::util::read-array routes < <(gcloud compute routes list --project "${NETWORK_PROJECT}" \
  3866        --filter="name ~ '${TRUNCATED_PREFIX}-.{8}-.{4}-.{4}-.{4}-.{12}'" \
  3867        --format='value(name)')
  3868      while (( "${#routes[@]}" > 0 )); do
  3869        echo Deleting routes "${routes[*]::${batch}}"
  3870        gcloud compute routes delete \
  3871          --project "${NETWORK_PROJECT}" \
  3872          --quiet \
  3873          "${routes[@]::${batch}}"
  3874        routes=( "${routes[@]:${batch}}" )
  3875      done
  3876  
  3877      # Delete persistent disk for influx-db.
  3878      if gcloud compute disks describe "${INSTANCE_PREFIX}"-influxdb-pd --zone "${ZONE}" --project "${PROJECT}" &>/dev/null; then
  3879        gcloud compute disks delete \
  3880          --project "${PROJECT}" \
  3881          --quiet \
  3882          --zone "${ZONE}" \
  3883          "${INSTANCE_PREFIX}"-influxdb-pd
  3884      fi
  3885  
  3886      # Delete all remaining firewall rules and network.
  3887      delete-firewall-rules \
  3888        "${CLUSTER_NAME}-default-internal-master" \
  3889        "${CLUSTER_NAME}-default-internal-node"
  3890  
  3891      if [[ "${KUBE_DELETE_NETWORK}" == "true" ]]; then
  3892        delete-firewall-rules \
  3893        "${NETWORK}-default-ssh" \
  3894        "${NETWORK}-default-rdp" \
  3895        "${NETWORK}-default-internal"  # Pre-1.5 clusters
  3896        delete-cloud-nat-router
  3897        # Delete all remaining firewall rules in the network.
  3898        delete-all-firewall-rules || true
  3899        delete-subnetworks || true
  3900        delete-network || true  # might fail if there are leaked resources that reference the network
  3901      fi
  3902  
  3903      # If there are no more remaining master replicas, we should update kubeconfig.
  3904      export CONTEXT="${PROJECT}_${INSTANCE_PREFIX}"
  3905      clear-kubeconfig
  3906    else
  3907    # If some master replicas remain: cluster has been changed, we need to re-validate it.
  3908      echo "... calling validate-cluster" >&2
  3909      # Override errexit
  3910      (validate-cluster) && validate_result="$?" || validate_result="$?"
  3911  
  3912      # We have two different failure modes from validate cluster:
  3913      # - 1: fatal error - cluster won't be working correctly
  3914      # - 2: weak error - something went wrong, but cluster probably will be working correctly
  3915      # We just print an error message in case 2).
  3916      if [[ "${validate_result}" -eq 1 ]]; then
  3917        exit 1
  3918      elif [[ "${validate_result}" -eq 2 ]]; then
  3919        echo "...ignoring non-fatal errors in validate-cluster" >&2
  3920      fi
  3921    fi
  3922    set -e
  3923  }
  3924  
  3925  # Prints name of one of the master replicas in the current zone. It will be either
  3926  # just MASTER_NAME or MASTER_NAME with a suffix for a replica (see get-replica-name-regexp).
  3927  #
  3928  # Assumed vars:
  3929  #   PROJECT
  3930  #   ZONE
  3931  #   MASTER_NAME
  3932  #
  3933  # NOTE: Must be in sync with get-replica-name-regexp and set-replica-name.
  3934  function get-replica-name() {
  3935    # Ignore if gcloud compute fails and successfully echo any outcome
  3936    # shellcheck disable=SC2005
  3937    echo "$(gcloud compute instances list \
  3938      --project "${PROJECT}" \
  3939      --filter="name ~ '$(get-replica-name-regexp)' AND zone:(${ZONE})" \
  3940      --format "value(name)" | head -n1)"
  3941  }
  3942  
  3943  # Prints comma-separated names of all of the master replicas in all zones.
  3944  #
  3945  # Assumed vars:
  3946  #   PROJECT
  3947  #   MASTER_NAME
  3948  #
  3949  # NOTE: Must be in sync with get-replica-name-regexp and set-replica-name.
  3950  function get-all-replica-names() {
  3951    # Ignore if gcloud compute fails and successfully echo any outcome
  3952    # shellcheck disable=SC2005
  3953    echo "$(gcloud compute instances list \
  3954      --project "${PROJECT}" \
  3955      --filter="name ~ '$(get-replica-name-regexp)'" \
  3956      --format "value(name)" | tr "\n" "," | sed 's/,$//')"
  3957  }
  3958  
  3959  # Prints the number of all of the master replicas in all zones.
  3960  #
  3961  # Assumed vars:
  3962  #   MASTER_NAME
  3963  function get-master-replicas-count() {
  3964    detect-project
  3965    local num_masters
  3966    num_masters=$(gcloud compute instances list \
  3967      --project "${PROJECT}" \
  3968      --filter="name ~ '$(get-replica-name-regexp)'" \
  3969      --format "value(zone)" | wc -l)
  3970    echo -n "${num_masters}"
  3971  }
  3972  
  3973  # Prints regexp for full master machine name. In a cluster with replicated master,
  3974  # VM names may either be MASTER_NAME or MASTER_NAME with a suffix for a replica.
  3975  function get-replica-name-regexp() {
  3976    echo "^${MASTER_NAME}(-...)?"
  3977  }
  3978  
  3979  # Sets REPLICA_NAME to a unique name for a master replica that will match
  3980  # expected regexp (see get-replica-name-regexp).
  3981  #
  3982  # Assumed vars:
  3983  #   PROJECT
  3984  #   ZONE
  3985  #   MASTER_NAME
  3986  #
  3987  # Sets:
  3988  #   REPLICA_NAME
  3989  function set-replica-name() {
  3990    local instances
  3991    instances=$(gcloud compute instances list \
  3992      --project "${PROJECT}" \
  3993      --filter="name ~ '$(get-replica-name-regexp)'" \
  3994      --format "value(name)")
  3995  
  3996    suffix=""
  3997    while echo "${instances}" | grep "${suffix}" &>/dev/null; do
  3998      suffix="$(date | md5sum | head -c3)"
  3999    done
  4000    REPLICA_NAME="${MASTER_NAME}-${suffix}"
  4001  }
  4002  
  4003  # Gets the instance templates in use by the cluster. It echos the template names
  4004  # so that the function output can be used.
  4005  # Assumed vars:
  4006  #   NODE_INSTANCE_PREFIX
  4007  #   WINDOWS_NODE_INSTANCE_PREFIX
  4008  #
  4009  # $1: project
  4010  function get-template() {
  4011    local linux_filter="${NODE_INSTANCE_PREFIX}-(extra-)?template(-(${KUBE_RELEASE_VERSION_DASHED_REGEX}|${KUBE_CI_VERSION_DASHED_REGEX}))?"
  4012    local windows_filter="${WINDOWS_NODE_INSTANCE_PREFIX}-template(-(${KUBE_RELEASE_VERSION_DASHED_REGEX}|${KUBE_CI_VERSION_DASHED_REGEX}))?"
  4013  
  4014    gcloud compute instance-templates list \
  4015      --filter="name ~ '${linux_filter}' OR name ~ '${windows_filter}'" \
  4016      --project="${1}" --format='value(name)'
  4017  }
  4018  
  4019  # Checks if there are any present resources related kubernetes cluster.
  4020  #
  4021  # Assumed vars:
  4022  #   MASTER_NAME
  4023  #   NODE_INSTANCE_PREFIX
  4024  #   WINDOWS_NODE_INSTANCE_PREFIX
  4025  #   ZONE
  4026  #   REGION
  4027  # Vars set:
  4028  #   KUBE_RESOURCE_FOUND
  4029  function check-resources() {
  4030    detect-project
  4031    detect-node-names
  4032  
  4033    echo "Looking for already existing resources"
  4034    KUBE_RESOURCE_FOUND=""
  4035  
  4036    if [[ -n "${INSTANCE_GROUPS[*]:-}" ]]; then
  4037      KUBE_RESOURCE_FOUND="Managed instance groups ${INSTANCE_GROUPS[*]}"
  4038      return 1
  4039    fi
  4040    if [[ -n "${WINDOWS_INSTANCE_GROUPS[*]:-}" ]]; then
  4041      KUBE_RESOURCE_FOUND="Managed instance groups ${WINDOWS_INSTANCE_GROUPS[*]}"
  4042      return 1
  4043    fi
  4044  
  4045    if gcloud compute instance-templates describe --project "${PROJECT}" "${NODE_INSTANCE_PREFIX}-template" &>/dev/null; then
  4046      KUBE_RESOURCE_FOUND="Instance template ${NODE_INSTANCE_PREFIX}-template"
  4047      return 1
  4048    fi
  4049    if gcloud compute instance-templates describe --project "${PROJECT}" "${WINDOWS_NODE_INSTANCE_PREFIX}-template" &>/dev/null; then
  4050      KUBE_RESOURCE_FOUND="Instance template ${WINDOWS_NODE_INSTANCE_PREFIX}-template"
  4051      return 1
  4052    fi
  4053  
  4054    if gcloud compute instances describe --project "${PROJECT}" "${MASTER_NAME}" --zone "${ZONE}" &>/dev/null; then
  4055      KUBE_RESOURCE_FOUND="Kubernetes master ${MASTER_NAME}"
  4056      return 1
  4057    fi
  4058  
  4059    if gcloud compute disks describe --project "${PROJECT}" "${MASTER_NAME}"-pd --zone "${ZONE}" &>/dev/null; then
  4060      KUBE_RESOURCE_FOUND="Persistent disk ${MASTER_NAME}-pd"
  4061      return 1
  4062    fi
  4063  
  4064    # Find out what minions are running.
  4065    local -a minions
  4066    kube::util::read-array minions < <(gcloud compute instances list \
  4067      --project "${PROJECT}" \
  4068      --filter="(name ~ '${NODE_INSTANCE_PREFIX}-.+' OR name ~ '${WINDOWS_NODE_INSTANCE_PREFIX}-.+') AND zone:(${ZONE})" \
  4069      --format='value(name)')
  4070    if (( "${#minions[@]}" > 0 )); then
  4071      KUBE_RESOURCE_FOUND="${#minions[@]} matching ${NODE_INSTANCE_PREFIX}-.+ or ${WINDOWS_NODE_INSTANCE_PREFIX}-.+"
  4072      return 1
  4073    fi
  4074  
  4075    if gcloud compute firewall-rules describe --project "${NETWORK_PROJECT}" "${MASTER_NAME}-https" &>/dev/null; then
  4076      KUBE_RESOURCE_FOUND="Firewall rules for ${MASTER_NAME}-https"
  4077      return 1
  4078    fi
  4079  
  4080    if gcloud compute firewall-rules describe --project "${NETWORK_PROJECT}" "${NODE_TAG}-all" &>/dev/null; then
  4081      KUBE_RESOURCE_FOUND="Firewall rules for ${MASTER_NAME}-all"
  4082      return 1
  4083    fi
  4084  
  4085    local -a routes
  4086    kube::util::read-array routes < <(gcloud compute routes list --project "${NETWORK_PROJECT}" \
  4087      --filter="name ~ '${INSTANCE_PREFIX}-minion-.{4}'" --format='value(name)')
  4088    if (( "${#routes[@]}" > 0 )); then
  4089      KUBE_RESOURCE_FOUND="${#routes[@]} routes matching ${INSTANCE_PREFIX}-minion-.{4}"
  4090      return 1
  4091    fi
  4092  
  4093    if gcloud compute addresses describe --project "${PROJECT}" "${MASTER_NAME}-ip" --region "${REGION}" &>/dev/null; then
  4094      KUBE_RESOURCE_FOUND="Master's reserved IP"
  4095      return 1
  4096    fi
  4097  
  4098    # No resources found.
  4099    return 0
  4100  }
  4101  
  4102  # -----------------------------------------------------------------------------
  4103  # Cluster specific test helpers
  4104  
  4105  # Execute prior to running tests to build a release if required for env.
  4106  #
  4107  # Assumed Vars:
  4108  #   KUBE_ROOT
  4109  function test-build-release() {
  4110    # Make a release
  4111    "${KUBE_ROOT}/build/release.sh"
  4112  }
  4113  
  4114  # Execute prior to running tests to initialize required structure.
  4115  #
  4116  # Assumed vars:
  4117  #   Variables from config.sh
  4118  function test-setup() {
  4119    # Detect the project into $PROJECT if it isn't set
  4120    detect-project
  4121  
  4122    if [[ ${MULTIZONE:-} == "true" && -n ${E2E_ZONES:-} ]]; then
  4123      for KUBE_GCE_ZONE in ${E2E_ZONES}; do
  4124        KUBE_GCE_ZONE="${KUBE_GCE_ZONE}" KUBE_USE_EXISTING_MASTER="${KUBE_USE_EXISTING_MASTER:-}" "${KUBE_ROOT}/cluster/kube-up.sh"
  4125        KUBE_USE_EXISTING_MASTER="true" # For subsequent zones we use the existing master
  4126      done
  4127    else
  4128      "${KUBE_ROOT}/cluster/kube-up.sh"
  4129    fi
  4130  
  4131    # Open up port 80 & 8080 so common containers on minions can be reached
  4132    # TODO(roberthbailey): Remove this once we are no longer relying on hostPorts.
  4133    local start
  4134    start=$(date +%s)
  4135    gcloud compute firewall-rules create \
  4136      --project "${NETWORK_PROJECT}" \
  4137      --target-tags "${NODE_TAG}" \
  4138      --allow tcp:80,tcp:8080 \
  4139      --network "${NETWORK}" \
  4140      "${NODE_TAG}-http-alt" 2> /dev/null || true
  4141    # As there is no simple way to wait longer for this operation we need to manually
  4142    # wait some additional time (20 minutes altogether).
  4143    while ! gcloud compute firewall-rules describe --project "${NETWORK_PROJECT}" "${NODE_TAG}-http-alt" 2> /dev/null; do
  4144      if [[ $((start + 1200)) -lt $(date +%s) ]]; then
  4145        echo -e "${color_red:-}Failed to create firewall ${NODE_TAG}-http-alt in ${NETWORK_PROJECT}" >&2
  4146        exit 1
  4147      fi
  4148      sleep 5
  4149    done
  4150  
  4151    # Open up the NodePort range
  4152    # TODO(justinsb): Move to main setup, if we decide whether we want to do this by default.
  4153    start=$(date +%s)
  4154    gcloud compute firewall-rules create \
  4155      --project "${NETWORK_PROJECT}" \
  4156      --target-tags "${NODE_TAG}" \
  4157      --allow tcp:30000-32767,udp:30000-32767 \
  4158      --network "${NETWORK}" \
  4159      "${NODE_TAG}-nodeports" 2> /dev/null || true
  4160    # As there is no simple way to wait longer for this operation we need to manually
  4161    # wait some additional time (20 minutes altogether).
  4162    while ! gcloud compute firewall-rules describe --project "${NETWORK_PROJECT}" "${NODE_TAG}-nodeports" 2> /dev/null; do
  4163      if [[ $((start + 1200)) -lt $(date +%s) ]]; then
  4164        echo -e "${color_red}Failed to create firewall ${NODE_TAG}-nodeports in ${PROJECT}" >&2
  4165        exit 1
  4166      fi
  4167      sleep 5
  4168    done
  4169  }
  4170  
  4171  # Execute after running tests to perform any required clean-up.
  4172  function test-teardown() {
  4173    detect-project
  4174    echo "Shutting down test cluster in background."
  4175    delete-firewall-rules \
  4176      "${NODE_TAG}-http-alt" \
  4177      "${NODE_TAG}-nodeports"
  4178    if [[ ${MULTIZONE:-} == "true" && -n ${E2E_ZONES:-} ]]; then
  4179      local zones
  4180      read -r -a zones <<< "${E2E_ZONES}"
  4181      # tear them down in reverse order, finally tearing down the master too.
  4182      for ((zone_num=${#zones[@]}-1; zone_num>0; zone_num--)); do
  4183        KUBE_GCE_ZONE="${zones[zone_num]}" KUBE_USE_EXISTING_MASTER="true" "${KUBE_ROOT}/cluster/kube-down.sh"
  4184      done
  4185      KUBE_GCE_ZONE="${zones[0]}" KUBE_USE_EXISTING_MASTER="false" "${KUBE_ROOT}/cluster/kube-down.sh"
  4186    else
  4187      "${KUBE_ROOT}/cluster/kube-down.sh"
  4188    fi
  4189  }
  4190  
  4191  # SSH to a node by name ($1) and run a command ($2).
  4192  function ssh-to-node() {
  4193    local node="$1"
  4194    local cmd="$2"
  4195    # Loop until we can successfully ssh into the box
  4196    for (( i=0; i<5; i++)); do
  4197      if gcloud compute ssh --ssh-flag='-o LogLevel=quiet' --ssh-flag='-o ConnectTimeout=30' --project "${PROJECT}" --zone="${ZONE}" "${node}" --command 'echo test > /dev/null'; then
  4198        break
  4199      fi
  4200      sleep 5
  4201    done
  4202    # Then actually try the command.
  4203    gcloud compute ssh --ssh-flag="-o LogLevel=quiet" --ssh-flag="-o ConnectTimeout=30" --project "${PROJECT}" --zone="${ZONE}" "${node}" --command "${cmd}"
  4204  }
  4205  
  4206  # Perform preparations required to run e2e tests
  4207  function prepare-e2e() {
  4208    detect-project
  4209  }
  4210  
  4211  # Delete the image given by $1.
  4212  function delete-image() {
  4213    gcloud container images delete --quiet "$1"
  4214  }