github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/scripts/lib/golang.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  # The golang package that we are building.
    20  readonly KUBE_GO_PACKAGE=k8s.io/kubernetes
    21  readonly KUBE_GOPATH="${KUBE_OUTPUT}/go"
    22  
    23  # The server platform we are building on.
    24  readonly KUBE_SUPPORTED_SERVER_PLATFORMS=(
    25    linux/amd64
    26    linux/arm
    27    linux/arm64
    28    linux/s390x
    29    linux/ppc64le
    30  )
    31  
    32  # The node platforms we build for
    33  readonly KUBE_SUPPORTED_NODE_PLATFORMS=(
    34    linux/amd64
    35    linux/arm
    36    linux/arm64
    37    linux/s390x
    38    linux/ppc64le
    39    windows/amd64
    40  )
    41  
    42  # If we update this we should also update the set of platforms whose standard
    43  # library is precompiled for in build/build-image/cross/Dockerfile
    44  readonly KUBE_SUPPORTED_CLIENT_PLATFORMS=(
    45    linux/amd64
    46    linux/386
    47    linux/arm
    48    linux/arm64
    49    linux/s390x
    50    linux/ppc64le
    51    darwin/amd64
    52    darwin/386
    53    windows/amd64
    54    windows/386
    55  )
    56  
    57  # Which platforms we should compile test targets for.
    58  # Not all client platforms need these tests
    59  readonly KUBE_SUPPORTED_TEST_PLATFORMS=(
    60    linux/amd64
    61    linux/arm
    62    linux/arm64
    63    linux/s390x
    64    linux/ppc64le
    65    darwin/amd64
    66    windows/amd64
    67  )
    68  
    69  # The set of server targets that we are only building for Linux
    70  # If you update this list, please also update build/BUILD.
    71  kube::golang::server_targets() {
    72    local targets=(
    73      cmd/kube-proxy
    74      cmd/kube-apiserver
    75      cmd/kube-controller-manager
    76      cmd/kubelet
    77      cmd/kubeadm
    78      cmd/kube-scheduler
    79      vendor/k8s.io/apiextensions-apiserver
    80      cluster/gce/gci/mounter
    81    )
    82    echo "${targets[@]}"
    83  }
    84  
    85  IFS=" " read -ra KUBE_SERVER_TARGETS <<< "$(kube::golang::server_targets)"
    86  readonly KUBE_SERVER_TARGETS
    87  readonly KUBE_SERVER_BINARIES=("${KUBE_SERVER_TARGETS[@]##*/}")
    88  
    89  # The set of server targets we build docker images for
    90  kube::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    )
    98    echo "${targets[@]}"
    99  }
   100  
   101  IFS=" " read -ra KUBE_SERVER_IMAGE_TARGETS <<< "$(kube::golang::server_image_targets)"
   102  readonly KUBE_SERVER_IMAGE_TARGETS
   103  readonly KUBE_SERVER_IMAGE_BINARIES=("${KUBE_SERVER_IMAGE_TARGETS[@]##*/}")
   104  
   105  # The set of conformance targets we build docker image for
   106  kube::golang::conformance_image_targets() {
   107    # NOTE: this contains cmd targets for kube::release::build_conformance_image
   108    local targets=(
   109      vendor/github.com/onsi/ginkgo/ginkgo
   110      test/e2e/e2e.test
   111      cluster/images/conformance/go-runner
   112      cmd/kubectl
   113    )
   114    echo "${targets[@]}"
   115  }
   116  
   117  IFS=" " read -ra KUBE_CONFORMANCE_IMAGE_TARGETS <<< "$(kube::golang::conformance_image_targets)"
   118  readonly KUBE_CONFORMANCE_IMAGE_TARGETS
   119  
   120  # The set of server targets that we are only building for Kubernetes nodes
   121  # If you update this list, please also update build/BUILD.
   122  kube::golang::node_targets() {
   123    local targets=(
   124      cmd/kube-proxy
   125      cmd/kubeadm
   126      cmd/kubelet
   127    )
   128    echo "${targets[@]}"
   129  }
   130  
   131  IFS=" " read -ra KUBE_NODE_TARGETS <<< "$(kube::golang::node_targets)"
   132  readonly KUBE_NODE_TARGETS
   133  readonly KUBE_NODE_BINARIES=("${KUBE_NODE_TARGETS[@]##*/}")
   134  readonly KUBE_NODE_BINARIES_WIN=("${KUBE_NODE_BINARIES[@]/%/.exe}")
   135  
   136  # ------------
   137  # NOTE: All functions that return lists should use newlines.
   138  # bash functions can't return arrays, and spaces are tricky, so newline
   139  # separators are the preferred pattern.
   140  # To transform a string of newline-separated items to an array, use kube::util::read-array:
   141  # kube::util::read-array FOO < <(kube::golang::dups a b c a)
   142  #
   143  # ALWAYS remember to quote your subshells. Not doing so will break in
   144  # bash 4.3, and potentially cause other issues.
   145  # ------------
   146  
   147  # Returns a sorted newline-separated list containing only duplicated items.
   148  kube::golang::dups() {
   149    # We use printf to insert newlines, which are required by sort.
   150    printf "%s\n" "$@" | sort | uniq -d
   151  }
   152  
   153  # Returns a sorted newline-separated list with duplicated items removed.
   154  kube::golang::dedup() {
   155    # We use printf to insert newlines, which are required by sort.
   156    printf "%s\n" "$@" | sort -u
   157  }
   158  
   159  # Depends on values of user-facing KUBE_BUILD_PLATFORMS, KUBE_FASTBUILD,
   160  # and KUBE_BUILDER_OS.
   161  # Configures KUBE_SERVER_PLATFORMS, KUBE_NODE_PLATFOMRS,
   162  # KUBE_TEST_PLATFORMS, and KUBE_CLIENT_PLATFORMS, then sets them
   163  # to readonly.
   164  # The configured vars will only contain platforms allowed by the
   165  # KUBE_SUPPORTED* vars at the top of this file.
   166  declare -a KUBE_SERVER_PLATFORMS
   167  declare -a KUBE_CLIENT_PLATFORMS
   168  declare -a KUBE_NODE_PLATFORMS
   169  declare -a KUBE_TEST_PLATFORMS
   170  kube::golang::setup_platforms() {
   171    if [[ -n "${KUBE_BUILD_PLATFORMS:-}" ]]; then
   172      # KUBE_BUILD_PLATFORMS needs to be read into an array before the next
   173      # step, or quoting treats it all as one element.
   174      local -a platforms
   175      IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS}"
   176  
   177      # Deduplicate to ensure the intersection trick with kube::golang::dups
   178      # is not defeated by duplicates in user input.
   179      kube::util::read-array platforms < <(kube::golang::dedup "${platforms[@]}")
   180  
   181      # Use kube::golang::dups to restrict the builds to the platforms in
   182      # KUBE_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each
   183      # set, so if they appear twice after the merge they are in the intersection.
   184      kube::util::read-array KUBE_SERVER_PLATFORMS < <(kube::golang::dups \
   185          "${platforms[@]}" \
   186          "${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}" \
   187        )
   188      readonly KUBE_SERVER_PLATFORMS
   189  
   190      kube::util::read-array KUBE_NODE_PLATFORMS < <(kube::golang::dups \
   191          "${platforms[@]}" \
   192          "${KUBE_SUPPORTED_NODE_PLATFORMS[@]}" \
   193        )
   194      readonly KUBE_NODE_PLATFORMS
   195  
   196      kube::util::read-array KUBE_TEST_PLATFORMS < <(kube::golang::dups \
   197          "${platforms[@]}" \
   198          "${KUBE_SUPPORTED_TEST_PLATFORMS[@]}" \
   199        )
   200      readonly KUBE_TEST_PLATFORMS
   201  
   202      kube::util::read-array KUBE_CLIENT_PLATFORMS < <(kube::golang::dups \
   203          "${platforms[@]}" \
   204          "${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}" \
   205        )
   206      readonly KUBE_CLIENT_PLATFORMS
   207  
   208    elif [[ "${KUBE_FASTBUILD:-}" == "true" ]]; then
   209      KUBE_SERVER_PLATFORMS=(linux/amd64)
   210      readonly KUBE_SERVER_PLATFORMS
   211      KUBE_NODE_PLATFORMS=(linux/amd64)
   212      readonly KUBE_NODE_PLATFORMS
   213      if [[ "${KUBE_BUILDER_OS:-}" == "darwin"* ]]; then
   214        KUBE_TEST_PLATFORMS=(
   215          darwin/amd64
   216          linux/amd64
   217        )
   218        readonly KUBE_TEST_PLATFORMS
   219        KUBE_CLIENT_PLATFORMS=(
   220          darwin/amd64
   221          linux/amd64
   222        )
   223        readonly KUBE_CLIENT_PLATFORMS
   224      else
   225        KUBE_TEST_PLATFORMS=(linux/amd64)
   226        readonly KUBE_TEST_PLATFORMS
   227        KUBE_CLIENT_PLATFORMS=(linux/amd64)
   228        readonly KUBE_CLIENT_PLATFORMS
   229      fi
   230    else
   231      KUBE_SERVER_PLATFORMS=("${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}")
   232      readonly KUBE_SERVER_PLATFORMS
   233  
   234      KUBE_NODE_PLATFORMS=("${KUBE_SUPPORTED_NODE_PLATFORMS[@]}")
   235      readonly KUBE_NODE_PLATFORMS
   236  
   237      KUBE_CLIENT_PLATFORMS=("${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}")
   238      readonly KUBE_CLIENT_PLATFORMS
   239  
   240      KUBE_TEST_PLATFORMS=("${KUBE_SUPPORTED_TEST_PLATFORMS[@]}")
   241      readonly KUBE_TEST_PLATFORMS
   242    fi
   243  }
   244  
   245  kube::golang::setup_platforms
   246  
   247  # The set of client targets that we are building for all platforms
   248  # If you update this list, please also update build/BUILD.
   249  readonly KUBE_CLIENT_TARGETS=(
   250    cmd/kubectl
   251  )
   252  readonly KUBE_CLIENT_BINARIES=("${KUBE_CLIENT_TARGETS[@]##*/}")
   253  readonly KUBE_CLIENT_BINARIES_WIN=("${KUBE_CLIENT_BINARIES[@]/%/.exe}")
   254  
   255  # The set of test targets that we are building for all platforms
   256  # If you update this list, please also update build/BUILD.
   257  kube::golang::test_targets() {
   258    local targets=(
   259      cmd/gendocs
   260      cmd/genkubedocs
   261      cmd/genman
   262      cmd/genyaml
   263      cmd/genswaggertypedocs
   264      cmd/linkcheck
   265      vendor/github.com/onsi/ginkgo/ginkgo
   266      test/e2e/e2e.test
   267      cluster/images/conformance/go-runner
   268    )
   269    echo "${targets[@]}"
   270  }
   271  IFS=" " read -ra KUBE_TEST_TARGETS <<< "$(kube::golang::test_targets)"
   272  readonly KUBE_TEST_TARGETS
   273  readonly KUBE_TEST_BINARIES=("${KUBE_TEST_TARGETS[@]##*/}")
   274  readonly KUBE_TEST_BINARIES_WIN=("${KUBE_TEST_BINARIES[@]/%/.exe}")
   275  # If you update this list, please also update build/BUILD.
   276  readonly KUBE_TEST_PORTABLE=(
   277    test/e2e/testing-manifests
   278    test/kubemark
   279    hack/e2e-internal
   280    hack/get-build.sh
   281    hack/ginkgo-e2e.sh
   282    hack/lib
   283  )
   284  
   285  # Test targets which run on the Kubernetes clusters directly, so we only
   286  # need to target server platforms.
   287  # These binaries will be distributed in the kubernetes-test tarball.
   288  # If you update this list, please also update build/BUILD.
   289  kube::golang::server_test_targets() {
   290    local targets=(
   291      cmd/kubemark
   292      vendor/github.com/onsi/ginkgo/ginkgo
   293    )
   294  
   295    if [[ "${OSTYPE:-}" == "linux"* ]]; then
   296      targets+=( test/e2e_node/e2e_node.test )
   297    fi
   298  
   299    echo "${targets[@]}"
   300  }
   301  
   302  IFS=" " read -ra KUBE_TEST_SERVER_TARGETS <<< "$(kube::golang::server_test_targets)"
   303  readonly KUBE_TEST_SERVER_TARGETS
   304  readonly KUBE_TEST_SERVER_BINARIES=("${KUBE_TEST_SERVER_TARGETS[@]##*/}")
   305  readonly KUBE_TEST_SERVER_PLATFORMS=("${KUBE_SERVER_PLATFORMS[@]:+"${KUBE_SERVER_PLATFORMS[@]}"}")
   306  
   307  # Gigabytes necessary for parallel platform builds.
   308  # As of January 2018, RAM usage is exceeding 30G
   309  # Setting to 40 to provide some headroom
   310  readonly KUBE_PARALLEL_BUILD_MEMORY=40
   311  
   312  readonly KUBE_ALL_TARGETS=(
   313    "${KUBE_SERVER_TARGETS[@]}"
   314    "${KUBE_CLIENT_TARGETS[@]}"
   315    "${KUBE_TEST_TARGETS[@]}"
   316    "${KUBE_TEST_SERVER_TARGETS[@]}"
   317  )
   318  readonly KUBE_ALL_BINARIES=("${KUBE_ALL_TARGETS[@]##*/}")
   319  
   320  readonly KUBE_STATIC_LIBRARIES=(
   321    kube-apiserver
   322    kube-controller-manager
   323    kube-scheduler
   324    kube-proxy
   325    kubeadm
   326    kubectl
   327  )
   328  
   329  # Fully-qualified package names that we want to instrument for coverage information.
   330  readonly KUBE_COVERAGE_INSTRUMENTED_PACKAGES=(
   331    k8s.io/kubernetes/cmd/kube-apiserver
   332    k8s.io/kubernetes/cmd/kube-controller-manager
   333    k8s.io/kubernetes/cmd/kube-scheduler
   334    k8s.io/kubernetes/cmd/kube-proxy
   335    k8s.io/kubernetes/cmd/kubelet
   336  )
   337  
   338  # KUBE_CGO_OVERRIDES is a space-separated list of binaries which should be built
   339  # with CGO enabled, assuming CGO is supported on the target platform.
   340  # This overrides any entry in KUBE_STATIC_LIBRARIES.
   341  IFS=" " read -ra KUBE_CGO_OVERRIDES <<< "${KUBE_CGO_OVERRIDES:-}"
   342  readonly KUBE_CGO_OVERRIDES
   343  # KUBE_STATIC_OVERRIDES is a space-separated list of binaries which should be
   344  # built with CGO disabled. This is in addition to the list in
   345  # KUBE_STATIC_LIBRARIES.
   346  IFS=" " read -ra KUBE_STATIC_OVERRIDES <<< "${KUBE_STATIC_OVERRIDES:-}"
   347  readonly KUBE_STATIC_OVERRIDES
   348  
   349  kube::golang::is_statically_linked_library() {
   350    local e
   351    # Explicitly enable cgo when building kubectl for darwin from darwin.
   352    [[ "$(go env GOHOSTOS)" == "darwin" && "$(go env GOOS)" == "darwin" &&
   353      "$1" == *"/kubectl" ]] && return 1
   354    if [[ -n "${KUBE_CGO_OVERRIDES:+x}" ]]; then
   355      for e in "${KUBE_CGO_OVERRIDES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 1; done;
   356    fi
   357    for e in "${KUBE_STATIC_LIBRARIES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done;
   358    if [[ -n "${KUBE_STATIC_OVERRIDES:+x}" ]]; then
   359      for e in "${KUBE_STATIC_OVERRIDES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done;
   360    fi
   361    return 1;
   362  }
   363  
   364  # kube::binaries_from_targets take a list of build targets and return the
   365  # full go package to be built
   366  kube::golang::binaries_from_targets() {
   367    local target
   368    for target; do
   369      # If the target starts with what looks like a domain name, assume it has a
   370      # fully-qualified package name rather than one that needs the Kubernetes
   371      # package prepended.
   372      if [[ "${target}" =~ ^([[:alnum:]]+".")+[[:alnum:]]+"/" ]]; then
   373        echo "${target}"
   374      else
   375        echo "${KUBE_GO_PACKAGE}/${target}"
   376      fi
   377    done
   378  }
   379  
   380  # Asks golang what it thinks the host platform is. The go tool chain does some
   381  # slightly different things when the target platform matches the host platform.
   382  kube::golang::host_platform() {
   383    echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)"
   384  }
   385  
   386  # Takes the platform name ($1) and sets the appropriate golang env variables
   387  # for that platform.
   388  kube::golang::set_platform_envs() {
   389    [[ -n ${1-} ]] || {
   390      kube::log::error_exit "!!! Internal error. No platform set in kube::golang::set_platform_envs"
   391    }
   392  
   393    export GOOS=${platform%/*}
   394    export GOARCH=${platform##*/}
   395  
   396    # Do not set CC when building natively on a platform, only if cross-compiling from linux/amd64
   397    if [[ $(kube::golang::host_platform) == "linux/amd64" ]]; then
   398      # Dynamic CGO linking for other server architectures than linux/amd64 goes here
   399      # If you want to include support for more server platforms than these, add arch-specific gcc names here
   400      case "${platform}" in
   401        "linux/arm")
   402          export CGO_ENABLED=1
   403          export CC=arm-linux-gnueabihf-gcc
   404          ;;
   405        "linux/arm64")
   406          export CGO_ENABLED=1
   407          export CC=aarch64-linux-gnu-gcc
   408          ;;
   409        "linux/ppc64le")
   410          export CGO_ENABLED=1
   411          export CC=powerpc64le-linux-gnu-gcc
   412          ;;
   413        "linux/s390x")
   414          export CGO_ENABLED=1
   415          export CC=s390x-linux-gnu-gcc
   416          ;;
   417      esac
   418    fi
   419  }
   420  
   421  kube::golang::unset_platform_envs() {
   422    unset GOOS
   423    unset GOARCH
   424    unset GOROOT
   425    unset CGO_ENABLED
   426    unset CC
   427  }
   428  
   429  # Create the GOPATH tree under $KUBE_OUTPUT
   430  kube::golang::create_gopath_tree() {
   431    local go_pkg_dir="${KUBE_GOPATH}/src/${KUBE_GO_PACKAGE}"
   432    local go_pkg_basedir
   433    go_pkg_basedir=$(dirname "${go_pkg_dir}")
   434  
   435    mkdir -p "${go_pkg_basedir}"
   436  
   437    # TODO: This symlink should be relative.
   438    if [[ ! -e "${go_pkg_dir}" || "$(readlink "${go_pkg_dir}")" != "${KUBE_ROOT}" ]]; then
   439      ln -snf "${KUBE_ROOT}" "${go_pkg_dir}"
   440    fi
   441  
   442    # Using bazel with a recursive target (e.g. bazel test ...) will abort due to
   443    # the symlink loop created in this function, so create this special file which
   444    # tells bazel not to follow the symlink.
   445    touch "${go_pkg_basedir}/DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN"
   446    # Additionally, the //:package-srcs glob recursively includes all
   447    # subdirectories, and similarly fails due to the symlink loop. By creating a
   448    # BUILD.bazel file, we effectively create a dummy package, which stops the
   449    # glob from descending further into the tree and hitting the loop.
   450    cat >"${KUBE_GOPATH}/BUILD.bazel" <<EOF
   451  # This dummy BUILD file prevents Bazel from trying to descend through the
   452  # infinite loop created by the symlink at
   453  # ${go_pkg_dir}
   454  EOF
   455  }
   456  
   457  # Ensure the go tool exists and is a viable version.
   458  kube::golang::verify_go_version() {
   459    if [[ -z "$(command -v go)" ]]; then
   460      kube::log::usage_from_stdin <<EOF
   461  Can't find 'go' in PATH, please fix and retry.
   462  See http://golang.org/doc/install for installation instructions.
   463  EOF
   464      return 2
   465    fi
   466  
   467    local go_version
   468    IFS=" " read -ra go_version <<< "$(go version)"
   469    local minimum_go_version
   470    minimum_go_version=go1.13.4
   471    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
   472      kube::log::usage_from_stdin <<EOF
   473  Detected go version: ${go_version[*]}.
   474  Kubernetes requires ${minimum_go_version} or greater.
   475  Please install ${minimum_go_version} or later.
   476  EOF
   477      return 2
   478    fi
   479  }
   480  
   481  # kube::golang::setup_env will check that the `go` commands is available in
   482  # ${PATH}. It will also check that the Go version is good enough for the
   483  # Kubernetes build.
   484  #
   485  # Inputs:
   486  #   KUBE_EXTRA_GOPATH - If set, this is included in created GOPATH
   487  #
   488  # Outputs:
   489  #   env-var GOPATH points to our local output dir
   490  #   env-var GOBIN is unset (we want binaries in a predictable place)
   491  #   env-var GO15VENDOREXPERIMENT=1
   492  #   current directory is within GOPATH
   493  kube::golang::setup_env() {
   494    kube::golang::verify_go_version
   495  
   496    kube::golang::create_gopath_tree
   497  
   498    export GOPATH="${KUBE_GOPATH}"
   499    export GOCACHE="${KUBE_GOPATH}/cache"
   500  
   501    # Append KUBE_EXTRA_GOPATH to the GOPATH if it is defined.
   502    if [[ -n ${KUBE_EXTRA_GOPATH:-} ]]; then
   503      GOPATH="${GOPATH}:${KUBE_EXTRA_GOPATH}"
   504    fi
   505  
   506    # Make sure our own Go binaries are in PATH.
   507    export PATH="${KUBE_GOPATH}/bin:${PATH}"
   508  
   509    # Change directories so that we are within the GOPATH.  Some tools get really
   510    # upset if this is not true.  We use a whole fake GOPATH here to collect the
   511    # resultant binaries.  Go will not let us use GOBIN with `go install` and
   512    # cross-compiling, and `go install -o <file>` only works for a single pkg.
   513    local subdir
   514    subdir=$(kube::realpath . | sed "s|${KUBE_ROOT}||")
   515    cd "${KUBE_GOPATH}/src/${KUBE_GO_PACKAGE}/${subdir}" || return 1
   516  
   517    # Set GOROOT so binaries that parse code can work properly.
   518    GOROOT=$(go env GOROOT)
   519    export GOROOT
   520  
   521    # Unset GOBIN in case it already exists in the current session.
   522    unset GOBIN
   523  
   524    # This seems to matter to some tools
   525    export GO15VENDOREXPERIMENT=1
   526  }
   527  
   528  # This will take binaries from $GOPATH/bin and copy them to the appropriate
   529  # place in ${KUBE_OUTPUT_BINDIR}
   530  #
   531  # Ideally this wouldn't be necessary and we could just set GOBIN to
   532  # KUBE_OUTPUT_BINDIR but that won't work in the face of cross compilation.  'go
   533  # install' will place binaries that match the host platform directly in $GOBIN
   534  # while placing cross compiled binaries into `platform_arch` subdirs.  This
   535  # complicates pretty much everything else we do around packaging and such.
   536  kube::golang::place_bins() {
   537    local host_platform
   538    host_platform=$(kube::golang::host_platform)
   539  
   540    V=2 kube::log::status "Placing binaries"
   541  
   542    local platform
   543    for platform in "${KUBE_CLIENT_PLATFORMS[@]}"; do
   544      # The substitution on platform_src below will replace all slashes with
   545      # underscores.  It'll transform darwin/amd64 -> darwin_amd64.
   546      local platform_src="/${platform//\//_}"
   547      if [[ "${platform}" == "${host_platform}" ]]; then
   548        platform_src=""
   549        rm -f "${THIS_PLATFORM_BIN}"
   550        ln -s "${KUBE_OUTPUT_BINPATH}/${platform}" "${THIS_PLATFORM_BIN}"
   551      fi
   552  
   553      local full_binpath_src="${KUBE_GOPATH}/bin${platform_src}"
   554      if [[ -d "${full_binpath_src}" ]]; then
   555        mkdir -p "${KUBE_OUTPUT_BINPATH}/${platform}"
   556        find "${full_binpath_src}" -maxdepth 1 -type f -exec \
   557          rsync -pc {} "${KUBE_OUTPUT_BINPATH}/${platform}" \;
   558      fi
   559    done
   560  }
   561  
   562  # Try and replicate the native binary placement of go install without
   563  # calling go install.
   564  kube::golang::outfile_for_binary() {
   565    local binary=$1
   566    local platform=$2
   567    local output_path="${KUBE_GOPATH}/bin"
   568    local bin
   569    bin=$(basename "${binary}")
   570    if [[ "${platform}" != "${host_platform}" ]]; then
   571      output_path="${output_path}/${platform//\//_}"
   572    fi
   573    if [[ ${GOOS} == "windows" ]]; then
   574      bin="${bin}.exe"
   575    fi
   576    echo "${output_path}/${bin}"
   577  }
   578  
   579  # Argument: the name of a Kubernetes package.
   580  # Returns 0 if the binary can be built with coverage, 1 otherwise.
   581  # NB: this ignores whether coverage is globally enabled or not.
   582  kube::golang::is_instrumented_package() {
   583    kube::util::array_contains "$1" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"
   584    return $?
   585  }
   586  
   587  # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler)
   588  # Echos the path to a dummy test used for coverage information.
   589  kube::golang::path_for_coverage_dummy_test() {
   590    local package="$1"
   591    local path="${KUBE_GOPATH}/src/${package}"
   592    local name
   593    name=$(basename "${package}")
   594    echo "${path}/zz_generated_${name}_test.go"
   595  }
   596  
   597  # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler).
   598  # Creates a dummy unit test on disk in the source directory for the given package.
   599  # This unit test will invoke the package's standard entry point when run.
   600  kube::golang::create_coverage_dummy_test() {
   601    local package="$1"
   602    local name
   603    name="$(basename "${package}")"
   604    cat <<EOF > "$(kube::golang::path_for_coverage_dummy_test "${package}")"
   605  package main
   606  import (
   607    "testing"
   608    "k8s.io/kubernetes/pkg/util/coverage"
   609  )
   610  
   611  func TestMain(m *testing.M) {
   612    // Get coverage running
   613    coverage.InitCoverage("${name}")
   614  
   615    // Go!
   616    main()
   617  
   618    // Make sure we actually write the profiling information to disk, if we make it here.
   619    // On long-running services, or anything that calls os.Exit(), this is insufficient,
   620    // so we also flush periodically with a default period of five seconds (configurable by
   621    // the KUBE_COVERAGE_FLUSH_INTERVAL environment variable).
   622    coverage.FlushCoverage()
   623  }
   624  EOF
   625  }
   626  
   627  # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler).
   628  # Deletes a test generated by kube::golang::create_coverage_dummy_test.
   629  # It is not an error to call this for a nonexistent test.
   630  kube::golang::delete_coverage_dummy_test() {
   631    local package="$1"
   632    rm -f "$(kube::golang::path_for_coverage_dummy_test "${package}")"
   633  }
   634  
   635  # Arguments: a list of kubernetes packages to build.
   636  # Expected variables: ${build_args} should be set to an array of Go build arguments.
   637  # In addition, ${package} and ${platform} should have been set earlier, and if
   638  # ${KUBE_BUILD_WITH_COVERAGE} is set, coverage instrumentation will be enabled.
   639  #
   640  # Invokes Go to actually build some packages. If coverage is disabled, simply invokes
   641  # go install. If coverage is enabled, builds covered binaries using go test, temporarily
   642  # producing the required unit test files and then cleaning up after itself.
   643  # Non-covered binaries are then built using go install as usual.
   644  kube::golang::build_some_binaries() {
   645    if [[ -n "${KUBE_BUILD_WITH_COVERAGE:-}" ]]; then
   646      local -a uncovered=()
   647      for package in "$@"; do
   648        if kube::golang::is_instrumented_package "${package}"; then
   649          V=2 kube::log::info "Building ${package} with coverage..."
   650  
   651          kube::golang::create_coverage_dummy_test "${package}"
   652          kube::util::trap_add "kube::golang::delete_coverage_dummy_test \"${package}\"" EXIT
   653  
   654          go test -c -o "$(kube::golang::outfile_for_binary "${package}" "${platform}")" \
   655            -covermode count \
   656            -coverpkg k8s.io/...,k8s.io/kubernetes/vendor/k8s.io/... \
   657            "${build_args[@]}" \
   658            -tags coverage \
   659            "${package}"
   660        else
   661          uncovered+=("${package}")
   662        fi
   663      done
   664      if [[ "${#uncovered[@]}" != 0 ]]; then
   665        V=2 kube::log::info "Building ${uncovered[*]} without coverage..."
   666        go install "${build_args[@]}" "${uncovered[@]}"
   667      else
   668        V=2 kube::log::info "Nothing to build without coverage."
   669       fi
   670     else
   671      V=2 kube::log::info "Coverage is disabled."
   672      go install "${build_args[@]}" "$@"
   673     fi
   674  }
   675  
   676  kube::golang::build_binaries_for_platform() {
   677    local platform=$1
   678  
   679    local -a statics=()
   680    local -a nonstatics=()
   681    local -a tests=()
   682  
   683    V=2 kube::log::info "Env for ${platform}: GOOS=${GOOS-} GOARCH=${GOARCH-} GOROOT=${GOROOT-} CGO_ENABLED=${CGO_ENABLED-} CC=${CC-}"
   684  
   685    for binary in "${binaries[@]}"; do
   686      if [[ "${binary}" =~ ".test"$ ]]; then
   687        tests+=("${binary}")
   688      elif kube::golang::is_statically_linked_library "${binary}"; then
   689        statics+=("${binary}")
   690      else
   691        nonstatics+=("${binary}")
   692      fi
   693    done
   694  
   695    local -a build_args
   696    if [[ "${#statics[@]}" != 0 ]]; then
   697      build_args=(
   698        -installsuffix static
   699        ${goflags:+"${goflags[@]}"}
   700        -gcflags "${gogcflags:-}"
   701        -asmflags "${goasmflags:-}"
   702        -ldflags "${goldflags:-}"
   703        -tags "${gotags:-}"
   704      )
   705      CGO_ENABLED=0 kube::golang::build_some_binaries "${statics[@]}"
   706    fi
   707  
   708    if [[ "${#nonstatics[@]}" != 0 ]]; then
   709      build_args=(
   710        ${goflags:+"${goflags[@]}"}
   711        -gcflags "${gogcflags:-}"
   712        -asmflags "${goasmflags:-}"
   713        -ldflags "${goldflags:-}"
   714        -tags "${gotags:-}"
   715      )
   716      kube::golang::build_some_binaries "${nonstatics[@]}"
   717    fi
   718  
   719    for test in "${tests[@]:+${tests[@]}}"; do
   720      local outfile testpkg
   721      outfile=$(kube::golang::outfile_for_binary "${test}" "${platform}")
   722      testpkg=$(dirname "${test}")
   723  
   724      mkdir -p "$(dirname "${outfile}")"
   725      go test -c \
   726        ${goflags:+"${goflags[@]}"} \
   727        -gcflags "${gogcflags:-}" \
   728        -asmflags "${goasmflags:-}" \
   729        -ldflags "${goldflags:-}" \
   730        -tags "${gotags:-}" \
   731        -o "${outfile}" \
   732        "${testpkg}"
   733    done
   734  }
   735  
   736  # Return approximate physical memory available in gigabytes.
   737  kube::golang::get_physmem() {
   738    local mem
   739  
   740    # Linux kernel version >=3.14, in kb
   741    if mem=$(grep MemAvailable /proc/meminfo | awk '{ print $2 }'); then
   742      echo $(( mem / 1048576 ))
   743      return
   744    fi
   745  
   746    # Linux, in kb
   747    if mem=$(grep MemTotal /proc/meminfo | awk '{ print $2 }'); then
   748      echo $(( mem / 1048576 ))
   749      return
   750    fi
   751  
   752    # OS X, in bytes. Note that get_physmem, as used, should only ever
   753    # run in a Linux container (because it's only used in the multiple
   754    # platform case, which is a Dockerized build), but this is provided
   755    # for completeness.
   756    if mem=$(sysctl -n hw.memsize 2>/dev/null); then
   757      echo $(( mem / 1073741824 ))
   758      return
   759    fi
   760  
   761    # If we can't infer it, just give up and assume a low memory system
   762    echo 1
   763  }
   764  
   765  # Build binaries targets specified
   766  #
   767  # Input:
   768  #   $@ - targets and go flags.  If no targets are set then all binaries targets
   769  #     are built.
   770  #   KUBE_BUILD_PLATFORMS - Incoming variable of targets to build for.  If unset
   771  #     then just the host architecture is built.
   772  kube::golang::build_binaries() {
   773    # Create a sub-shell so that we don't pollute the outer environment
   774    (
   775      # Check for `go` binary and set ${GOPATH}.
   776      kube::golang::setup_env
   777      V=2 kube::log::info "Go version: $(go version)"
   778  
   779      local host_platform
   780      host_platform=$(kube::golang::host_platform)
   781  
   782      local goflags goldflags goasmflags gogcflags gotags
   783      # If GOLDFLAGS is unset, then set it to the a default of "-s -w".
   784      # Disable SC2153 for this, as it will throw a warning that the local
   785      # variable goldflags will exist, and it suggest changing it to this.
   786      # shellcheck disable=SC2153
   787      goldflags="${GOLDFLAGS=-s -w} $(kube::version::ldflags)"
   788      goasmflags="-trimpath=${KUBE_ROOT}"
   789      gogcflags="${GOGCFLAGS:-} -trimpath=${KUBE_ROOT}"
   790  
   791      # extract tags if any specified in GOFLAGS
   792      # shellcheck disable=SC2001
   793      gotags="selinux,$(echo "${GOFLAGS:-}" | sed -e 's|.*-tags=\([^-]*\).*|\1|')"
   794  
   795      local -a targets=()
   796      local arg
   797  
   798      for arg; do
   799        if [[ "${arg}" == -* ]]; then
   800          # Assume arguments starting with a dash are flags to pass to go.
   801          goflags+=("${arg}")
   802        else
   803          targets+=("${arg}")
   804        fi
   805      done
   806  
   807      if [[ ${#targets[@]} -eq 0 ]]; then
   808        targets=("${KUBE_ALL_TARGETS[@]}")
   809      fi
   810  
   811      local -a platforms
   812      IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS:-}"
   813      if [[ ${#platforms[@]} -eq 0 ]]; then
   814        platforms=("${host_platform}")
   815      fi
   816  
   817      local -a binaries
   818      while IFS="" read -r binary; do binaries+=("$binary"); done < <(kube::golang::binaries_from_targets "${targets[@]}")
   819  
   820      local parallel=false
   821      if [[ ${#platforms[@]} -gt 1 ]]; then
   822        local gigs
   823        gigs=$(kube::golang::get_physmem)
   824  
   825        if [[ ${gigs} -ge ${KUBE_PARALLEL_BUILD_MEMORY} ]]; then
   826          kube::log::status "Multiple platforms requested and available ${gigs}G >= threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in parallel"
   827          parallel=true
   828        else
   829          kube::log::status "Multiple platforms requested, but available ${gigs}G < threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in serial"
   830          parallel=false
   831        fi
   832      fi
   833  
   834      if [[ "${parallel}" == "true" ]]; then
   835        kube::log::status "Building go targets for {${platforms[*]}} in parallel (output will appear in a burst when complete):" "${targets[@]}"
   836        local platform
   837        for platform in "${platforms[@]}"; do (
   838            kube::golang::set_platform_envs "${platform}"
   839            kube::log::status "${platform}: build started"
   840            kube::golang::build_binaries_for_platform "${platform}"
   841            kube::log::status "${platform}: build finished"
   842          ) &> "/tmp//${platform//\//_}.build" &
   843        done
   844  
   845        local fails=0
   846        for job in $(jobs -p); do
   847          wait "${job}" || (( fails+=1 ))
   848        done
   849  
   850        for platform in "${platforms[@]}"; do
   851          cat "/tmp//${platform//\//_}.build"
   852        done
   853  
   854        exit ${fails}
   855      else
   856        for platform in "${platforms[@]}"; do
   857          kube::log::status "Building go targets for ${platform}:" "${targets[@]}"
   858          (
   859            kube::golang::set_platform_envs "${platform}"
   860            kube::golang::build_binaries_for_platform "${platform}"
   861          )
   862        done
   863      fi
   864    )
   865  }