k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/build/common.sh (about)

     1  #!/usr/bin/env bash
     2  
     3  # Copyright 2014 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  # shellcheck disable=SC2034 # Variables sourced in other scripts.
    18  
    19  # Common utilities, variables and checks for all build scripts.
    20  set -o errexit
    21  set -o nounset
    22  set -o pipefail
    23  
    24  # Unset CDPATH, having it set messes up with script import paths
    25  unset CDPATH
    26  
    27  USER_ID=$(id -u)
    28  GROUP_ID=$(id -g)
    29  
    30  DOCKER_OPTS=${DOCKER_OPTS:-""}
    31  IFS=" " read -r -a DOCKER <<< "docker ${DOCKER_OPTS}"
    32  DOCKER_HOST=${DOCKER_HOST:-""}
    33  GOPROXY=${GOPROXY:-""}
    34  
    35  # This will canonicalize the path
    36  KUBE_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd -P)
    37  
    38  source "${KUBE_ROOT}/hack/lib/init.sh"
    39  
    40  # Constants
    41  readonly KUBE_BUILD_IMAGE_REPO=kube-build
    42  KUBE_BUILD_IMAGE_CROSS_TAG="$(cat "${KUBE_ROOT}/build/build-image/cross/VERSION")"
    43  readonly KUBE_BUILD_IMAGE_CROSS_TAG
    44  
    45  readonly KUBE_DOCKER_REGISTRY="${KUBE_DOCKER_REGISTRY:-registry.k8s.io}"
    46  KUBE_BASE_IMAGE_REGISTRY="${KUBE_BASE_IMAGE_REGISTRY:-registry.k8s.io/build-image}"
    47  readonly KUBE_BASE_IMAGE_REGISTRY
    48  
    49  # This version number is used to cause everyone to rebuild their data containers
    50  # and build image.  This is especially useful for automated build systems like
    51  # Jenkins.
    52  #
    53  # Increment/change this number if you change the build image (anything under
    54  # build/build-image) or change the set of volumes in the data container.
    55  KUBE_BUILD_IMAGE_VERSION_BASE="$(cat "${KUBE_ROOT}/build/build-image/VERSION")"
    56  readonly KUBE_BUILD_IMAGE_VERSION_BASE
    57  readonly KUBE_BUILD_IMAGE_VERSION="${KUBE_BUILD_IMAGE_VERSION_BASE}-${KUBE_BUILD_IMAGE_CROSS_TAG}"
    58  
    59  # Make it possible to override the `kube-cross` image, and tag independent of `KUBE_BASE_IMAGE_REGISTRY`
    60  KUBE_CROSS_IMAGE="${KUBE_CROSS_IMAGE:-"${KUBE_BASE_IMAGE_REGISTRY}/kube-cross"}"
    61  readonly KUBE_CROSS_IMAGE
    62  KUBE_CROSS_VERSION="${KUBE_CROSS_VERSION:-"${KUBE_BUILD_IMAGE_CROSS_TAG}"}"
    63  readonly KUBE_CROSS_VERSION
    64  
    65  # Here we map the output directories across both the local and remote _output
    66  # directories:
    67  #
    68  # *_OUTPUT_ROOT    - the base of all output in that environment.
    69  # *_OUTPUT_SUBPATH - location where golang stuff is built/cached.  Also
    70  #                    persisted across docker runs with a volume mount.
    71  # *_OUTPUT_BINPATH - location where final binaries are placed.  If the remote
    72  #                    is really remote, this is the stuff that has to be copied
    73  #                    back.
    74  # OUT_DIR can come in from the Makefile, so honor it.
    75  readonly LOCAL_OUTPUT_ROOT="${KUBE_ROOT}/${OUT_DIR:-_output}"
    76  readonly LOCAL_OUTPUT_SUBPATH="${LOCAL_OUTPUT_ROOT}/dockerized"
    77  readonly LOCAL_OUTPUT_BINPATH="${LOCAL_OUTPUT_SUBPATH}/bin"
    78  readonly LOCAL_OUTPUT_GOPATH="${LOCAL_OUTPUT_SUBPATH}/go"
    79  readonly LOCAL_OUTPUT_IMAGE_STAGING="${LOCAL_OUTPUT_ROOT}/images"
    80  
    81  # This is a symlink to binaries for "this platform" (e.g. build tools).
    82  readonly THIS_PLATFORM_BIN="${LOCAL_OUTPUT_ROOT}/bin"
    83  
    84  readonly KUBE_GO_PACKAGE=k8s.io/kubernetes
    85  readonly REMOTE_ROOT="/go/src/${KUBE_GO_PACKAGE}"
    86  readonly REMOTE_OUTPUT_ROOT="${REMOTE_ROOT}/_output"
    87  readonly REMOTE_OUTPUT_SUBPATH="${REMOTE_OUTPUT_ROOT}/dockerized"
    88  readonly REMOTE_OUTPUT_BINPATH="${REMOTE_OUTPUT_SUBPATH}/bin"
    89  readonly REMOTE_OUTPUT_GOPATH="${REMOTE_OUTPUT_SUBPATH}/go"
    90  
    91  # This is the port on the workstation host to expose RSYNC on.  Set this if you
    92  # are doing something fancy with ssh tunneling.
    93  readonly KUBE_RSYNC_PORT="${KUBE_RSYNC_PORT:-}"
    94  
    95  # This is the port that rsync is running on *inside* the container. This may be
    96  # mapped to KUBE_RSYNC_PORT via docker networking.
    97  readonly KUBE_CONTAINER_RSYNC_PORT=8730
    98  
    99  # These are the default versions (image tags) for their respective base images.
   100  readonly __default_distroless_iptables_version=v0.5.4
   101  readonly __default_go_runner_version=v2.3.1-go1.22.3-bookworm.0
   102  readonly __default_setcap_version=bookworm-v1.0.2
   103  
   104  # These are the base images for the Docker-wrapped binaries.
   105  readonly KUBE_GORUNNER_IMAGE="${KUBE_GORUNNER_IMAGE:-$KUBE_BASE_IMAGE_REGISTRY/go-runner:$__default_go_runner_version}"
   106  readonly KUBE_APISERVER_BASE_IMAGE="${KUBE_APISERVER_BASE_IMAGE:-$KUBE_GORUNNER_IMAGE}"
   107  readonly KUBE_CONTROLLER_MANAGER_BASE_IMAGE="${KUBE_CONTROLLER_MANAGER_BASE_IMAGE:-$KUBE_GORUNNER_IMAGE}"
   108  readonly KUBE_SCHEDULER_BASE_IMAGE="${KUBE_SCHEDULER_BASE_IMAGE:-$KUBE_GORUNNER_IMAGE}"
   109  readonly KUBE_PROXY_BASE_IMAGE="${KUBE_PROXY_BASE_IMAGE:-$KUBE_BASE_IMAGE_REGISTRY/distroless-iptables:$__default_distroless_iptables_version}"
   110  readonly KUBECTL_BASE_IMAGE="${KUBECTL_BASE_IMAGE:-$KUBE_GORUNNER_IMAGE}"
   111  
   112  # This is the image used in a multi-stage build to apply capabilities to Docker-wrapped binaries.
   113  readonly KUBE_BUILD_SETCAP_IMAGE="${KUBE_BUILD_SETCAP_IMAGE:-$KUBE_BASE_IMAGE_REGISTRY/setcap:$__default_setcap_version}"
   114  
   115  # Get the set of master binaries that run in Docker (on Linux)
   116  # Entry format is "<binary-name>,<base-image>".
   117  # Binaries are placed in /usr/local/bin inside the image.
   118  # `make` users can override any or all of the base images using the associated
   119  # environment variables.
   120  #
   121  # $1 - server architecture
   122  kube::build::get_docker_wrapped_binaries() {
   123    ### If you change any of these lists, please also update DOCKERIZED_BINARIES
   124    ### in build/BUILD. And kube::golang::server_image_targets
   125    local targets=(
   126      "kube-apiserver,${KUBE_APISERVER_BASE_IMAGE}"
   127      "kube-controller-manager,${KUBE_CONTROLLER_MANAGER_BASE_IMAGE}"
   128      "kube-scheduler,${KUBE_SCHEDULER_BASE_IMAGE}"
   129      "kube-proxy,${KUBE_PROXY_BASE_IMAGE}"
   130      "kubectl,${KUBECTL_BASE_IMAGE}"
   131    )
   132  
   133    echo "${targets[@]}"
   134  }
   135  
   136  # ---------------------------------------------------------------------------
   137  # Basic setup functions
   138  
   139  # Verify that the right utilities and such are installed for building Kube. Set
   140  # up some dynamic constants.
   141  # Args:
   142  #   $1 - boolean of whether to require functioning docker (default true)
   143  #
   144  # Vars set:
   145  #   KUBE_ROOT_HASH
   146  #   KUBE_BUILD_IMAGE_TAG_BASE
   147  #   KUBE_BUILD_IMAGE_TAG
   148  #   KUBE_BUILD_IMAGE
   149  #   KUBE_BUILD_CONTAINER_NAME_BASE
   150  #   KUBE_BUILD_CONTAINER_NAME
   151  #   KUBE_DATA_CONTAINER_NAME_BASE
   152  #   KUBE_DATA_CONTAINER_NAME
   153  #   KUBE_RSYNC_CONTAINER_NAME_BASE
   154  #   KUBE_RSYNC_CONTAINER_NAME
   155  #   DOCKER_MOUNT_ARGS
   156  #   LOCAL_OUTPUT_BUILD_CONTEXT
   157  # shellcheck disable=SC2120 # optional parameters
   158  function kube::build::verify_prereqs() {
   159    local -r require_docker=${1:-true}
   160    kube::log::status "Verifying Prerequisites...."
   161    kube::build::ensure_tar || return 1
   162    kube::build::ensure_rsync || return 1
   163    if ${require_docker}; then
   164      kube::build::ensure_docker_in_path || return 1
   165      if kube::build::is_osx; then
   166          kube::build::docker_available_on_osx || return 1
   167      fi
   168      kube::util::ensure_docker_daemon_connectivity || return 1
   169  
   170      if (( KUBE_VERBOSE > 6 )); then
   171        kube::log::status "Docker Version:"
   172        "${DOCKER[@]}" version | kube::log::info_from_stdin
   173      fi
   174    fi
   175  
   176    KUBE_GIT_BRANCH=$(git symbolic-ref --short -q HEAD 2>/dev/null || true)
   177    KUBE_ROOT_HASH=$(kube::build::short_hash "${HOSTNAME:-}:${KUBE_ROOT}:${KUBE_GIT_BRANCH}")
   178    KUBE_BUILD_IMAGE_TAG_BASE="build-${KUBE_ROOT_HASH}"
   179    KUBE_BUILD_IMAGE_TAG="${KUBE_BUILD_IMAGE_TAG_BASE}-${KUBE_BUILD_IMAGE_VERSION}"
   180    KUBE_BUILD_IMAGE="${KUBE_BUILD_IMAGE_REPO}:${KUBE_BUILD_IMAGE_TAG}"
   181    KUBE_BUILD_CONTAINER_NAME_BASE="kube-build-${KUBE_ROOT_HASH}"
   182    KUBE_BUILD_CONTAINER_NAME="${KUBE_BUILD_CONTAINER_NAME_BASE}-${KUBE_BUILD_IMAGE_VERSION}"
   183    KUBE_RSYNC_CONTAINER_NAME_BASE="kube-rsync-${KUBE_ROOT_HASH}"
   184    KUBE_RSYNC_CONTAINER_NAME="${KUBE_RSYNC_CONTAINER_NAME_BASE}-${KUBE_BUILD_IMAGE_VERSION}"
   185    KUBE_DATA_CONTAINER_NAME_BASE="kube-build-data-${KUBE_ROOT_HASH}"
   186    KUBE_DATA_CONTAINER_NAME="${KUBE_DATA_CONTAINER_NAME_BASE}-${KUBE_BUILD_IMAGE_VERSION}"
   187    DOCKER_MOUNT_ARGS=(--volumes-from "${KUBE_DATA_CONTAINER_NAME}")
   188    LOCAL_OUTPUT_BUILD_CONTEXT="${LOCAL_OUTPUT_IMAGE_STAGING}/${KUBE_BUILD_IMAGE}"
   189  
   190    kube::version::get_version_vars
   191    kube::version::save_version_vars "${KUBE_ROOT}/.dockerized-kube-version-defs"
   192  
   193    # Without this, the user's umask can leak through.
   194    umask 0022
   195  }
   196  
   197  # ---------------------------------------------------------------------------
   198  # Utility functions
   199  
   200  function kube::build::docker_available_on_osx() {
   201    if [[ -z "${DOCKER_HOST}" ]]; then
   202      if [[ -S "/var/run/docker.sock" ]] || [[ -S "$(docker context inspect --format  '{{.Endpoints.docker.Host}}' | awk -F 'unix://' '{print $2}')" ]]; then
   203        kube::log::status "Using docker on macOS"
   204        return 0
   205      fi
   206  
   207      kube::log::status "No docker host is set."
   208      kube::log::status "It looks like you're running Mac OS X, but Docker for Mac cannot be found."
   209      kube::log::status "See: https://docs.docker.com/engine/installation/mac/ for installation instructions."
   210      return 1
   211    fi
   212  }
   213  
   214  function kube::build::is_osx() {
   215    [[ "$(uname)" == "Darwin" ]]
   216  }
   217  
   218  function kube::build::is_gnu_sed() {
   219    [[ $(sed --version 2>&1) == *GNU* ]]
   220  }
   221  
   222  function kube::build::ensure_rsync() {
   223    if [[ -z "$(which rsync)" ]]; then
   224      kube::log::error "Can't find 'rsync' in PATH, please fix and retry."
   225      return 1
   226    fi
   227  }
   228  
   229  function kube::build::ensure_docker_in_path() {
   230    if [[ -z "$(which docker)" ]]; then
   231      kube::log::error "Can't find 'docker' in PATH, please fix and retry."
   232      kube::log::error "See https://docs.docker.com/installation/#installation for installation instructions."
   233      return 1
   234    fi
   235  }
   236  
   237  function kube::build::ensure_tar() {
   238    if [[ -n "${TAR:-}" ]]; then
   239      return
   240    fi
   241  
   242    # Find gnu tar if it is available, bomb out if not.
   243    TAR=tar
   244    if which gtar &>/dev/null; then
   245        TAR=gtar
   246    else
   247        if which gnutar &>/dev/null; then
   248  	  TAR=gnutar
   249        fi
   250    fi
   251    if ! "${TAR}" --version | grep -q GNU; then
   252      echo "  !!! Cannot find GNU tar. Build on Linux or install GNU tar"
   253      echo "      on Mac OS X (brew install gnu-tar)."
   254      return 1
   255    fi
   256  }
   257  
   258  function kube::build::has_docker() {
   259    which docker &> /dev/null
   260  }
   261  
   262  function kube::build::has_ip() {
   263    which ip &> /dev/null && ip -Version | grep 'iproute2' &> /dev/null
   264  }
   265  
   266  # Detect if a specific image exists
   267  #
   268  # $1 - image repo name
   269  # $2 - image tag
   270  function kube::build::docker_image_exists() {
   271    [[ -n $1 && -n $2 ]] || {
   272      kube::log::error "Internal error. Image not specified in docker_image_exists."
   273      exit 2
   274    }
   275  
   276    [[ $("${DOCKER[@]}" images -q "${1}:${2}") ]]
   277  }
   278  
   279  # Delete all images that match a tag prefix except for the "current" version
   280  #
   281  # $1: The image repo/name
   282  # $2: The tag base. We consider any image that matches $2*
   283  # $3: The current image not to delete if provided
   284  function kube::build::docker_delete_old_images() {
   285    # In Docker 1.12, we can replace this with
   286    #    docker images "$1" --format "{{.Tag}}"
   287    for tag in $("${DOCKER[@]}" images "${1}" | tail -n +2 | awk '{print $2}') ; do
   288      if [[ "${tag}" != "${2}"* ]] ; then
   289        V=3 kube::log::status "Keeping image ${1}:${tag}"
   290        continue
   291      fi
   292  
   293      if [[ -z "${3:-}" || "${tag}" != "${3}" ]] ; then
   294        V=2 kube::log::status "Deleting image ${1}:${tag}"
   295        "${DOCKER[@]}" rmi "${1}:${tag}" >/dev/null
   296      else
   297        V=3 kube::log::status "Keeping image ${1}:${tag}"
   298      fi
   299    done
   300  }
   301  
   302  # Stop and delete all containers that match a pattern
   303  #
   304  # $1: The base container prefix
   305  # $2: The current container to keep, if provided
   306  function kube::build::docker_delete_old_containers() {
   307    # In Docker 1.12 we can replace this line with
   308    #   docker ps -a --format="{{.Names}}"
   309    for container in $("${DOCKER[@]}" ps -a | tail -n +2 | awk '{print $NF}') ; do
   310      if [[ "${container}" != "${1}"* ]] ; then
   311        V=3 kube::log::status "Keeping container ${container}"
   312        continue
   313      fi
   314      if [[ -z "${2:-}" || "${container}" != "${2}" ]] ; then
   315        V=2 kube::log::status "Deleting container ${container}"
   316        kube::build::destroy_container "${container}"
   317      else
   318        V=3 kube::log::status "Keeping container ${container}"
   319      fi
   320    done
   321  }
   322  
   323  # Takes $1 and computes a short has for it. Useful for unique tag generation
   324  function kube::build::short_hash() {
   325    [[ $# -eq 1 ]] || {
   326      kube::log::error "Internal error.  No data based to short_hash."
   327      exit 2
   328    }
   329  
   330    local short_hash
   331    if which md5 >/dev/null 2>&1; then
   332      short_hash=$(md5 -q -s "$1")
   333    else
   334      short_hash=$(echo -n "$1" | md5sum)
   335    fi
   336    echo "${short_hash:0:10}"
   337  }
   338  
   339  # Pedantically kill, wait-on and remove a container. The -f -v options
   340  # to rm don't actually seem to get the job done, so force kill the
   341  # container, wait to ensure it's stopped, then try the remove. This is
   342  # a workaround for bug https://github.com/docker/docker/issues/3968.
   343  function kube::build::destroy_container() {
   344    "${DOCKER[@]}" kill "$1" >/dev/null 2>&1 || true
   345    if [[ $("${DOCKER[@]}" version --format '{{.Server.Version}}') = 17.06.0* ]]; then
   346      # Workaround https://github.com/moby/moby/issues/33948.
   347      # TODO: remove when 17.06.0 is not relevant anymore
   348      DOCKER_API_VERSION=v1.29 "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true
   349    else
   350      "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true
   351    fi
   352    "${DOCKER[@]}" rm -f -v "$1" >/dev/null 2>&1 || true
   353  }
   354  
   355  # ---------------------------------------------------------------------------
   356  # Building
   357  
   358  
   359  function kube::build::clean() {
   360    if kube::build::has_docker ; then
   361      kube::build::docker_delete_old_containers "${KUBE_BUILD_CONTAINER_NAME_BASE}"
   362      kube::build::docker_delete_old_containers "${KUBE_RSYNC_CONTAINER_NAME_BASE}"
   363      kube::build::docker_delete_old_containers "${KUBE_DATA_CONTAINER_NAME_BASE}"
   364      kube::build::docker_delete_old_images "${KUBE_BUILD_IMAGE_REPO}" "${KUBE_BUILD_IMAGE_TAG_BASE}"
   365  
   366      V=2 kube::log::status "Cleaning all untagged docker images"
   367      "${DOCKER[@]}" rmi "$("${DOCKER[@]}" images -q --filter 'dangling=true')" 2> /dev/null || true
   368    fi
   369  
   370    if [[ -d "${LOCAL_OUTPUT_ROOT}" ]]; then
   371      kube::log::status "Removing _output directory"
   372      # this ensures we can clean _output/local/go/cache which is not rw by default
   373      chmod -R +w "${LOCAL_OUTPUT_ROOT}"
   374      rm -rf "${LOCAL_OUTPUT_ROOT}"
   375    fi
   376  }
   377  
   378  # Set up the context directory for the kube-build image and build it.
   379  function kube::build::build_image() {
   380    mkdir -p "${LOCAL_OUTPUT_BUILD_CONTEXT}"
   381    # Make sure the context directory owned by the right user for syncing sources to container.
   382    chown -R "${USER_ID}":"${GROUP_ID}" "${LOCAL_OUTPUT_BUILD_CONTEXT}"
   383  
   384    cp /etc/localtime "${LOCAL_OUTPUT_BUILD_CONTEXT}/"
   385    chmod u+w "${LOCAL_OUTPUT_BUILD_CONTEXT}/localtime"
   386  
   387    cp "${KUBE_ROOT}/build/build-image/Dockerfile" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile"
   388    cp "${KUBE_ROOT}/build/build-image/rsyncd.sh" "${LOCAL_OUTPUT_BUILD_CONTEXT}/"
   389    dd if=/dev/urandom bs=512 count=1 2>/dev/null | LC_ALL=C tr -dc 'A-Za-z0-9' | dd bs=32 count=1 2>/dev/null > "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password"
   390    chmod go= "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password"
   391  
   392    kube::build::docker_build "${KUBE_BUILD_IMAGE}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" 'false' "--build-arg=KUBE_CROSS_IMAGE=${KUBE_CROSS_IMAGE} --build-arg=KUBE_CROSS_VERSION=${KUBE_CROSS_VERSION}"
   393  
   394    # Clean up old versions of everything
   395    kube::build::docker_delete_old_containers "${KUBE_BUILD_CONTAINER_NAME_BASE}" "${KUBE_BUILD_CONTAINER_NAME}"
   396    kube::build::docker_delete_old_containers "${KUBE_RSYNC_CONTAINER_NAME_BASE}" "${KUBE_RSYNC_CONTAINER_NAME}"
   397    kube::build::docker_delete_old_containers "${KUBE_DATA_CONTAINER_NAME_BASE}" "${KUBE_DATA_CONTAINER_NAME}"
   398    kube::build::docker_delete_old_images "${KUBE_BUILD_IMAGE_REPO}" "${KUBE_BUILD_IMAGE_TAG_BASE}" "${KUBE_BUILD_IMAGE_TAG}"
   399  
   400    kube::build::ensure_data_container
   401    kube::build::sync_to_container
   402  }
   403  
   404  # Build a docker image from a Dockerfile.
   405  # $1 is the name of the image to build
   406  # $2 is the location of the "context" directory, with the Dockerfile at the root.
   407  # $3 is the value to set the --pull flag for docker build; true by default
   408  # $4 is the set of --build-args for docker.
   409  function kube::build::docker_build() {
   410    kube::util::ensure-docker-buildx
   411  
   412    local -r image=$1
   413    local -r context_dir=$2
   414    local -r pull="${3:-true}"
   415    local build_args
   416    IFS=" " read -r -a build_args <<< "$4"
   417    readonly build_args
   418    local -ra build_cmd=("${DOCKER[@]}" buildx build --load -t "${image}" "--pull=${pull}" "${build_args[@]}" "${context_dir}")
   419  
   420    kube::log::status "Building Docker image ${image}"
   421    local docker_output
   422    docker_output=$(DOCKER_CLI_EXPERIMENTAL=enabled "${build_cmd[@]}" 2>&1) || {
   423      cat <<EOF >&2
   424  +++ Docker build command failed for ${image}
   425  
   426  ${docker_output}
   427  
   428  To retry manually, run:
   429  
   430  DOCKER_CLI_EXPERIMENTAL=enabled ${build_cmd[*]}
   431  
   432  EOF
   433      return 1
   434    }
   435  }
   436  
   437  function kube::build::ensure_data_container() {
   438    # If the data container exists AND exited successfully, we can use it.
   439    # Otherwise nuke it and start over.
   440    local ret=0
   441    local code=0
   442  
   443    code=$(docker inspect \
   444        -f '{{.State.ExitCode}}' \
   445        "${KUBE_DATA_CONTAINER_NAME}" 2>/dev/null) || ret=$?
   446    if [[ "${ret}" == 0 && "${code}" != 0 ]]; then
   447      kube::build::destroy_container "${KUBE_DATA_CONTAINER_NAME}"
   448      ret=1
   449    fi
   450    if [[ "${ret}" != 0 ]]; then
   451      kube::log::status "Creating data container ${KUBE_DATA_CONTAINER_NAME}"
   452      # We have to ensure the directory exists, or else the docker run will
   453      # create it as root.
   454      mkdir -p "${LOCAL_OUTPUT_GOPATH}"
   455      # We want this to run as root to be able to chown, so non-root users can
   456      # later use the result as a data container.  This run both creates the data
   457      # container and chowns the GOPATH.
   458      #
   459      # The data container creates volumes for all of the directories that store
   460      # intermediates for the Go build. This enables incremental builds across
   461      # Docker sessions. The *_cgo paths are re-compiled versions of the go std
   462      # libraries for true static building.
   463      local -ra docker_cmd=(
   464        "${DOCKER[@]}" run
   465        --volume "${REMOTE_ROOT}"   # white-out the whole output dir
   466        --volume /usr/local/go/pkg/linux_386_cgo
   467        --volume /usr/local/go/pkg/linux_amd64_cgo
   468        --volume /usr/local/go/pkg/linux_arm_cgo
   469        --volume /usr/local/go/pkg/linux_arm64_cgo
   470        --volume /usr/local/go/pkg/linux_ppc64le_cgo
   471        --volume /usr/local/go/pkg/darwin_amd64_cgo
   472        --volume /usr/local/go/pkg/darwin_386_cgo
   473        --volume /usr/local/go/pkg/windows_amd64_cgo
   474        --volume /usr/local/go/pkg/windows_386_cgo
   475        --name "${KUBE_DATA_CONTAINER_NAME}"
   476        --hostname "${HOSTNAME}"
   477        "${KUBE_BUILD_IMAGE}"
   478        chown -R "${USER_ID}":"${GROUP_ID}"
   479          "${REMOTE_ROOT}"
   480          /usr/local/go/pkg/
   481      )
   482      "${docker_cmd[@]}"
   483    fi
   484  }
   485  
   486  # Run a command in the kube-build image.  This assumes that the image has
   487  # already been built.
   488  function kube::build::run_build_command() {
   489    kube::log::status "Running build command..."
   490    kube::build::run_build_command_ex "${KUBE_BUILD_CONTAINER_NAME}" -- "$@"
   491  }
   492  
   493  # Run a command in the kube-build image.  This assumes that the image has
   494  # already been built.
   495  #
   496  # Arguments are in the form of
   497  #  <container name> <extra docker args> -- <command>
   498  function kube::build::run_build_command_ex() {
   499    [[ $# != 0 ]] || { echo "Invalid input - please specify a container name." >&2; return 4; }
   500    local container_name="${1}"
   501    shift
   502  
   503    local -a docker_run_opts=(
   504      "--name=${container_name}"
   505      "--user=$(id -u):$(id -g)"
   506      "--hostname=${HOSTNAME}"
   507      "-e=GOPROXY=${GOPROXY}"
   508      "${DOCKER_MOUNT_ARGS[@]}"
   509    )
   510  
   511    local detach=false
   512  
   513    [[ $# != 0 ]] || { echo "Invalid input - please specify docker arguments followed by --." >&2; return 4; }
   514    # Everything before "--" is an arg to docker
   515    until [ -z "${1-}" ] ; do
   516      if [[ "$1" == "--" ]]; then
   517        shift
   518        break
   519      fi
   520      docker_run_opts+=("$1")
   521      if [[ "$1" == "-d" || "$1" == "--detach" ]] ; then
   522        detach=true
   523      fi
   524      shift
   525    done
   526  
   527    # Everything after "--" is the command to run
   528    [[ $# != 0 ]] || { echo "Invalid input - please specify a command to run." >&2; return 4; }
   529    local -a cmd=()
   530    until [ -z "${1-}" ] ; do
   531      cmd+=("$1")
   532      shift
   533    done
   534  
   535    docker_run_opts+=(
   536      --env "KUBE_FASTBUILD=${KUBE_FASTBUILD:-false}"
   537      --env "KUBE_BUILDER_OS=${OSTYPE:-notdetected}"
   538      --env "KUBE_VERBOSE=${KUBE_VERBOSE}"
   539      --env "KUBE_BUILD_WITH_COVERAGE=${KUBE_BUILD_WITH_COVERAGE:-}"
   540      --env "KUBE_BUILD_PLATFORMS=${KUBE_BUILD_PLATFORMS:-}"
   541      --env "KUBE_CGO_OVERRIDES=' ${KUBE_CGO_OVERRIDES[*]:-} '"
   542      --env "KUBE_STATIC_OVERRIDES=' ${KUBE_STATIC_OVERRIDES[*]:-} '"
   543      --env "FORCE_HOST_GO=${FORCE_HOST_GO:-}"
   544      --env "GO_VERSION=${GO_VERSION:-}"
   545      --env "GOTOOLCHAIN=${GOTOOLCHAIN:-}"
   546      --env "GOFLAGS=${GOFLAGS:-}"
   547      --env "GOGCFLAGS=${GOGCFLAGS:-}"
   548      --env "SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH:-}"
   549    )
   550  
   551    # use GOLDFLAGS only if it is set explicitly.
   552    if [[ -v GOLDFLAGS ]]; then
   553      docker_run_opts+=(
   554        --env "GOLDFLAGS=${GOLDFLAGS:-}"
   555      )
   556    fi
   557  
   558    if [[ -n "${DOCKER_CGROUP_PARENT:-}" ]]; then
   559      kube::log::status "Using ${DOCKER_CGROUP_PARENT} as container cgroup parent"
   560      docker_run_opts+=(--cgroup-parent "${DOCKER_CGROUP_PARENT}")
   561    fi
   562  
   563    # If we have stdin we can run interactive.  This allows things like 'shell.sh'
   564    # to work.  However, if we run this way and don't have stdin, then it ends up
   565    # running in a daemon-ish mode.  So if we don't have a stdin, we explicitly
   566    # attach stderr/stdout but don't bother asking for a tty.
   567    if [[ -t 0 ]]; then
   568      docker_run_opts+=(--interactive --tty)
   569    elif [[ "${detach}" == false ]]; then
   570      docker_run_opts+=("--attach=stdout" "--attach=stderr")
   571    fi
   572  
   573    local -ra docker_cmd=(
   574      "${DOCKER[@]}" run "${docker_run_opts[@]}" "${KUBE_BUILD_IMAGE}")
   575  
   576    # Clean up container from any previous run
   577    kube::build::destroy_container "${container_name}"
   578    "${docker_cmd[@]}" "${cmd[@]}"
   579    if [[ "${detach}" == false ]]; then
   580      kube::build::destroy_container "${container_name}"
   581    fi
   582  }
   583  
   584  function kube::build::rsync_probe {
   585    # Wait until rsync is up and running.
   586    local tries=20
   587    while (( tries > 0 )) ; do
   588      if rsync "rsync://k8s@${1}:${2}/" \
   589           --password-file="${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" \
   590           &> /dev/null ; then
   591        return 0
   592      fi
   593      tries=$(( tries - 1))
   594      sleep 0.1
   595    done
   596  
   597    return 1
   598  }
   599  
   600  # Start up the rsync container in the background. This should be explicitly
   601  # stopped with kube::build::stop_rsyncd_container.
   602  #
   603  # This will set the global var KUBE_RSYNC_ADDR to the effective port that the
   604  # rsync daemon can be reached out.
   605  function kube::build::start_rsyncd_container() {
   606    IPTOOL=ifconfig
   607    if kube::build::has_ip ; then
   608      IPTOOL="ip address"
   609    fi
   610    kube::build::stop_rsyncd_container
   611    V=3 kube::log::status "Starting rsyncd container"
   612    kube::build::run_build_command_ex \
   613      "${KUBE_RSYNC_CONTAINER_NAME}" -p 127.0.0.1:"${KUBE_RSYNC_PORT}":"${KUBE_CONTAINER_RSYNC_PORT}" -d \
   614      -e ALLOW_HOST="$(${IPTOOL} | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | tr '\n' ' ')" \
   615      -- /rsyncd.sh >/dev/null
   616  
   617    local mapped_port
   618    if ! mapped_port=$("${DOCKER[@]}" port "${KUBE_RSYNC_CONTAINER_NAME}" "${KUBE_CONTAINER_RSYNC_PORT}" 2> /dev/null | cut -d: -f 2) ; then
   619      kube::log::error "Could not get effective rsync port"
   620      return 1
   621    fi
   622  
   623    local container_ip
   624    container_ip=$("${DOCKER[@]}" inspect --format '{{ .NetworkSettings.IPAddress }}' "${KUBE_RSYNC_CONTAINER_NAME}")
   625  
   626    # Sometimes we can reach rsync through localhost and a NAT'd port.  Other
   627    # times (when we are running in another docker container on the Jenkins
   628    # machines) we have to talk directly to the container IP.  There is no one
   629    # strategy that works in all cases so we test to figure out which situation we
   630    # are in.
   631    if kube::build::rsync_probe 127.0.0.1 "${mapped_port}"; then
   632      KUBE_RSYNC_ADDR="127.0.0.1:${mapped_port}"
   633      return 0
   634    elif kube::build::rsync_probe "${container_ip}" "${KUBE_CONTAINER_RSYNC_PORT}"; then
   635      KUBE_RSYNC_ADDR="${container_ip}:${KUBE_CONTAINER_RSYNC_PORT}"
   636      return 0
   637    fi
   638  
   639    kube::log::error "Could not connect to rsync container."
   640    return 1
   641  }
   642  
   643  function kube::build::stop_rsyncd_container() {
   644    V=3 kube::log::status "Stopping any currently running rsyncd container"
   645    unset KUBE_RSYNC_ADDR
   646    kube::build::destroy_container "${KUBE_RSYNC_CONTAINER_NAME}"
   647  }
   648  
   649  function kube::build::rsync {
   650    local -a rsync_opts=(
   651      --archive
   652      "--password-file=${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password"
   653    )
   654    if (( KUBE_VERBOSE >= 6 )); then
   655      rsync_opts+=("-iv")
   656    fi
   657    if (( KUBE_RSYNC_COMPRESS > 0 )); then
   658       rsync_opts+=("--compress-level=${KUBE_RSYNC_COMPRESS}")
   659    fi
   660    V=3 kube::log::status "Running rsync"
   661    rsync "${rsync_opts[@]}" "$@"
   662  }
   663  
   664  # This will launch rsyncd in a container and then sync the source tree to the
   665  # container over the local network.
   666  function kube::build::sync_to_container() {
   667    kube::log::status "Syncing sources to container"
   668  
   669    kube::build::start_rsyncd_container
   670  
   671    # rsync filters are a bit confusing.  Here we are syncing everything except
   672    # output only directories and things that are not necessary like the git
   673    # directory and generated files. The '- /' filter prevents rsync
   674    # from trying to set the uid/gid/perms on the root of the sync tree.
   675    # As an exception, we need to sync generated files in staging/, because
   676    # they will not be re-generated by 'make'. Note that the 'H' filtered files
   677    # are hidden from rsync so they will be deleted in the target container if
   678    # they exist. This will allow them to be re-created in the container if
   679    # necessary.
   680    kube::build::rsync \
   681      --delete \
   682      --filter='- /_tmp/' \
   683      --filter='- /_output/' \
   684      --filter='- /' \
   685      "${KUBE_ROOT}/" "rsync://k8s@${KUBE_RSYNC_ADDR}/k8s/"
   686  
   687    kube::build::stop_rsyncd_container
   688  }
   689  
   690  # Copy all build results back out.
   691  function kube::build::copy_output() {
   692    kube::log::status "Syncing out of container"
   693  
   694    kube::build::start_rsyncd_container
   695  
   696    # The filter syntax for rsync is a little obscure. It filters on files and
   697    # directories.  If you don't go in to a directory you won't find any files
   698    # there.  Rules are evaluated in order.  The last two rules are a little
   699    # magic. '+ */' says to go in to every directory and '- /**' says to ignore
   700    # any file or directory that isn't already specifically allowed.
   701    #
   702    # We are looking to copy out all of the built binaries along with various
   703    # generated files.
   704    kube::build::rsync \
   705      --prune-empty-dirs \
   706      --filter='- /_temp/' \
   707      --filter='+ /vendor/' \
   708      --filter='+ /staging/***/Godeps/**' \
   709      --filter='+ /_output/dockerized/bin/**' \
   710      --filter='+ zz_generated.*' \
   711      --filter='+ generated.proto' \
   712      --filter='+ *.pb.go' \
   713      --filter='+ types.go' \
   714      --filter='+ */' \
   715      --filter='- /**' \
   716      "rsync://k8s@${KUBE_RSYNC_ADDR}/k8s/" "${KUBE_ROOT}"
   717  
   718    kube::build::stop_rsyncd_container
   719  }