k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/hack/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 readonly KUBE_GOPATH="${KUBE_GOPATH:-"${KUBE_OUTPUT}/go"}" 20 export KUBE_GOPATH 21 22 # The server platform we are building on. 23 readonly 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 31 readonly 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 41 readonly 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 57 readonly 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 69 kube::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 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 cmd/kubectl 98 ) 99 echo "${targets[@]}" 100 } 101 102 IFS=" " read -ra KUBE_SERVER_IMAGE_TARGETS <<< "$(kube::golang::server_image_targets)" 103 readonly KUBE_SERVER_IMAGE_TARGETS 104 readonly KUBE_SERVER_IMAGE_BINARIES=("${KUBE_SERVER_IMAGE_TARGETS[@]##*/}") 105 106 # The set of conformance targets we build docker image for 107 kube::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 118 IFS=" " read -ra KUBE_CONFORMANCE_IMAGE_TARGETS <<< "$(kube::golang::conformance_image_targets)" 119 readonly KUBE_CONFORMANCE_IMAGE_TARGETS 120 121 # The set of server targets that we are only building for Kubernetes nodes 122 kube::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 132 IFS=" " read -ra KUBE_NODE_TARGETS <<< "$(kube::golang::node_targets)" 133 readonly KUBE_NODE_TARGETS 134 readonly KUBE_NODE_BINARIES=("${KUBE_NODE_TARGETS[@]##*/}") 135 readonly 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. 149 kube::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. 155 kube::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. 167 declare -a KUBE_SERVER_PLATFORMS 168 declare -a KUBE_CLIENT_PLATFORMS 169 declare -a KUBE_NODE_PLATFORMS 170 declare -a KUBE_TEST_PLATFORMS 171 kube::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 251 kube::golang::setup_platforms 252 253 # The set of client targets that we are building for all platforms 254 readonly KUBE_CLIENT_TARGETS=( 255 cmd/kubectl 256 cmd/kubectl-convert 257 ) 258 readonly KUBE_CLIENT_BINARIES=("${KUBE_CLIENT_TARGETS[@]##*/}") 259 readonly KUBE_CLIENT_BINARIES_WIN=("${KUBE_CLIENT_BINARIES[@]/%/.exe}") 260 261 # The set of test targets that we are building for all platforms 262 kube::golang::test_targets() { 263 local targets=( 264 ginkgo 265 test/e2e/e2e.test 266 test/conformance/image/go-runner 267 ) 268 echo "${targets[@]}" 269 } 270 IFS=" " read -ra KUBE_TEST_TARGETS <<< "$(kube::golang::test_targets)" 271 readonly KUBE_TEST_TARGETS 272 readonly KUBE_TEST_BINARIES=("${KUBE_TEST_TARGETS[@]##*/}") 273 readonly KUBE_TEST_BINARIES_WIN=("${KUBE_TEST_BINARIES[@]/%/.exe}") 274 readonly 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. 286 kube::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 299 IFS=" " read -ra KUBE_TEST_SERVER_TARGETS <<< "$(kube::golang::server_test_targets)" 300 readonly KUBE_TEST_SERVER_TARGETS 301 readonly KUBE_TEST_SERVER_BINARIES=("${KUBE_TEST_SERVER_TARGETS[@]##*/}") 302 readonly 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. 309 readonly KUBE_PARALLEL_BUILD_MEMORY=${KUBE_PARALLEL_BUILD_MEMORY:-20} 310 311 readonly KUBE_ALL_TARGETS=( 312 "${KUBE_SERVER_TARGETS[@]}" 313 "${KUBE_CLIENT_TARGETS[@]}" 314 "${KUBE_TEST_TARGETS[@]}" 315 "${KUBE_TEST_SERVER_TARGETS[@]}" 316 ) 317 readonly KUBE_ALL_BINARIES=("${KUBE_ALL_TARGETS[@]##*/}") 318 319 readonly 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. 335 readonly 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. 346 IFS=" " read -ra KUBE_CGO_OVERRIDES_LIST <<< "${KUBE_CGO_OVERRIDES:-}" 347 readonly 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. 351 IFS=" " read -ra KUBE_STATIC_OVERRIDES_LIST <<< "${KUBE_STATIC_OVERRIDES:-}" 352 readonly KUBE_STATIC_OVERRIDES_LIST 353 354 kube::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. 373 kube::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. 423 kube::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. 454 kube::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. 460 kube::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 508 kube::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 532 Can't find 'go' in PATH, please fix and retry. 533 See http://golang.org/doc/install for installation instructions. 534 EOF 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 544 Detected go version: ${go_version[*]}. 545 Kubernetes requires ${minimum_go_version} or greater. 546 Please install ${minimum_go_version} or later. 547 EOF 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 560 kube::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 597 kube::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. 620 kube::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 V=3 kube::log::status "Placing binaries for ${platform} in ${KUBE_OUTPUT_BIN}/${platform}" 639 local full_binpath_src="${KUBE_GOPATH}/bin${platform_src}" 640 if [[ -d "${full_binpath_src}" ]]; then 641 mkdir -p "${KUBE_OUTPUT_BIN}/${platform}" 642 find "${full_binpath_src}" -maxdepth 1 -type f -exec \ 643 rsync -pc {} "${KUBE_OUTPUT_BIN}/${platform}" \; 644 fi 645 done 646 } 647 648 # Try and replicate the native binary placement of go install without 649 # calling go install. 650 kube::golang::outfile_for_binary() { 651 local binary=$1 652 local platform=$2 653 local output_path="${KUBE_GOPATH}/bin" 654 local bin 655 bin=$(basename "${binary}") 656 if [[ "${platform}" != "${host_platform}" ]]; then 657 output_path="${output_path}/${platform//\//_}" 658 fi 659 if [[ ${GOOS} == "windows" ]]; then 660 bin="${bin}.exe" 661 fi 662 echo "${output_path}/${bin}" 663 } 664 665 # Argument: the name of a Kubernetes package. 666 # Returns 0 if the binary can be built with coverage, 1 otherwise. 667 # NB: this ignores whether coverage is globally enabled or not. 668 kube::golang::is_instrumented_package() { 669 if kube::util::array_contains "$1" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"; then 670 return 0 671 fi 672 # Some cases, like `make kubectl`, pass $1 as "./cmd/kubectl" rather than 673 # "k8s.io/kubernetes/kubectl". Try to normalize and handle that. We don't 674 # do this always because it is a bit slow. 675 pkg=$(go list -find "$1") 676 if kube::util::array_contains "${pkg}" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"; then 677 return 0 678 fi 679 return 1 680 } 681 682 # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler) 683 # Echos the path to a dummy test used for coverage information. 684 kube::golang::path_for_coverage_dummy_test() { 685 local package="$1" 686 local path 687 path=$(go list -find -f '{{.Dir}}' "${package}") 688 local name 689 name=$(basename "${package}") 690 echo "${path}/zz_generated_${name}_test.go" 691 } 692 693 # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler). 694 # Creates a dummy unit test on disk in the source directory for the given package. 695 # This unit test will invoke the package's standard entry point when run. 696 kube::golang::create_coverage_dummy_test() { 697 local package="$1" 698 local name 699 name="$(basename "${package}")" 700 cat <<EOF > "$(kube::golang::path_for_coverage_dummy_test "${package}")" 701 package main 702 import ( 703 "testing" 704 "k8s.io/kubernetes/pkg/util/coverage" 705 ) 706 707 func TestMain(m *testing.M) { 708 // Get coverage running 709 coverage.InitCoverage("${name}") 710 711 // Go! 712 main() 713 714 // Make sure we actually write the profiling information to disk, if we make it here. 715 // On long-running services, or anything that calls os.Exit(), this is insufficient, 716 // so we also flush periodically with a default period of five seconds (configurable by 717 // the KUBE_COVERAGE_FLUSH_INTERVAL environment variable). 718 coverage.FlushCoverage() 719 } 720 EOF 721 } 722 723 # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler). 724 # Deletes a test generated by kube::golang::create_coverage_dummy_test. 725 # It is not an error to call this for a nonexistent test. 726 kube::golang::delete_coverage_dummy_test() { 727 local package="$1" 728 rm -f "$(kube::golang::path_for_coverage_dummy_test "${package}")" 729 } 730 731 # Arguments: a list of kubernetes packages to build. 732 # Expected variables: ${build_args} should be set to an array of Go build arguments. 733 # In addition, ${package} and ${platform} should have been set earlier, and if 734 # ${KUBE_BUILD_WITH_COVERAGE} is set, coverage instrumentation will be enabled. 735 # 736 # Invokes Go to actually build some packages. If coverage is disabled, simply invokes 737 # go install. If coverage is enabled, builds covered binaries using go test, temporarily 738 # producing the required unit test files and then cleaning up after itself. 739 # Non-covered binaries are then built using go install as usual. 740 # 741 # See comments in kube::golang::setup_env regarding where built binaries go. 742 kube::golang::build_some_binaries() { 743 if [[ -n "${KUBE_BUILD_WITH_COVERAGE:-}" ]]; then 744 local -a uncovered=() 745 for package in "$@"; do 746 if kube::golang::is_instrumented_package "${package}"; then 747 V=2 kube::log::info "Building ${package} with coverage..." 748 749 kube::golang::create_coverage_dummy_test "${package}" 750 kube::util::trap_add "kube::golang::delete_coverage_dummy_test \"${package}\"" EXIT 751 752 go test -c -o "$(kube::golang::outfile_for_binary "${package}" "${platform}")" \ 753 -covermode count \ 754 -coverpkg k8s.io/... \ 755 "${build_args[@]}" \ 756 -tags coverage \ 757 "${package}" 758 else 759 uncovered+=("${package}") 760 fi 761 done 762 if [[ "${#uncovered[@]}" != 0 ]]; then 763 V=2 kube::log::info "Building ${uncovered[*]} without coverage..." 764 GOPROXY=off go install "${build_args[@]}" "${uncovered[@]}" 765 else 766 V=2 kube::log::info "Nothing to build without coverage." 767 fi 768 else 769 V=2 kube::log::info "Coverage is disabled." 770 GOPROXY=off go install "${build_args[@]}" "$@" 771 fi 772 } 773 774 # Args: 775 # $1: platform (e.g. darwin/amd64) 776 kube::golang::build_binaries_for_platform() { 777 # This is for sanity. Without it, user umasks can leak through. 778 umask 0022 779 780 local platform=$1 781 782 local -a statics=() 783 local -a nonstatics=() 784 local -a tests=() 785 786 for binary in "${binaries[@]}"; do 787 if [[ "${binary}" =~ ".test"$ ]]; then 788 tests+=("${binary}") 789 kube::log::info " ${binary} (test)" 790 elif kube::golang::is_statically_linked "${binary}"; then 791 statics+=("${binary}") 792 kube::log::info " ${binary} (static)" 793 else 794 nonstatics+=("${binary}") 795 kube::log::info " ${binary} (non-static)" 796 fi 797 done 798 799 V=2 kube::log::info "Env for ${platform}: GOPATH=${GOPATH-} GOOS=${GOOS-} GOARCH=${GOARCH-} GOROOT=${GOROOT-} CGO_ENABLED=${CGO_ENABLED-} CC=${CC-}" 800 V=3 kube::log::info "Building binaries with GCFLAGS=${gogcflags} LDFLAGS=${goldflags}" 801 802 local -a build_args 803 if [[ "${#statics[@]}" != 0 ]]; then 804 build_args=( 805 -installsuffix=static 806 ${goflags:+"${goflags[@]}"} 807 -gcflags="${gogcflags}" 808 -ldflags="${goldflags}" 809 -tags="${gotags:-}" 810 ) 811 CGO_ENABLED=0 kube::golang::build_some_binaries "${statics[@]}" 812 fi 813 814 if [[ "${#nonstatics[@]}" != 0 ]]; then 815 build_args=( 816 ${goflags:+"${goflags[@]}"} 817 -gcflags="${gogcflags}" 818 -ldflags="${goldflags}" 819 -tags="${gotags:-}" 820 ) 821 kube::golang::build_some_binaries "${nonstatics[@]}" 822 fi 823 824 for test in "${tests[@]:+${tests[@]}}"; do 825 local outfile testpkg 826 outfile=$(kube::golang::outfile_for_binary "${test}" "${platform}") 827 testpkg=$(dirname "${test}") 828 829 mkdir -p "$(dirname "${outfile}")" 830 go test -c \ 831 ${goflags:+"${goflags[@]}"} \ 832 -gcflags="${gogcflags}" \ 833 -ldflags="${goldflags}" \ 834 -tags="${gotags:-}" \ 835 -o "${outfile}" \ 836 "${testpkg}" 837 done 838 } 839 840 # Return approximate physical memory available in gigabytes. 841 kube::golang::get_physmem() { 842 local mem 843 844 # Linux kernel version >=3.14, in kb 845 if mem=$(grep MemAvailable /proc/meminfo | awk '{ print $2 }'); then 846 echo $(( mem / 1048576 )) 847 return 848 fi 849 850 # Linux, in kb 851 if mem=$(grep MemTotal /proc/meminfo | awk '{ print $2 }'); then 852 echo $(( mem / 1048576 )) 853 return 854 fi 855 856 # OS X, in bytes. Note that get_physmem, as used, should only ever 857 # run in a Linux container (because it's only used in the multiple 858 # platform case, which is a Dockerized build), but this is provided 859 # for completeness. 860 if mem=$(sysctl -n hw.memsize 2>/dev/null); then 861 echo $(( mem / 1073741824 )) 862 return 863 fi 864 865 # If we can't infer it, just give up and assume a low memory system 866 echo 1 867 } 868 869 # Build binaries targets specified 870 # 871 # Input: 872 # $@ - targets and go flags. If no targets are set then all binaries targets 873 # are built. 874 # KUBE_BUILD_PLATFORMS - Incoming variable of targets to build for. If unset 875 # then just the host architecture is built. 876 kube::golang::build_binaries() { 877 V=2 kube::log::info "Go version: $(GOFLAGS='' go version)" 878 879 local host_platform 880 host_platform=$(kube::golang::host_platform) 881 882 # These are "local" but are visible to and relied on by functions this 883 # function calls. They are effectively part of the calling API to 884 # build_binaries_for_platform. 885 local goflags goldflags gogcflags gotags 886 887 goflags=() 888 gogcflags="${GOGCFLAGS:-}" 889 goldflags="all=$(kube::version::ldflags) ${GOLDFLAGS:-}" 890 891 if [[ "${DBG:-}" == 1 ]]; then 892 # Debugging - disable optimizations and inlining and trimPath 893 gogcflags="${gogcflags} all=-N -l" 894 else 895 # Not debugging - disable symbols and DWARF, trim embedded paths 896 goldflags="${goldflags} -s -w" 897 goflags+=("-trimpath") 898 fi 899 900 # Extract tags if any specified in GOFLAGS 901 gotags="selinux,notest,$(echo "${GOFLAGS:-}" | sed -ne 's|.*-tags=\([^-]*\).*|\1|p')" 902 903 local -a targets=() 904 local arg 905 906 for arg; do 907 if [[ "${arg}" == -* ]]; then 908 # Assume arguments starting with a dash are flags to pass to go. 909 goflags+=("${arg}") 910 else 911 targets+=("${arg}") 912 fi 913 done 914 915 local -a platforms 916 IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS:-}" 917 if [[ ${#platforms[@]} -eq 0 ]]; then 918 platforms=("${host_platform}") 919 fi 920 921 if [[ ${#targets[@]} -eq 0 ]]; then 922 targets=("${KUBE_ALL_TARGETS[@]}") 923 fi 924 kube::util::read-array targets < <(kube::golang::dedup "${targets[@]}") 925 926 local -a binaries 927 kube::util::read-array binaries < <(kube::golang::normalize_go_targets "${targets[@]}") 928 kube::util::read-array binaries < <(kube::golang::dedup "${binaries[@]}") 929 930 local parallel=false 931 if [[ ${#platforms[@]} -gt 1 ]]; then 932 local gigs 933 gigs=$(kube::golang::get_physmem) 934 935 if [[ ${gigs} -ge ${KUBE_PARALLEL_BUILD_MEMORY} ]]; then 936 kube::log::status "Multiple platforms requested and available ${gigs}G >= threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in parallel" 937 parallel=true 938 else 939 kube::log::status "Multiple platforms requested, but available ${gigs}G < threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in serial" 940 parallel=false 941 fi 942 fi 943 944 if [[ "${parallel}" == "true" ]]; then 945 kube::log::status "Building go targets for {${platforms[*]}} in parallel (output will appear in a burst when complete):" "${targets[@]}" 946 local platform 947 for platform in "${platforms[@]}"; do ( 948 kube::golang::set_platform_envs "${platform}" 949 kube::log::status "${platform}: build started" 950 kube::golang::build_binaries_for_platform "${platform}" 951 kube::log::status "${platform}: build finished" 952 ) &> "/tmp//${platform//\//_}.build" & 953 done 954 955 local fails=0 956 for job in $(jobs -p); do 957 wait "${job}" || (( fails+=1 )) 958 done 959 960 for platform in "${platforms[@]}"; do 961 cat "/tmp//${platform//\//_}.build" 962 done 963 964 return "${fails}" 965 else 966 for platform in "${platforms[@]}"; do 967 kube::log::status "Building go targets for ${platform}" 968 ( 969 kube::golang::set_platform_envs "${platform}" 970 kube::golang::build_binaries_for_platform "${platform}" 971 ) 972 done 973 fi 974 }