...

Text file src/k8s.io/kubernetes/hack/lib/golang.sh

Documentation: k8s.io/kubernetes/hack/lib

     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
    19readonly KUBE_GOPATH="${KUBE_GOPATH:-"${KUBE_OUTPUT}/go"}"
    20export KUBE_GOPATH
    21
    22# The server platform we are building on.
    23readonly KUBE_SUPPORTED_SERVER_PLATFORMS=(
    24  linux/amd64
    25  linux/arm64
    26  linux/s390x
    27  linux/ppc64le
    28)
    29
    30# The node platforms we build for
    31readonly KUBE_SUPPORTED_NODE_PLATFORMS=(
    32  linux/amd64
    33  linux/arm64
    34  linux/s390x
    35  linux/ppc64le
    36  windows/amd64
    37)
    38
    39# If we update this we should also update the set of platforms whose standard
    40# library is precompiled for in build/build-image/cross/Dockerfile
    41readonly KUBE_SUPPORTED_CLIENT_PLATFORMS=(
    42  linux/amd64
    43  linux/386
    44  linux/arm
    45  linux/arm64
    46  linux/s390x
    47  linux/ppc64le
    48  darwin/amd64
    49  darwin/arm64
    50  windows/amd64
    51  windows/386
    52  windows/arm64
    53)
    54
    55# Which platforms we should compile test targets for.
    56# Not all client platforms need these tests
    57readonly KUBE_SUPPORTED_TEST_PLATFORMS=(
    58  linux/amd64
    59  linux/arm64
    60  linux/s390x
    61  linux/ppc64le
    62  darwin/amd64
    63  darwin/arm64
    64  windows/amd64
    65  windows/arm64
    66)
    67
    68# The set of server targets that we are only building for Linux
    69kube::golang::server_targets() {
    70  local targets=(
    71    cmd/kube-proxy
    72    cmd/kube-apiserver
    73    cmd/kube-controller-manager
    74    cmd/kubelet
    75    cmd/kubeadm
    76    cmd/kube-scheduler
    77    staging/src/k8s.io/component-base/logs/kube-log-runner
    78    staging/src/k8s.io/kube-aggregator
    79    staging/src/k8s.io/apiextensions-apiserver
    80    cluster/gce/gci/mounter
    81  )
    82  echo "${targets[@]}"
    83}
    84
    85IFS=" " read -ra KUBE_SERVER_TARGETS <<< "$(kube::golang::server_targets)"
    86readonly KUBE_SERVER_TARGETS
    87readonly KUBE_SERVER_BINARIES=("${KUBE_SERVER_TARGETS[@]##*/}")
    88
    89# The set of server targets we build docker images for
    90kube::golang::server_image_targets() {
    91  # NOTE: this contains cmd targets for kube::build::get_docker_wrapped_binaries
    92  local targets=(
    93    cmd/kube-apiserver
    94    cmd/kube-controller-manager
    95    cmd/kube-scheduler
    96    cmd/kube-proxy
    97    cmd/kubectl
    98  )
    99  echo "${targets[@]}"
   100}
   101
   102IFS=" " read -ra KUBE_SERVER_IMAGE_TARGETS <<< "$(kube::golang::server_image_targets)"
   103readonly KUBE_SERVER_IMAGE_TARGETS
   104readonly KUBE_SERVER_IMAGE_BINARIES=("${KUBE_SERVER_IMAGE_TARGETS[@]##*/}")
   105
   106# The set of conformance targets we build docker image for
   107kube::golang::conformance_image_targets() {
   108  # NOTE: this contains cmd targets for kube::release::build_conformance_image
   109  local targets=(
   110    ginkgo
   111    test/e2e/e2e.test
   112    test/conformance/image/go-runner
   113    cmd/kubectl
   114  )
   115  echo "${targets[@]}"
   116}
   117
   118IFS=" " read -ra KUBE_CONFORMANCE_IMAGE_TARGETS <<< "$(kube::golang::conformance_image_targets)"
   119readonly KUBE_CONFORMANCE_IMAGE_TARGETS
   120
   121# The set of server targets that we are only building for Kubernetes nodes
   122kube::golang::node_targets() {
   123  local targets=(
   124    cmd/kube-proxy
   125    cmd/kubeadm
   126    cmd/kubelet
   127    staging/src/k8s.io/component-base/logs/kube-log-runner
   128  )
   129  echo "${targets[@]}"
   130}
   131
   132IFS=" " read -ra KUBE_NODE_TARGETS <<< "$(kube::golang::node_targets)"
   133readonly KUBE_NODE_TARGETS
   134readonly KUBE_NODE_BINARIES=("${KUBE_NODE_TARGETS[@]##*/}")
   135readonly KUBE_NODE_BINARIES_WIN=("${KUBE_NODE_BINARIES[@]/%/.exe}")
   136
   137# ------------
   138# NOTE: All functions that return lists should use newlines.
   139# bash functions can't return arrays, and spaces are tricky, so newline
   140# separators are the preferred pattern.
   141# To transform a string of newline-separated items to an array, use kube::util::read-array:
   142# kube::util::read-array FOO < <(kube::golang::dups a b c a)
   143#
   144# ALWAYS remember to quote your subshells. Not doing so will break in
   145# bash 4.3, and potentially cause other issues.
   146# ------------
   147
   148# Returns a sorted newline-separated list containing only duplicated items.
   149kube::golang::dups() {
   150  # We use printf to insert newlines, which are required by sort.
   151  printf "%s\n" "$@" | sort | uniq -d
   152}
   153
   154# Returns a sorted newline-separated list with duplicated items removed.
   155kube::golang::dedup() {
   156  # We use printf to insert newlines, which are required by sort.
   157  printf "%s\n" "$@" | sort -u
   158}
   159
   160# Depends on values of user-facing KUBE_BUILD_PLATFORMS, KUBE_FASTBUILD,
   161# and KUBE_BUILDER_OS.
   162# Configures KUBE_SERVER_PLATFORMS, KUBE_NODE_PLATFOMRS,
   163# KUBE_TEST_PLATFORMS, and KUBE_CLIENT_PLATFORMS, then sets them
   164# to readonly.
   165# The configured vars will only contain platforms allowed by the
   166# KUBE_SUPPORTED* vars at the top of this file.
   167declare -a KUBE_SERVER_PLATFORMS
   168declare -a KUBE_CLIENT_PLATFORMS
   169declare -a KUBE_NODE_PLATFORMS
   170declare -a KUBE_TEST_PLATFORMS
   171kube::golang::setup_platforms() {
   172  if [[ -n "${KUBE_BUILD_PLATFORMS:-}" ]]; then
   173    # KUBE_BUILD_PLATFORMS needs to be read into an array before the next
   174    # step, or quoting treats it all as one element.
   175    local -a platforms
   176    IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS}"
   177
   178    # Deduplicate to ensure the intersection trick with kube::golang::dups
   179    # is not defeated by duplicates in user input.
   180    kube::util::read-array platforms < <(kube::golang::dedup "${platforms[@]}")
   181
   182    # Use kube::golang::dups to restrict the builds to the platforms in
   183    # KUBE_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each
   184    # set, so if they appear twice after the merge they are in the intersection.
   185    kube::util::read-array KUBE_SERVER_PLATFORMS < <(kube::golang::dups \
   186        "${platforms[@]}" \
   187        "${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}" \
   188      )
   189    readonly KUBE_SERVER_PLATFORMS
   190
   191    kube::util::read-array KUBE_NODE_PLATFORMS < <(kube::golang::dups \
   192        "${platforms[@]}" \
   193        "${KUBE_SUPPORTED_NODE_PLATFORMS[@]}" \
   194      )
   195    readonly KUBE_NODE_PLATFORMS
   196
   197    kube::util::read-array KUBE_TEST_PLATFORMS < <(kube::golang::dups \
   198        "${platforms[@]}" \
   199        "${KUBE_SUPPORTED_TEST_PLATFORMS[@]}" \
   200      )
   201    readonly KUBE_TEST_PLATFORMS
   202
   203    kube::util::read-array KUBE_CLIENT_PLATFORMS < <(kube::golang::dups \
   204        "${platforms[@]}" \
   205        "${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}" \
   206      )
   207    readonly KUBE_CLIENT_PLATFORMS
   208
   209  elif [[ "${KUBE_FASTBUILD:-}" == "true" ]]; then
   210    host_arch=$(kube::util::host_arch)
   211    if [[ "${host_arch}" != "amd64" && "${host_arch}" != "arm64" && "${host_arch}" != "ppc64le" && "${host_arch}" != "s390x" ]]; then
   212      # on any platform other than amd64, arm64, ppc64le and s390x, we just default to amd64
   213      host_arch="amd64"
   214    fi
   215    KUBE_SERVER_PLATFORMS=("linux/${host_arch}")
   216    readonly KUBE_SERVER_PLATFORMS
   217    KUBE_NODE_PLATFORMS=("linux/${host_arch}")
   218    readonly KUBE_NODE_PLATFORMS
   219    if [[ "${KUBE_BUILDER_OS:-}" == "darwin"* ]]; then
   220      KUBE_TEST_PLATFORMS=(
   221        "darwin/${host_arch}"
   222        "linux/${host_arch}"
   223      )
   224      readonly KUBE_TEST_PLATFORMS
   225      KUBE_CLIENT_PLATFORMS=(
   226        "darwin/${host_arch}"
   227        "linux/${host_arch}"
   228      )
   229      readonly KUBE_CLIENT_PLATFORMS
   230    else
   231      KUBE_TEST_PLATFORMS=("linux/${host_arch}")
   232      readonly KUBE_TEST_PLATFORMS
   233      KUBE_CLIENT_PLATFORMS=("linux/${host_arch}")
   234      readonly KUBE_CLIENT_PLATFORMS
   235    fi
   236  else
   237    KUBE_SERVER_PLATFORMS=("${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}")
   238    readonly KUBE_SERVER_PLATFORMS
   239
   240    KUBE_NODE_PLATFORMS=("${KUBE_SUPPORTED_NODE_PLATFORMS[@]}")
   241    readonly KUBE_NODE_PLATFORMS
   242
   243    KUBE_CLIENT_PLATFORMS=("${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}")
   244    readonly KUBE_CLIENT_PLATFORMS
   245
   246    KUBE_TEST_PLATFORMS=("${KUBE_SUPPORTED_TEST_PLATFORMS[@]}")
   247    readonly KUBE_TEST_PLATFORMS
   248  fi
   249}
   250
   251kube::golang::setup_platforms
   252
   253# The set of client targets that we are building for all platforms
   254readonly KUBE_CLIENT_TARGETS=(
   255  cmd/kubectl
   256  cmd/kubectl-convert
   257)
   258readonly KUBE_CLIENT_BINARIES=("${KUBE_CLIENT_TARGETS[@]##*/}")
   259readonly KUBE_CLIENT_BINARIES_WIN=("${KUBE_CLIENT_BINARIES[@]/%/.exe}")
   260
   261# The set of test targets that we are building for all platforms
   262kube::golang::test_targets() {
   263  local targets=(
   264    ginkgo
   265    test/e2e/e2e.test
   266    test/conformance/image/go-runner
   267  )
   268  echo "${targets[@]}"
   269}
   270IFS=" " read -ra KUBE_TEST_TARGETS <<< "$(kube::golang::test_targets)"
   271readonly KUBE_TEST_TARGETS
   272readonly KUBE_TEST_BINARIES=("${KUBE_TEST_TARGETS[@]##*/}")
   273readonly KUBE_TEST_BINARIES_WIN=("${KUBE_TEST_BINARIES[@]/%/.exe}")
   274readonly KUBE_TEST_PORTABLE=(
   275  test/e2e/testing-manifests
   276  test/kubemark
   277  hack/e2e-internal
   278  hack/get-build.sh
   279  hack/ginkgo-e2e.sh
   280  hack/lib
   281)
   282
   283# Test targets which run on the Kubernetes clusters directly, so we only
   284# need to target server platforms.
   285# These binaries will be distributed in the kubernetes-test tarball.
   286kube::golang::server_test_targets() {
   287  local targets=(
   288    cmd/kubemark
   289    ginkgo
   290  )
   291
   292  if [[ "${OSTYPE:-}" == "linux"* ]]; then
   293    targets+=( test/e2e_node/e2e_node.test )
   294  fi
   295
   296  echo "${targets[@]}"
   297}
   298
   299IFS=" " read -ra KUBE_TEST_SERVER_TARGETS <<< "$(kube::golang::server_test_targets)"
   300readonly KUBE_TEST_SERVER_TARGETS
   301readonly KUBE_TEST_SERVER_BINARIES=("${KUBE_TEST_SERVER_TARGETS[@]##*/}")
   302readonly KUBE_TEST_SERVER_PLATFORMS=("${KUBE_SERVER_PLATFORMS[@]:+"${KUBE_SERVER_PLATFORMS[@]}"}")
   303
   304# Gigabytes necessary for parallel platform builds.
   305# As of March 2021 (go 1.16/amd64), the RSS usage is 2GiB by using cached
   306# memory of 15GiB.
   307# This variable can be overwritten at your own risk.
   308# It's defaulting to 20G to provide some headroom.
   309readonly KUBE_PARALLEL_BUILD_MEMORY=${KUBE_PARALLEL_BUILD_MEMORY:-20}
   310
   311readonly KUBE_ALL_TARGETS=(
   312  "${KUBE_SERVER_TARGETS[@]}"
   313  "${KUBE_CLIENT_TARGETS[@]}"
   314  "${KUBE_TEST_TARGETS[@]}"
   315  "${KUBE_TEST_SERVER_TARGETS[@]}"
   316)
   317readonly KUBE_ALL_BINARIES=("${KUBE_ALL_TARGETS[@]##*/}")
   318
   319readonly KUBE_STATIC_BINARIES=(
   320  apiextensions-apiserver
   321  kube-aggregator
   322  kube-apiserver
   323  kube-controller-manager
   324  kube-scheduler
   325  kube-proxy
   326  kube-log-runner
   327  kubeadm
   328  kubectl
   329  kubectl-convert
   330  kubemark
   331  mounter
   332)
   333
   334# Fully-qualified package names that we want to instrument for coverage information.
   335readonly KUBE_COVERAGE_INSTRUMENTED_PACKAGES=(
   336  k8s.io/kubernetes/cmd/kube-apiserver
   337  k8s.io/kubernetes/cmd/kube-controller-manager
   338  k8s.io/kubernetes/cmd/kube-scheduler
   339  k8s.io/kubernetes/cmd/kube-proxy
   340  k8s.io/kubernetes/cmd/kubelet
   341)
   342
   343# KUBE_CGO_OVERRIDES is a space-separated list of binaries which should be built
   344# with CGO enabled, assuming CGO is supported on the target platform.
   345# This overrides any entry in KUBE_STATIC_BINARIES.
   346IFS=" " read -ra KUBE_CGO_OVERRIDES_LIST <<< "${KUBE_CGO_OVERRIDES:-}"
   347readonly KUBE_CGO_OVERRIDES_LIST
   348# KUBE_STATIC_OVERRIDES is a space-separated list of binaries which should be
   349# built with CGO disabled. This is in addition to the list in
   350# KUBE_STATIC_BINARIES.
   351IFS=" " read -ra KUBE_STATIC_OVERRIDES_LIST <<< "${KUBE_STATIC_OVERRIDES:-}"
   352readonly KUBE_STATIC_OVERRIDES_LIST
   353
   354kube::golang::is_statically_linked() {
   355  local e
   356  # Explicitly enable cgo when building kubectl for darwin from darwin.
   357  [[ "$(go env GOHOSTOS)" == "darwin" && "$(go env GOOS)" == "darwin" &&
   358    "$1" == *"/kubectl" ]] && return 1
   359  if [[ -n "${KUBE_CGO_OVERRIDES_LIST:+x}" ]]; then
   360    for e in "${KUBE_CGO_OVERRIDES_LIST[@]}"; do [[ "${1}" == *"/${e}" ]] && return 1; done;
   361  fi
   362  for e in "${KUBE_STATIC_BINARIES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done;
   363  if [[ -n "${KUBE_STATIC_OVERRIDES_LIST:+x}" ]]; then
   364    for e in "${KUBE_STATIC_OVERRIDES_LIST[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done;
   365  fi
   366  return 1;
   367}
   368
   369# kube::golang::best_guess_go_targets takes a list of build targets, which might
   370# be Go-style names (e.g. example.com/foo/bar or ./foo/bar) or just local paths
   371# (e.g. foo/bar) and produces a respective list (on stdout) of our best guess at
   372# Go target names.
   373kube::golang::best_guess_go_targets() {
   374  local target
   375  for target; do
   376    if [ "${target}" = "ginkgo" ] ||
   377       [ "${target}" = "github.com/onsi/ginkgo/ginkgo" ] ||
   378       [ "${target}" = "vendor/github.com/onsi/ginkgo/ginkgo" ]; then
   379      # Aliases that build the ginkgo CLI for hack/ginkgo-e2e.sh.
   380      # "ginkgo" is the one that is documented in the Makefile. The others
   381      # are for backwards compatibility.
   382      echo "github.com/onsi/ginkgo/v2/ginkgo"
   383      continue
   384    fi
   385
   386    if [[ "${target}" =~ ^([[:alnum:]]+".")+[[:alnum:]]+"/" ]]; then
   387      # If the target starts with what looks like a domain name, assume it has a
   388      # fully-qualified Go package name.
   389      echo "${target}"
   390      continue
   391    fi
   392
   393    if [[ "${target}" =~ ^vendor/ ]]; then
   394      # Strip vendor/ prefix, since we're building in gomodule mode.  This is
   395      # for backwards compatibility.
   396      echo "${target#"vendor/"}"
   397      continue
   398    fi
   399
   400    # If the target starts with "./", assume it is a local path which qualifies
   401    # as a Go target name.
   402    if [[ "${target}" =~ ^\./ ]]; then
   403      echo "${target}"
   404      continue
   405    fi
   406
   407    # Otherwise assume it's a relative path (e.g. foo/bar or foo/bar/bar.test).
   408    # We probably SHOULDN'T accept this, but we did in the past and it would be
   409    # rude to break things if we don't NEED to.  We can't really test if it
   410    # exists or not, because the last element might be an output file (e.g.
   411    # bar.test) or even "...".
   412    echo "./${target}"
   413  done
   414}
   415
   416# kube::golang::normalize_go_targets takes a list of build targets, which might
   417# be Go-style names (e.g. example.com/foo/bar or ./foo/bar) or just local paths
   418# (e.g. foo/bar) and produces a respective list (on stdout) of Go package
   419# names.
   420#
   421# If this cannot find (go list -find -e) one or more inputs, it will emit the
   422# them on stdout, so callers can at least get a useful error.
   423kube::golang::normalize_go_targets() {
   424  local targets=()
   425  kube::util::read-array targets < <(kube::golang::best_guess_go_targets "$@")
   426  kube::util::read-array targets < <(kube::golang::dedup "${targets[@]}")
   427  set -- "${targets[@]}"
   428
   429  for target; do
   430    if [[ "${target}" =~ ".test"$ ]]; then
   431      local dir
   432      dir="$(dirname "${target}")"
   433      local tst
   434      tst="$(basename "${target}")"
   435      local pkg
   436      pkg="$(go list -find -e "${dir}")"
   437      echo "${pkg}/${tst}"
   438      continue
   439    fi
   440    if [[ "${target}" =~ "/..."$ ]]; then
   441      local dir
   442      dir="$(dirname "${target}")"
   443      local pkg
   444      pkg="$(go list -find -e "${dir}")"
   445      echo "${pkg}/..."
   446      continue
   447    fi
   448    go list -find -e "${target}"
   449  done
   450}
   451
   452# Asks golang what it thinks the host platform is. The go tool chain does some
   453# slightly different things when the target platform matches the host platform.
   454kube::golang::host_platform() {
   455  echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)"
   456}
   457
   458# Takes the platform name ($1) and sets the appropriate golang env variables
   459# for that platform.
   460kube::golang::set_platform_envs() {
   461  [[ -n ${1-} ]] || {
   462    kube::log::error_exit "!!! Internal error. No platform set in kube::golang::set_platform_envs"
   463  }
   464
   465  export GOOS=${platform%/*}
   466  export GOARCH=${platform##*/}
   467
   468  # Do not set CC when building natively on a platform, only if cross-compiling
   469  if [[ $(kube::golang::host_platform) != "$platform" ]]; then
   470    # Dynamic CGO linking for other server architectures than host architecture goes here
   471    # If you want to include support for more server platforms than these, add arch-specific gcc names here
   472    case "${platform}" in
   473      "linux/amd64")
   474        export CGO_ENABLED=1
   475        export CC=${KUBE_LINUX_AMD64_CC:-x86_64-linux-gnu-gcc}
   476        ;;
   477      "linux/arm")
   478        export CGO_ENABLED=1
   479        export CC=${KUBE_LINUX_ARM_CC:-arm-linux-gnueabihf-gcc}
   480        ;;
   481      "linux/arm64")
   482        export CGO_ENABLED=1
   483        export CC=${KUBE_LINUX_ARM64_CC:-aarch64-linux-gnu-gcc}
   484        ;;
   485      "linux/ppc64le")
   486        export CGO_ENABLED=1
   487        export CC=${KUBE_LINUX_PPC64LE_CC:-powerpc64le-linux-gnu-gcc}
   488        ;;
   489      "linux/s390x")
   490        export CGO_ENABLED=1
   491        export CC=${KUBE_LINUX_S390X_CC:-s390x-linux-gnu-gcc}
   492        ;;
   493    esac
   494  fi
   495
   496  # if CC is defined for platform then always enable it
   497  ccenv=$(echo "$platform" | awk -F/ '{print "KUBE_" toupper($1) "_" toupper($2) "_CC"}')
   498  if [ -n "${!ccenv-}" ]; then 
   499    export CGO_ENABLED=1
   500    export CC="${!ccenv}"
   501  fi
   502}
   503
   504# Ensure the go tool exists and is a viable version.
   505# Inputs:
   506#   env-var GO_VERSION is the desired go version to use, downloading it if needed (defaults to content of .go-version)
   507#   env-var FORCE_HOST_GO set to a non-empty value uses the go version in the $PATH and skips ensuring $GO_VERSION is used
   508kube::golang::internal::verify_go_version() {
   509  # default GO_VERSION to content of .go-version
   510  GO_VERSION="${GO_VERSION:-"$(cat "${KUBE_ROOT}/.go-version")"}"
   511  if [ "${GOTOOLCHAIN:-auto}" != 'auto' ]; then
   512    # no-op, just respect GOTOOLCHAIN
   513    :
   514  elif [ -n "${FORCE_HOST_GO:-}" ]; then
   515    # ensure existing host version is used, like before GOTOOLCHAIN existed
   516    export GOTOOLCHAIN='local'
   517  else
   518    # otherwise, we want to ensure the go version matches GO_VERSION
   519    GOTOOLCHAIN="go${GO_VERSION}"
   520    export GOTOOLCHAIN
   521    # if go is either not installed or too old to respect GOTOOLCHAIN then use gimme
   522    if ! (command -v go >/dev/null && [ "$(go version | cut -d' ' -f3)" = "${GOTOOLCHAIN}" ]); then
   523      export GIMME_ENV_PREFIX=${GIMME_ENV_PREFIX:-"${KUBE_OUTPUT}/.gimme/envs"}
   524      export GIMME_VERSION_PREFIX=${GIMME_VERSION_PREFIX:-"${KUBE_OUTPUT}/.gimme/versions"}
   525      # eval because the output of this is shell to set PATH etc.
   526      eval "$("${KUBE_ROOT}/third_party/gimme/gimme" "${GO_VERSION}")"
   527    fi
   528  fi
   529
   530  if [[ -z "$(command -v go)" ]]; then
   531    kube::log::usage_from_stdin <<EOF
   532Can't find 'go' in PATH, please fix and retry.
   533See http://golang.org/doc/install for installation instructions.
   534EOF
   535    return 2
   536  fi
   537
   538  local go_version
   539  IFS=" " read -ra go_version <<< "$(GOFLAGS='' go version)"
   540  local minimum_go_version
   541  minimum_go_version=go1.22
   542  if [[ "${minimum_go_version}" != $(echo -e "${minimum_go_version}\n${go_version[2]}" | sort -s -t. -k 1,1 -k 2,2n -k 3,3n | head -n1) && "${go_version[2]}" != "devel" ]]; then
   543    kube::log::usage_from_stdin <<EOF
   544Detected go version: ${go_version[*]}.
   545Kubernetes requires ${minimum_go_version} or greater.
   546Please install ${minimum_go_version} or later.
   547EOF
   548    return 2
   549  fi
   550}
   551
   552# kube::golang::setup_env will check that the `go` commands is available in
   553# ${PATH}. It will also check that the Go version is good enough for the
   554# Kubernetes build.
   555#
   556# Outputs:
   557#   env-var GOPATH points to our local output dir
   558#   env-var GOBIN is unset (we want binaries in a predictable place)
   559#   env-var PATH includes the local GOPATH
   560kube::golang::setup_env() {
   561  # Even in module mode, we need to set GOPATH for `go build` and `go install`
   562  # to work.  We build various tools (usually via `go install`) from a lot of
   563  # scripts.
   564  #   * We can't just set GOBIN because that does not work on cross-compiles.
   565  #   * We could always use `go build -o <something>`, but it's subtle wrt
   566  #     cross-compiles and whether the <something> is a file or a directory,
   567  #     and EVERY caller has to get it *just* right.
   568  #   * We could leave GOPATH alone and let `go install` write binaries
   569  #     wherever the user's GOPATH says (or doesn't say).
   570  #
   571  # Instead we set it to a phony local path and process the results ourselves.
   572  # In particular, GOPATH[0]/bin will be used for `go install`, with
   573  # cross-compiles adding an extra directory under that.
   574  export GOPATH="${KUBE_GOPATH}"
   575
   576  # If these are not set, set them now.  This ensures that any subsequent
   577  # scripts we run (which may call this function again) use the same values.
   578  export GOCACHE="${GOCACHE:-"${KUBE_GOPATH}/cache/build"}"
   579  export GOMODCACHE="${GOMODCACHE:-"${KUBE_GOPATH}/cache/mod"}"
   580
   581  # Make sure our own Go binaries are in PATH.
   582  export PATH="${KUBE_GOPATH}/bin:${PATH}"
   583
   584  # Unset GOBIN in case it already exists in the current session.
   585  # Cross-compiles will not work with it set.
   586  unset GOBIN
   587
   588  # Turn on modules and workspaces (both are default-on).
   589  unset GO111MODULE
   590  unset GOWORK
   591
   592  # This may try to download our specific Go version.  Do it last so it uses
   593  # the above-configured environment.
   594  kube::golang::internal::verify_go_version
   595}
   596
   597kube::golang::setup_gomaxprocs() {
   598  # GOMAXPROCS by default does not reflect the number of cpu(s) available
   599  # when running in a container, please see https://github.com/golang/go/issues/33803
   600  if [[ -z "${GOMAXPROCS:-}" ]]; then
   601    if ! command -v ncpu >/dev/null 2>&1; then
   602      go -C "${KUBE_ROOT}/hack/tools" install ./ncpu || echo "Will not automatically set GOMAXPROCS"
   603    fi
   604    if command -v ncpu >/dev/null 2>&1; then
   605      GOMAXPROCS=$(ncpu)
   606      export GOMAXPROCS
   607      kube::log::status "Set GOMAXPROCS automatically to ${GOMAXPROCS}"
   608    fi
   609  fi
   610}
   611
   612# This will take binaries from $GOPATH/bin and copy them to the appropriate
   613# place in ${KUBE_OUTPUT_BIN}
   614#
   615# Ideally this wouldn't be necessary and we could just set GOBIN to
   616# KUBE_OUTPUT_BIN but that won't work in the face of cross compilation.  'go
   617# install' will place binaries that match the host platform directly in $GOBIN
   618# while placing cross compiled binaries into `platform_arch` subdirs.  This
   619# complicates pretty much everything else we do around packaging and such.
   620kube::golang::place_bins() {
   621  local host_platform
   622  host_platform=$(kube::golang::host_platform)
   623
   624  V=2 kube::log::status "Placing binaries"
   625
   626  local platform
   627  for platform in "${KUBE_CLIENT_PLATFORMS[@]}"; do
   628    # The substitution on platform_src below will replace all slashes with
   629    # underscores.  It'll transform darwin/amd64 -> darwin_amd64.
   630    local platform_src="/${platform//\//_}"
   631    if [[ "${platform}" == "${host_platform}" ]]; then
   632      platform_src=""
   633      rm -f "${THIS_PLATFORM_BIN}"
   634      mkdir -p "$(dirname "${THIS_PLATFORM_BIN}")"
   635      ln -s "${KUBE_OUTPUT_BIN}/${platform}" "${THIS_PLATFORM_BIN}"
   636    fi
   637
   638    local full_binpath_src="${KUBE_GOPATH}/bin${platform_src}"
   639    if [[ -d "${full_binpath_src}" ]]; then
   640      mkdir -p "${KUBE_OUTPUT_BIN}/${platform}"
   641      find "${full_binpath_src}" -maxdepth 1 -type f -exec \
   642        rsync -pc {} "${KUBE_OUTPUT_BIN}/${platform}" \;
   643    fi
   644  done
   645}
   646
   647# Try and replicate the native binary placement of go install without
   648# calling go install.
   649kube::golang::outfile_for_binary() {
   650  local binary=$1
   651  local platform=$2
   652  local output_path="${KUBE_GOPATH}/bin"
   653  local bin
   654  bin=$(basename "${binary}")
   655  if [[ "${platform}" != "${host_platform}" ]]; then
   656    output_path="${output_path}/${platform//\//_}"
   657  fi
   658  if [[ ${GOOS} == "windows" ]]; then
   659    bin="${bin}.exe"
   660  fi
   661  echo "${output_path}/${bin}"
   662}
   663
   664# Argument: the name of a Kubernetes package.
   665# Returns 0 if the binary can be built with coverage, 1 otherwise.
   666# NB: this ignores whether coverage is globally enabled or not.
   667kube::golang::is_instrumented_package() {
   668  if kube::util::array_contains "$1" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"; then
   669    return 0
   670  fi
   671  # Some cases, like `make kubectl`, pass $1 as "./cmd/kubectl" rather than
   672  # "k8s.io/kubernetes/kubectl".  Try to normalize and handle that.  We don't
   673  # do this always because it is a bit slow.
   674  pkg=$(go list -find "$1")
   675  if kube::util::array_contains "${pkg}" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"; then
   676    return 0
   677  fi
   678  return 1
   679}
   680
   681# Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler)
   682# Echos the path to a dummy test used for coverage information.
   683kube::golang::path_for_coverage_dummy_test() {
   684  local package="$1"
   685  local path
   686  path=$(go list -find -f '{{.Dir}}' "${package}")
   687  local name
   688  name=$(basename "${package}")
   689  echo "${path}/zz_generated_${name}_test.go"
   690}
   691
   692# Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler).
   693# Creates a dummy unit test on disk in the source directory for the given package.
   694# This unit test will invoke the package's standard entry point when run.
   695kube::golang::create_coverage_dummy_test() {
   696  local package="$1"
   697  local name
   698  name="$(basename "${package}")"
   699  cat <<EOF > "$(kube::golang::path_for_coverage_dummy_test "${package}")"
   700package main
   701import (
   702  "testing"
   703  "k8s.io/kubernetes/pkg/util/coverage"
   704)
   705
   706func TestMain(m *testing.M) {
   707  // Get coverage running
   708  coverage.InitCoverage("${name}")
   709
   710  // Go!
   711  main()
   712
   713  // Make sure we actually write the profiling information to disk, if we make it here.
   714  // On long-running services, or anything that calls os.Exit(), this is insufficient,
   715  // so we also flush periodically with a default period of five seconds (configurable by
   716  // the KUBE_COVERAGE_FLUSH_INTERVAL environment variable).
   717  coverage.FlushCoverage()
   718}
   719EOF
   720}
   721
   722# Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler).
   723# Deletes a test generated by kube::golang::create_coverage_dummy_test.
   724# It is not an error to call this for a nonexistent test.
   725kube::golang::delete_coverage_dummy_test() {
   726  local package="$1"
   727  rm -f "$(kube::golang::path_for_coverage_dummy_test "${package}")"
   728}
   729
   730# Arguments: a list of kubernetes packages to build.
   731# Expected variables: ${build_args} should be set to an array of Go build arguments.
   732# In addition, ${package} and ${platform} should have been set earlier, and if
   733# ${KUBE_BUILD_WITH_COVERAGE} is set, coverage instrumentation will be enabled.
   734#
   735# Invokes Go to actually build some packages. If coverage is disabled, simply invokes
   736# go install. If coverage is enabled, builds covered binaries using go test, temporarily
   737# producing the required unit test files and then cleaning up after itself.
   738# Non-covered binaries are then built using go install as usual.
   739#
   740# See comments in kube::golang::setup_env regarding where built binaries go.
   741kube::golang::build_some_binaries() {
   742  if [[ -n "${KUBE_BUILD_WITH_COVERAGE:-}" ]]; then
   743    local -a uncovered=()
   744    for package in "$@"; do
   745      if kube::golang::is_instrumented_package "${package}"; then
   746        V=2 kube::log::info "Building ${package} with coverage..."
   747
   748        kube::golang::create_coverage_dummy_test "${package}"
   749        kube::util::trap_add "kube::golang::delete_coverage_dummy_test \"${package}\"" EXIT
   750
   751        go test -c -o "$(kube::golang::outfile_for_binary "${package}" "${platform}")" \
   752          -covermode count \
   753          -coverpkg k8s.io/... \
   754          "${build_args[@]}" \
   755          -tags coverage \
   756          "${package}"
   757      else
   758        uncovered+=("${package}")
   759      fi
   760    done
   761    if [[ "${#uncovered[@]}" != 0 ]]; then
   762      V=2 kube::log::info "Building ${uncovered[*]} without coverage..."
   763      GOPROXY=off go install "${build_args[@]}" "${uncovered[@]}"
   764    else
   765      V=2 kube::log::info "Nothing to build without coverage."
   766    fi
   767  else
   768    V=2 kube::log::info "Coverage is disabled."
   769    GOPROXY=off go install "${build_args[@]}" "$@"
   770  fi
   771}
   772
   773# Args:
   774#  $1: platform (e.g. darwin/amd64)
   775kube::golang::build_binaries_for_platform() {
   776  # This is for sanity.  Without it, user umasks can leak through.
   777  umask 0022
   778
   779  local platform=$1
   780
   781  local -a statics=()
   782  local -a nonstatics=()
   783  local -a tests=()
   784
   785  for binary in "${binaries[@]}"; do
   786    if [[ "${binary}" =~ ".test"$ ]]; then
   787      tests+=("${binary}")
   788      kube::log::info "    ${binary} (test)"
   789    elif kube::golang::is_statically_linked "${binary}"; then
   790      statics+=("${binary}")
   791      kube::log::info "    ${binary} (static)"
   792    else
   793      nonstatics+=("${binary}")
   794      kube::log::info "    ${binary} (non-static)"
   795    fi
   796   done
   797
   798  V=2 kube::log::info "Env for ${platform}: GOOS=${GOOS-} GOARCH=${GOARCH-} GOROOT=${GOROOT-} CGO_ENABLED=${CGO_ENABLED-} CC=${CC-}"
   799  V=3 kube::log::info "Building binaries with GCFLAGS=${gogcflags} LDFLAGS=${goldflags}"
   800
   801  local -a build_args
   802  if [[ "${#statics[@]}" != 0 ]]; then
   803    build_args=(
   804      -installsuffix=static
   805      ${goflags:+"${goflags[@]}"}
   806      -gcflags="${gogcflags}"
   807      -ldflags="${goldflags}"
   808      -tags="${gotags:-}"
   809    )
   810    CGO_ENABLED=0 kube::golang::build_some_binaries "${statics[@]}"
   811  fi
   812
   813  if [[ "${#nonstatics[@]}" != 0 ]]; then
   814    build_args=(
   815      ${goflags:+"${goflags[@]}"}
   816      -gcflags="${gogcflags}"
   817      -ldflags="${goldflags}"
   818      -tags="${gotags:-}"
   819    )
   820    kube::golang::build_some_binaries "${nonstatics[@]}"
   821  fi
   822
   823  for test in "${tests[@]:+${tests[@]}}"; do
   824    local outfile testpkg
   825    outfile=$(kube::golang::outfile_for_binary "${test}" "${platform}")
   826    testpkg=$(dirname "${test}")
   827
   828    mkdir -p "$(dirname "${outfile}")"
   829    go test -c \
   830      ${goflags:+"${goflags[@]}"} \
   831      -gcflags="${gogcflags}" \
   832      -ldflags="${goldflags}" \
   833      -tags="${gotags:-}" \
   834      -o "${outfile}" \
   835      "${testpkg}"
   836  done
   837}
   838
   839# Return approximate physical memory available in gigabytes.
   840kube::golang::get_physmem() {
   841  local mem
   842
   843  # Linux kernel version >=3.14, in kb
   844  if mem=$(grep MemAvailable /proc/meminfo | awk '{ print $2 }'); then
   845    echo $(( mem / 1048576 ))
   846    return
   847  fi
   848
   849  # Linux, in kb
   850  if mem=$(grep MemTotal /proc/meminfo | awk '{ print $2 }'); then
   851    echo $(( mem / 1048576 ))
   852    return
   853  fi
   854
   855  # OS X, in bytes. Note that get_physmem, as used, should only ever
   856  # run in a Linux container (because it's only used in the multiple
   857  # platform case, which is a Dockerized build), but this is provided
   858  # for completeness.
   859  if mem=$(sysctl -n hw.memsize 2>/dev/null); then
   860    echo $(( mem / 1073741824 ))
   861    return
   862  fi
   863
   864  # If we can't infer it, just give up and assume a low memory system
   865  echo 1
   866}
   867
   868# Build binaries targets specified
   869#
   870# Input:
   871#   $@ - targets and go flags.  If no targets are set then all binaries targets
   872#     are built.
   873#   KUBE_BUILD_PLATFORMS - Incoming variable of targets to build for.  If unset
   874#     then just the host architecture is built.
   875kube::golang::build_binaries() {
   876  # Create a sub-shell so that we don't pollute the outer environment
   877  (
   878    # Check for `go` binary and set ${GOPATH}.
   879    kube::golang::setup_env
   880    V=2 kube::log::info "Go version: $(GOFLAGS='' go version)"
   881
   882    local host_platform
   883    host_platform=$(kube::golang::host_platform)
   884
   885    # These are "local" but are visible to and relied on by functions this
   886    # function calls.  They are effectively part of the calling API to
   887    # build_binaries_for_platform.
   888    local goflags goldflags gogcflags gotags
   889
   890    goflags=()
   891    gogcflags="${GOGCFLAGS:-}"
   892    goldflags="all=$(kube::version::ldflags) ${GOLDFLAGS:-}"
   893
   894    if [[ "${DBG:-}" == 1 ]]; then
   895        # Debugging - disable optimizations and inlining and trimPath
   896        gogcflags="${gogcflags} all=-N -l"
   897    else
   898        # Not debugging - disable symbols and DWARF, trim embedded paths
   899        goldflags="${goldflags} -s -w"
   900        goflags+=("-trimpath")
   901    fi
   902
   903    # Extract tags if any specified in GOFLAGS
   904    gotags="selinux,notest,$(echo "${GOFLAGS:-}" | sed -ne 's|.*-tags=\([^-]*\).*|\1|p')"
   905
   906    local -a targets=()
   907    local arg
   908
   909    for arg; do
   910      if [[ "${arg}" == -* ]]; then
   911        # Assume arguments starting with a dash are flags to pass to go.
   912        goflags+=("${arg}")
   913      else
   914        targets+=("${arg}")
   915      fi
   916    done
   917
   918    local -a platforms
   919    IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS:-}"
   920    if [[ ${#platforms[@]} -eq 0 ]]; then
   921      platforms=("${host_platform}")
   922    fi
   923
   924    if [[ ${#targets[@]} -eq 0 ]]; then
   925      targets=("${KUBE_ALL_TARGETS[@]}")
   926    fi
   927    kube::util::read-array targets < <(kube::golang::dedup "${targets[@]}")
   928
   929    local -a binaries
   930    kube::util::read-array binaries < <(kube::golang::normalize_go_targets "${targets[@]}")
   931    kube::util::read-array binaries < <(kube::golang::dedup "${binaries[@]}")
   932
   933    local parallel=false
   934    if [[ ${#platforms[@]} -gt 1 ]]; then
   935      local gigs
   936      gigs=$(kube::golang::get_physmem)
   937
   938      if [[ ${gigs} -ge ${KUBE_PARALLEL_BUILD_MEMORY} ]]; then
   939        kube::log::status "Multiple platforms requested and available ${gigs}G >= threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in parallel"
   940        parallel=true
   941      else
   942        kube::log::status "Multiple platforms requested, but available ${gigs}G < threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in serial"
   943        parallel=false
   944      fi
   945    fi
   946
   947    if [[ "${parallel}" == "true" ]]; then
   948      kube::log::status "Building go targets for {${platforms[*]}} in parallel (output will appear in a burst when complete):" "${targets[@]}"
   949      local platform
   950      for platform in "${platforms[@]}"; do (
   951          kube::golang::set_platform_envs "${platform}"
   952          kube::log::status "${platform}: build started"
   953          kube::golang::build_binaries_for_platform "${platform}"
   954          kube::log::status "${platform}: build finished"
   955        ) &> "/tmp//${platform//\//_}.build" &
   956      done
   957
   958      local fails=0
   959      for job in $(jobs -p); do
   960        wait "${job}" || (( fails+=1 ))
   961      done
   962
   963      for platform in "${platforms[@]}"; do
   964        cat "/tmp//${platform//\//_}.build"
   965      done
   966
   967      exit "${fails}"
   968    else
   969      for platform in "${platforms[@]}"; do
   970        kube::log::status "Building go targets for ${platform}"
   971        (
   972          kube::golang::set_platform_envs "${platform}"
   973          kube::golang::build_binaries_for_platform "${platform}"
   974        )
   975      done
   976    fi
   977  )
   978}

View as plain text