mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-29 06:06:03 +00:00
Both GO111MODULE and GOWORK default to on, so this just unsets them. We could set them to explicit values but this seems equivalent and cleaner.
979 lines
33 KiB
Bash
Executable File
979 lines
33 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Copyright 2014 The Kubernetes Authors.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# shellcheck disable=SC2034 # Variables sourced in other scripts.
|
|
|
|
readonly KUBE_GOPATH="${KUBE_GOPATH:-"${KUBE_OUTPUT}/go"}"
|
|
export KUBE_GOPATH
|
|
|
|
# The server platform we are building on.
|
|
readonly KUBE_SUPPORTED_SERVER_PLATFORMS=(
|
|
linux/amd64
|
|
linux/arm64
|
|
linux/s390x
|
|
linux/ppc64le
|
|
)
|
|
|
|
# The node platforms we build for
|
|
readonly KUBE_SUPPORTED_NODE_PLATFORMS=(
|
|
linux/amd64
|
|
linux/arm64
|
|
linux/s390x
|
|
linux/ppc64le
|
|
windows/amd64
|
|
)
|
|
|
|
# If we update this we should also update the set of platforms whose standard
|
|
# library is precompiled for in build/build-image/cross/Dockerfile
|
|
readonly KUBE_SUPPORTED_CLIENT_PLATFORMS=(
|
|
linux/amd64
|
|
linux/386
|
|
linux/arm
|
|
linux/arm64
|
|
linux/s390x
|
|
linux/ppc64le
|
|
darwin/amd64
|
|
darwin/arm64
|
|
windows/amd64
|
|
windows/386
|
|
windows/arm64
|
|
)
|
|
|
|
# Which platforms we should compile test targets for.
|
|
# Not all client platforms need these tests
|
|
readonly KUBE_SUPPORTED_TEST_PLATFORMS=(
|
|
linux/amd64
|
|
linux/arm64
|
|
linux/s390x
|
|
linux/ppc64le
|
|
darwin/amd64
|
|
darwin/arm64
|
|
windows/amd64
|
|
windows/arm64
|
|
)
|
|
|
|
# The set of server targets that we are only building for Linux
|
|
kube::golang::server_targets() {
|
|
local targets=(
|
|
cmd/kube-proxy
|
|
cmd/kube-apiserver
|
|
cmd/kube-controller-manager
|
|
cmd/kubelet
|
|
cmd/kubeadm
|
|
cmd/kube-scheduler
|
|
staging/src/k8s.io/component-base/logs/kube-log-runner
|
|
staging/src/k8s.io/kube-aggregator
|
|
staging/src/k8s.io/apiextensions-apiserver
|
|
cluster/gce/gci/mounter
|
|
)
|
|
echo "${targets[@]}"
|
|
}
|
|
|
|
IFS=" " read -ra KUBE_SERVER_TARGETS <<< "$(kube::golang::server_targets)"
|
|
readonly KUBE_SERVER_TARGETS
|
|
readonly KUBE_SERVER_BINARIES=("${KUBE_SERVER_TARGETS[@]##*/}")
|
|
|
|
# The set of server targets we build docker images for
|
|
kube::golang::server_image_targets() {
|
|
# NOTE: this contains cmd targets for kube::build::get_docker_wrapped_binaries
|
|
local targets=(
|
|
cmd/kube-apiserver
|
|
cmd/kube-controller-manager
|
|
cmd/kube-scheduler
|
|
cmd/kube-proxy
|
|
cmd/kubectl
|
|
)
|
|
echo "${targets[@]}"
|
|
}
|
|
|
|
IFS=" " read -ra KUBE_SERVER_IMAGE_TARGETS <<< "$(kube::golang::server_image_targets)"
|
|
readonly KUBE_SERVER_IMAGE_TARGETS
|
|
readonly KUBE_SERVER_IMAGE_BINARIES=("${KUBE_SERVER_IMAGE_TARGETS[@]##*/}")
|
|
|
|
# The set of conformance targets we build docker image for
|
|
kube::golang::conformance_image_targets() {
|
|
# NOTE: this contains cmd targets for kube::release::build_conformance_image
|
|
local targets=(
|
|
ginkgo
|
|
test/e2e/e2e.test
|
|
test/conformance/image/go-runner
|
|
cmd/kubectl
|
|
)
|
|
echo "${targets[@]}"
|
|
}
|
|
|
|
IFS=" " read -ra KUBE_CONFORMANCE_IMAGE_TARGETS <<< "$(kube::golang::conformance_image_targets)"
|
|
readonly KUBE_CONFORMANCE_IMAGE_TARGETS
|
|
|
|
# The set of server targets that we are only building for Kubernetes nodes
|
|
kube::golang::node_targets() {
|
|
local targets=(
|
|
cmd/kube-proxy
|
|
cmd/kubeadm
|
|
cmd/kubelet
|
|
staging/src/k8s.io/component-base/logs/kube-log-runner
|
|
)
|
|
echo "${targets[@]}"
|
|
}
|
|
|
|
IFS=" " read -ra KUBE_NODE_TARGETS <<< "$(kube::golang::node_targets)"
|
|
readonly KUBE_NODE_TARGETS
|
|
readonly KUBE_NODE_BINARIES=("${KUBE_NODE_TARGETS[@]##*/}")
|
|
readonly KUBE_NODE_BINARIES_WIN=("${KUBE_NODE_BINARIES[@]/%/.exe}")
|
|
|
|
# ------------
|
|
# NOTE: All functions that return lists should use newlines.
|
|
# bash functions can't return arrays, and spaces are tricky, so newline
|
|
# separators are the preferred pattern.
|
|
# To transform a string of newline-separated items to an array, use kube::util::read-array:
|
|
# kube::util::read-array FOO < <(kube::golang::dups a b c a)
|
|
#
|
|
# ALWAYS remember to quote your subshells. Not doing so will break in
|
|
# bash 4.3, and potentially cause other issues.
|
|
# ------------
|
|
|
|
# Returns a sorted newline-separated list containing only duplicated items.
|
|
kube::golang::dups() {
|
|
# We use printf to insert newlines, which are required by sort.
|
|
printf "%s\n" "$@" | sort | uniq -d
|
|
}
|
|
|
|
# Returns a sorted newline-separated list with duplicated items removed.
|
|
kube::golang::dedup() {
|
|
# We use printf to insert newlines, which are required by sort.
|
|
printf "%s\n" "$@" | sort -u
|
|
}
|
|
|
|
# Depends on values of user-facing KUBE_BUILD_PLATFORMS, KUBE_FASTBUILD,
|
|
# and KUBE_BUILDER_OS.
|
|
# Configures KUBE_SERVER_PLATFORMS, KUBE_NODE_PLATFOMRS,
|
|
# KUBE_TEST_PLATFORMS, and KUBE_CLIENT_PLATFORMS, then sets them
|
|
# to readonly.
|
|
# The configured vars will only contain platforms allowed by the
|
|
# KUBE_SUPPORTED* vars at the top of this file.
|
|
declare -a KUBE_SERVER_PLATFORMS
|
|
declare -a KUBE_CLIENT_PLATFORMS
|
|
declare -a KUBE_NODE_PLATFORMS
|
|
declare -a KUBE_TEST_PLATFORMS
|
|
kube::golang::setup_platforms() {
|
|
if [[ -n "${KUBE_BUILD_PLATFORMS:-}" ]]; then
|
|
# KUBE_BUILD_PLATFORMS needs to be read into an array before the next
|
|
# step, or quoting treats it all as one element.
|
|
local -a platforms
|
|
IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS}"
|
|
|
|
# Deduplicate to ensure the intersection trick with kube::golang::dups
|
|
# is not defeated by duplicates in user input.
|
|
kube::util::read-array platforms < <(kube::golang::dedup "${platforms[@]}")
|
|
|
|
# Use kube::golang::dups to restrict the builds to the platforms in
|
|
# KUBE_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each
|
|
# set, so if they appear twice after the merge they are in the intersection.
|
|
kube::util::read-array KUBE_SERVER_PLATFORMS < <(kube::golang::dups \
|
|
"${platforms[@]}" \
|
|
"${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}" \
|
|
)
|
|
readonly KUBE_SERVER_PLATFORMS
|
|
|
|
kube::util::read-array KUBE_NODE_PLATFORMS < <(kube::golang::dups \
|
|
"${platforms[@]}" \
|
|
"${KUBE_SUPPORTED_NODE_PLATFORMS[@]}" \
|
|
)
|
|
readonly KUBE_NODE_PLATFORMS
|
|
|
|
kube::util::read-array KUBE_TEST_PLATFORMS < <(kube::golang::dups \
|
|
"${platforms[@]}" \
|
|
"${KUBE_SUPPORTED_TEST_PLATFORMS[@]}" \
|
|
)
|
|
readonly KUBE_TEST_PLATFORMS
|
|
|
|
kube::util::read-array KUBE_CLIENT_PLATFORMS < <(kube::golang::dups \
|
|
"${platforms[@]}" \
|
|
"${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}" \
|
|
)
|
|
readonly KUBE_CLIENT_PLATFORMS
|
|
|
|
elif [[ "${KUBE_FASTBUILD:-}" == "true" ]]; then
|
|
host_arch=$(kube::util::host_arch)
|
|
if [[ "${host_arch}" != "amd64" && "${host_arch}" != "arm64" && "${host_arch}" != "ppc64le" && "${host_arch}" != "s390x" ]]; then
|
|
# on any platform other than amd64, arm64, ppc64le and s390x, we just default to amd64
|
|
host_arch="amd64"
|
|
fi
|
|
KUBE_SERVER_PLATFORMS=("linux/${host_arch}")
|
|
readonly KUBE_SERVER_PLATFORMS
|
|
KUBE_NODE_PLATFORMS=("linux/${host_arch}")
|
|
readonly KUBE_NODE_PLATFORMS
|
|
if [[ "${KUBE_BUILDER_OS:-}" == "darwin"* ]]; then
|
|
KUBE_TEST_PLATFORMS=(
|
|
"darwin/${host_arch}"
|
|
"linux/${host_arch}"
|
|
)
|
|
readonly KUBE_TEST_PLATFORMS
|
|
KUBE_CLIENT_PLATFORMS=(
|
|
"darwin/${host_arch}"
|
|
"linux/${host_arch}"
|
|
)
|
|
readonly KUBE_CLIENT_PLATFORMS
|
|
else
|
|
KUBE_TEST_PLATFORMS=("linux/${host_arch}")
|
|
readonly KUBE_TEST_PLATFORMS
|
|
KUBE_CLIENT_PLATFORMS=("linux/${host_arch}")
|
|
readonly KUBE_CLIENT_PLATFORMS
|
|
fi
|
|
else
|
|
KUBE_SERVER_PLATFORMS=("${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}")
|
|
readonly KUBE_SERVER_PLATFORMS
|
|
|
|
KUBE_NODE_PLATFORMS=("${KUBE_SUPPORTED_NODE_PLATFORMS[@]}")
|
|
readonly KUBE_NODE_PLATFORMS
|
|
|
|
KUBE_CLIENT_PLATFORMS=("${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}")
|
|
readonly KUBE_CLIENT_PLATFORMS
|
|
|
|
KUBE_TEST_PLATFORMS=("${KUBE_SUPPORTED_TEST_PLATFORMS[@]}")
|
|
readonly KUBE_TEST_PLATFORMS
|
|
fi
|
|
}
|
|
|
|
kube::golang::setup_platforms
|
|
|
|
# The set of client targets that we are building for all platforms
|
|
readonly KUBE_CLIENT_TARGETS=(
|
|
cmd/kubectl
|
|
cmd/kubectl-convert
|
|
)
|
|
readonly KUBE_CLIENT_BINARIES=("${KUBE_CLIENT_TARGETS[@]##*/}")
|
|
readonly KUBE_CLIENT_BINARIES_WIN=("${KUBE_CLIENT_BINARIES[@]/%/.exe}")
|
|
|
|
# The set of test targets that we are building for all platforms
|
|
kube::golang::test_targets() {
|
|
local targets=(
|
|
ginkgo
|
|
test/e2e/e2e.test
|
|
test/conformance/image/go-runner
|
|
)
|
|
echo "${targets[@]}"
|
|
}
|
|
IFS=" " read -ra KUBE_TEST_TARGETS <<< "$(kube::golang::test_targets)"
|
|
readonly KUBE_TEST_TARGETS
|
|
readonly KUBE_TEST_BINARIES=("${KUBE_TEST_TARGETS[@]##*/}")
|
|
readonly KUBE_TEST_BINARIES_WIN=("${KUBE_TEST_BINARIES[@]/%/.exe}")
|
|
readonly KUBE_TEST_PORTABLE=(
|
|
test/e2e/testing-manifests
|
|
test/kubemark
|
|
hack/e2e-internal
|
|
hack/get-build.sh
|
|
hack/ginkgo-e2e.sh
|
|
hack/lib
|
|
)
|
|
|
|
# Test targets which run on the Kubernetes clusters directly, so we only
|
|
# need to target server platforms.
|
|
# These binaries will be distributed in the kubernetes-test tarball.
|
|
kube::golang::server_test_targets() {
|
|
local targets=(
|
|
cmd/kubemark
|
|
ginkgo
|
|
)
|
|
|
|
if [[ "${OSTYPE:-}" == "linux"* ]]; then
|
|
targets+=( test/e2e_node/e2e_node.test )
|
|
fi
|
|
|
|
echo "${targets[@]}"
|
|
}
|
|
|
|
IFS=" " read -ra KUBE_TEST_SERVER_TARGETS <<< "$(kube::golang::server_test_targets)"
|
|
readonly KUBE_TEST_SERVER_TARGETS
|
|
readonly KUBE_TEST_SERVER_BINARIES=("${KUBE_TEST_SERVER_TARGETS[@]##*/}")
|
|
readonly KUBE_TEST_SERVER_PLATFORMS=("${KUBE_SERVER_PLATFORMS[@]:+"${KUBE_SERVER_PLATFORMS[@]}"}")
|
|
|
|
# Gigabytes necessary for parallel platform builds.
|
|
# As of March 2021 (go 1.16/amd64), the RSS usage is 2GiB by using cached
|
|
# memory of 15GiB.
|
|
# This variable can be overwritten at your own risk.
|
|
# It's defaulting to 20G to provide some headroom.
|
|
readonly KUBE_PARALLEL_BUILD_MEMORY=${KUBE_PARALLEL_BUILD_MEMORY:-20}
|
|
|
|
readonly KUBE_ALL_TARGETS=(
|
|
"${KUBE_SERVER_TARGETS[@]}"
|
|
"${KUBE_CLIENT_TARGETS[@]}"
|
|
"${KUBE_TEST_TARGETS[@]}"
|
|
"${KUBE_TEST_SERVER_TARGETS[@]}"
|
|
)
|
|
readonly KUBE_ALL_BINARIES=("${KUBE_ALL_TARGETS[@]##*/}")
|
|
|
|
readonly KUBE_STATIC_BINARIES=(
|
|
apiextensions-apiserver
|
|
kube-aggregator
|
|
kube-apiserver
|
|
kube-controller-manager
|
|
kube-scheduler
|
|
kube-proxy
|
|
kube-log-runner
|
|
kubeadm
|
|
kubectl
|
|
kubectl-convert
|
|
kubemark
|
|
mounter
|
|
)
|
|
|
|
# Fully-qualified package names that we want to instrument for coverage information.
|
|
readonly KUBE_COVERAGE_INSTRUMENTED_PACKAGES=(
|
|
k8s.io/kubernetes/cmd/kube-apiserver
|
|
k8s.io/kubernetes/cmd/kube-controller-manager
|
|
k8s.io/kubernetes/cmd/kube-scheduler
|
|
k8s.io/kubernetes/cmd/kube-proxy
|
|
k8s.io/kubernetes/cmd/kubelet
|
|
)
|
|
|
|
# KUBE_CGO_OVERRIDES is a space-separated list of binaries which should be built
|
|
# with CGO enabled, assuming CGO is supported on the target platform.
|
|
# This overrides any entry in KUBE_STATIC_BINARIES.
|
|
IFS=" " read -ra KUBE_CGO_OVERRIDES_LIST <<< "${KUBE_CGO_OVERRIDES:-}"
|
|
readonly KUBE_CGO_OVERRIDES_LIST
|
|
# KUBE_STATIC_OVERRIDES is a space-separated list of binaries which should be
|
|
# built with CGO disabled. This is in addition to the list in
|
|
# KUBE_STATIC_BINARIES.
|
|
IFS=" " read -ra KUBE_STATIC_OVERRIDES_LIST <<< "${KUBE_STATIC_OVERRIDES:-}"
|
|
readonly KUBE_STATIC_OVERRIDES_LIST
|
|
|
|
kube::golang::is_statically_linked() {
|
|
local e
|
|
# Explicitly enable cgo when building kubectl for darwin from darwin.
|
|
[[ "$(go env GOHOSTOS)" == "darwin" && "$(go env GOOS)" == "darwin" &&
|
|
"$1" == *"/kubectl" ]] && return 1
|
|
if [[ -n "${KUBE_CGO_OVERRIDES_LIST:+x}" ]]; then
|
|
for e in "${KUBE_CGO_OVERRIDES_LIST[@]}"; do [[ "${1}" == *"/${e}" ]] && return 1; done;
|
|
fi
|
|
for e in "${KUBE_STATIC_BINARIES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done;
|
|
if [[ -n "${KUBE_STATIC_OVERRIDES_LIST:+x}" ]]; then
|
|
for e in "${KUBE_STATIC_OVERRIDES_LIST[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done;
|
|
fi
|
|
return 1;
|
|
}
|
|
|
|
# kube::golang::best_guess_go_targets takes a list of build targets, which might
|
|
# be Go-style names (e.g. example.com/foo/bar or ./foo/bar) or just local paths
|
|
# (e.g. foo/bar) and produces a respective list (on stdout) of our best guess at
|
|
# Go target names.
|
|
kube::golang::best_guess_go_targets() {
|
|
local target
|
|
for target; do
|
|
if [ "${target}" = "ginkgo" ] ||
|
|
[ "${target}" = "github.com/onsi/ginkgo/ginkgo" ] ||
|
|
[ "${target}" = "vendor/github.com/onsi/ginkgo/ginkgo" ]; then
|
|
# Aliases that build the ginkgo CLI for hack/ginkgo-e2e.sh.
|
|
# "ginkgo" is the one that is documented in the Makefile. The others
|
|
# are for backwards compatibility.
|
|
echo "github.com/onsi/ginkgo/v2/ginkgo"
|
|
continue
|
|
fi
|
|
|
|
if [[ "${target}" =~ ^([[:alnum:]]+".")+[[:alnum:]]+"/" ]]; then
|
|
# If the target starts with what looks like a domain name, assume it has a
|
|
# fully-qualified Go package name.
|
|
echo "${target}"
|
|
continue
|
|
fi
|
|
|
|
if [[ "${target}" =~ ^vendor/ ]]; then
|
|
# Strip vendor/ prefix, since we're building in gomodule mode. This is
|
|
# for backwards compatibility.
|
|
echo "${target#"vendor/"}"
|
|
continue
|
|
fi
|
|
|
|
# If the target starts with "./", assume it is a local path which qualifies
|
|
# as a Go target name.
|
|
if [[ "${target}" =~ ^\./ ]]; then
|
|
echo "${target}"
|
|
continue
|
|
fi
|
|
|
|
# Otherwise assume it's a relative path (e.g. foo/bar or foo/bar/bar.test).
|
|
# We probably SHOULDN'T accept this, but we did in the past and it would be
|
|
# rude to break things if we don't NEED to. We can't really test if it
|
|
# exists or not, because the last element might be an output file (e.g.
|
|
# bar.test) or even "...".
|
|
echo "./${target}"
|
|
done
|
|
}
|
|
|
|
# kube::golang::normalize_go_targets takes a list of build targets, which might
|
|
# be Go-style names (e.g. example.com/foo/bar or ./foo/bar) or just local paths
|
|
# (e.g. foo/bar) and produces a respective list (on stdout) of Go package
|
|
# names.
|
|
kube::golang::normalize_go_targets() {
|
|
local targets=()
|
|
kube::util::read-array targets < <(kube::golang::best_guess_go_targets "$@")
|
|
kube::util::read-array targets < <(kube::golang::dedup "${targets[@]}")
|
|
set -- "${targets[@]}"
|
|
|
|
for target; do
|
|
if [[ "${target}" =~ ".test"$ ]]; then
|
|
local dir
|
|
dir="$(dirname "${target}")"
|
|
local tst
|
|
tst="$(basename "${target}")"
|
|
local pkg
|
|
pkg="$(go list -find "${dir}")"
|
|
echo "${pkg}/${tst}"
|
|
continue
|
|
fi
|
|
if [[ "${target}" =~ "/..."$ ]]; then
|
|
local dir
|
|
dir="$(dirname "${target}")"
|
|
local pkg
|
|
pkg="$(go list -find "${dir}")"
|
|
echo "${pkg}/..."
|
|
continue
|
|
fi
|
|
go list -find "${target}"
|
|
done
|
|
}
|
|
|
|
# Asks golang what it thinks the host platform is. The go tool chain does some
|
|
# slightly different things when the target platform matches the host platform.
|
|
kube::golang::host_platform() {
|
|
echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)"
|
|
}
|
|
|
|
# Takes the platform name ($1) and sets the appropriate golang env variables
|
|
# for that platform.
|
|
kube::golang::set_platform_envs() {
|
|
[[ -n ${1-} ]] || {
|
|
kube::log::error_exit "!!! Internal error. No platform set in kube::golang::set_platform_envs"
|
|
}
|
|
|
|
export GOOS=${platform%/*}
|
|
export GOARCH=${platform##*/}
|
|
|
|
# Do not set CC when building natively on a platform, only if cross-compiling
|
|
if [[ $(kube::golang::host_platform) != "$platform" ]]; then
|
|
# Dynamic CGO linking for other server architectures than host architecture goes here
|
|
# If you want to include support for more server platforms than these, add arch-specific gcc names here
|
|
case "${platform}" in
|
|
"linux/amd64")
|
|
export CGO_ENABLED=1
|
|
export CC=${KUBE_LINUX_AMD64_CC:-x86_64-linux-gnu-gcc}
|
|
;;
|
|
"linux/arm")
|
|
export CGO_ENABLED=1
|
|
export CC=${KUBE_LINUX_ARM_CC:-arm-linux-gnueabihf-gcc}
|
|
;;
|
|
"linux/arm64")
|
|
export CGO_ENABLED=1
|
|
export CC=${KUBE_LINUX_ARM64_CC:-aarch64-linux-gnu-gcc}
|
|
;;
|
|
"linux/ppc64le")
|
|
export CGO_ENABLED=1
|
|
export CC=${KUBE_LINUX_PPC64LE_CC:-powerpc64le-linux-gnu-gcc}
|
|
;;
|
|
"linux/s390x")
|
|
export CGO_ENABLED=1
|
|
export CC=${KUBE_LINUX_S390X_CC:-s390x-linux-gnu-gcc}
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# if CC is defined for platform then always enable it
|
|
ccenv=$(echo "$platform" | awk -F/ '{print "KUBE_" toupper($1) "_" toupper($2) "_CC"}')
|
|
if [ -n "${!ccenv-}" ]; then
|
|
export CGO_ENABLED=1
|
|
export CC="${!ccenv}"
|
|
fi
|
|
}
|
|
|
|
# Ensure the go tool exists and is a viable version.
|
|
# Inputs:
|
|
# env-var GO_VERSION is the desired go version to use, downloading it if needed (defaults to content of .go-version)
|
|
# 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
|
|
kube::golang::internal::verify_go_version() {
|
|
# default GO_VERSION to content of .go-version
|
|
GO_VERSION="${GO_VERSION:-"$(cat "${KUBE_ROOT}/.go-version")"}"
|
|
if [ "${GOTOOLCHAIN:-auto}" != 'auto' ]; then
|
|
# no-op, just respect GOTOOLCHAIN
|
|
:
|
|
elif [ -n "${FORCE_HOST_GO:-}" ]; then
|
|
# ensure existing host version is used, like before GOTOOLCHAIN existed
|
|
export GOTOOLCHAIN='local'
|
|
else
|
|
# otherwise, we want to ensure the go version matches GO_VERSION
|
|
GOTOOLCHAIN="go${GO_VERSION}"
|
|
export GOTOOLCHAIN
|
|
# if go is either not installed or too old to respect GOTOOLCHAIN then use gimme
|
|
if ! (command -v go >/dev/null && [ "$(go version | cut -d' ' -f3)" = "${GOTOOLCHAIN}" ]); then
|
|
export GIMME_ENV_PREFIX=${GIMME_ENV_PREFIX:-"${KUBE_OUTPUT}/.gimme/envs"}
|
|
export GIMME_VERSION_PREFIX=${GIMME_VERSION_PREFIX:-"${KUBE_OUTPUT}/.gimme/versions"}
|
|
# eval because the output of this is shell to set PATH etc.
|
|
eval "$("${KUBE_ROOT}/third_party/gimme/gimme" "${GO_VERSION}")"
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$(command -v go)" ]]; then
|
|
kube::log::usage_from_stdin <<EOF
|
|
Can't find 'go' in PATH, please fix and retry.
|
|
See http://golang.org/doc/install for installation instructions.
|
|
EOF
|
|
return 2
|
|
fi
|
|
|
|
local go_version
|
|
IFS=" " read -ra go_version <<< "$(GOFLAGS='' go version)"
|
|
local minimum_go_version
|
|
minimum_go_version=go1.22
|
|
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
|
|
kube::log::usage_from_stdin <<EOF
|
|
Detected go version: ${go_version[*]}.
|
|
Kubernetes requires ${minimum_go_version} or greater.
|
|
Please install ${minimum_go_version} or later.
|
|
EOF
|
|
return 2
|
|
fi
|
|
}
|
|
|
|
# kube::golang::setup_env will check that the `go` commands is available in
|
|
# ${PATH}. It will also check that the Go version is good enough for the
|
|
# Kubernetes build.
|
|
#
|
|
# Outputs:
|
|
# env-var GOPATH points to our local output dir
|
|
# env-var GOBIN is unset (we want binaries in a predictable place)
|
|
# env-var PATH includes the local GOPATH
|
|
kube::golang::setup_env() {
|
|
# Even in module mode, we need to set GOPATH for `go build` and `go install`
|
|
# to work. We build various tools (usually via `go install`) from a lot of
|
|
# scripts.
|
|
# * We can't just set GOBIN because that does not work on cross-compiles.
|
|
# * We could always use `go build -o <something>`, but it's subtle wrt
|
|
# cross-compiles and whether the <something> is a file or a directory,
|
|
# and EVERY caller has to get it *just* right.
|
|
# * We could leave GOPATH alone and let `go install` write binaries
|
|
# wherever the user's GOPATH says (or doesn't say).
|
|
#
|
|
# Instead we set it to a phony local path and process the results ourselves.
|
|
# In particular, GOPATH[0]/bin will be used for `go install`, with
|
|
# cross-compiles adding an extra directory under that.
|
|
export GOPATH="${KUBE_GOPATH}"
|
|
|
|
# If these are not set, set them now. This ensures that any subsequent
|
|
# scripts we run (which may call this function again) use the same values.
|
|
export GOCACHE="${GOCACHE:-"${KUBE_GOPATH}/cache/build"}"
|
|
export GOMODCACHE="${GOMODCACHE:-"${KUBE_GOPATH}/cache/mod"}"
|
|
|
|
# Make sure our own Go binaries are in PATH.
|
|
export PATH="${KUBE_GOPATH}/bin:${PATH}"
|
|
|
|
# Unset GOBIN in case it already exists in the current session.
|
|
# Cross-compiles will not work with it set.
|
|
unset GOBIN
|
|
|
|
# Turn on modules and workspaces (both are default-on).
|
|
unset GO111MODULE
|
|
unset GOWORK
|
|
|
|
# This may try to download our specific Go version. Do it last so it uses
|
|
# the above-configured environment.
|
|
kube::golang::internal::verify_go_version
|
|
}
|
|
|
|
kube::golang::setup_gomaxprocs() {
|
|
# GOMAXPROCS by default does not reflect the number of cpu(s) available
|
|
# when running in a container, please see https://github.com/golang/go/issues/33803
|
|
if [[ -z "${GOMAXPROCS:-}" ]]; then
|
|
if ! command -v ncpu >/dev/null 2>&1; then
|
|
# shellcheck disable=SC2164
|
|
pushd "${KUBE_ROOT}/hack/tools" >/dev/null
|
|
go install ./ncpu || echo "Will not automatically set GOMAXPROCS"
|
|
# shellcheck disable=SC2164
|
|
popd >/dev/null
|
|
fi
|
|
if command -v ncpu >/dev/null 2>&1; then
|
|
GOMAXPROCS=$(ncpu)
|
|
export GOMAXPROCS
|
|
kube::log::status "Set GOMAXPROCS automatically to ${GOMAXPROCS}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# This will take binaries from $GOPATH/bin and copy them to the appropriate
|
|
# place in ${KUBE_OUTPUT_BIN}
|
|
#
|
|
# Ideally this wouldn't be necessary and we could just set GOBIN to
|
|
# KUBE_OUTPUT_BIN but that won't work in the face of cross compilation. 'go
|
|
# install' will place binaries that match the host platform directly in $GOBIN
|
|
# while placing cross compiled binaries into `platform_arch` subdirs. This
|
|
# complicates pretty much everything else we do around packaging and such.
|
|
kube::golang::place_bins() {
|
|
local host_platform
|
|
host_platform=$(kube::golang::host_platform)
|
|
|
|
V=2 kube::log::status "Placing binaries"
|
|
|
|
local platform
|
|
for platform in "${KUBE_CLIENT_PLATFORMS[@]}"; do
|
|
# The substitution on platform_src below will replace all slashes with
|
|
# underscores. It'll transform darwin/amd64 -> darwin_amd64.
|
|
local platform_src="/${platform//\//_}"
|
|
if [[ "${platform}" == "${host_platform}" ]]; then
|
|
platform_src=""
|
|
rm -f "${THIS_PLATFORM_BIN}"
|
|
ln -s "${KUBE_OUTPUT_BIN}/${platform}" "${THIS_PLATFORM_BIN}"
|
|
fi
|
|
|
|
local full_binpath_src="${KUBE_GOPATH}/bin${platform_src}"
|
|
if [[ -d "${full_binpath_src}" ]]; then
|
|
mkdir -p "${KUBE_OUTPUT_BIN}/${platform}"
|
|
find "${full_binpath_src}" -maxdepth 1 -type f -exec \
|
|
rsync -pc {} "${KUBE_OUTPUT_BIN}/${platform}" \;
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Try and replicate the native binary placement of go install without
|
|
# calling go install.
|
|
kube::golang::outfile_for_binary() {
|
|
local binary=$1
|
|
local platform=$2
|
|
local output_path="${KUBE_GOPATH}/bin"
|
|
local bin
|
|
bin=$(basename "${binary}")
|
|
if [[ "${platform}" != "${host_platform}" ]]; then
|
|
output_path="${output_path}/${platform//\//_}"
|
|
fi
|
|
if [[ ${GOOS} == "windows" ]]; then
|
|
bin="${bin}.exe"
|
|
fi
|
|
echo "${output_path}/${bin}"
|
|
}
|
|
|
|
# Argument: the name of a Kubernetes package.
|
|
# Returns 0 if the binary can be built with coverage, 1 otherwise.
|
|
# NB: this ignores whether coverage is globally enabled or not.
|
|
kube::golang::is_instrumented_package() {
|
|
if kube::util::array_contains "$1" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"; then
|
|
return 0
|
|
fi
|
|
# Some cases, like `make kubectl`, pass $1 as "./cmd/kubectl" rather than
|
|
# "k8s.io/kubernetes/kubectl". Try to normalize and handle that. We don't
|
|
# do this always because it is a bit slow.
|
|
pkg=$(go list -find "$1")
|
|
if kube::util::array_contains "${pkg}" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler)
|
|
# Echos the path to a dummy test used for coverage information.
|
|
kube::golang::path_for_coverage_dummy_test() {
|
|
local package="$1"
|
|
local path
|
|
path=$(go list -find -f '{{.Dir}}' "${package}")
|
|
local name
|
|
name=$(basename "${package}")
|
|
echo "${path}/zz_generated_${name}_test.go"
|
|
}
|
|
|
|
# Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler).
|
|
# Creates a dummy unit test on disk in the source directory for the given package.
|
|
# This unit test will invoke the package's standard entry point when run.
|
|
kube::golang::create_coverage_dummy_test() {
|
|
local package="$1"
|
|
local name
|
|
name="$(basename "${package}")"
|
|
cat <<EOF > "$(kube::golang::path_for_coverage_dummy_test "${package}")"
|
|
package main
|
|
import (
|
|
"testing"
|
|
"k8s.io/kubernetes/pkg/util/coverage"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
// Get coverage running
|
|
coverage.InitCoverage("${name}")
|
|
|
|
// Go!
|
|
main()
|
|
|
|
// Make sure we actually write the profiling information to disk, if we make it here.
|
|
// On long-running services, or anything that calls os.Exit(), this is insufficient,
|
|
// so we also flush periodically with a default period of five seconds (configurable by
|
|
// the KUBE_COVERAGE_FLUSH_INTERVAL environment variable).
|
|
coverage.FlushCoverage()
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler).
|
|
# Deletes a test generated by kube::golang::create_coverage_dummy_test.
|
|
# It is not an error to call this for a nonexistent test.
|
|
kube::golang::delete_coverage_dummy_test() {
|
|
local package="$1"
|
|
rm -f "$(kube::golang::path_for_coverage_dummy_test "${package}")"
|
|
}
|
|
|
|
# Arguments: a list of kubernetes packages to build.
|
|
# Expected variables: ${build_args} should be set to an array of Go build arguments.
|
|
# In addition, ${package} and ${platform} should have been set earlier, and if
|
|
# ${KUBE_BUILD_WITH_COVERAGE} is set, coverage instrumentation will be enabled.
|
|
#
|
|
# Invokes Go to actually build some packages. If coverage is disabled, simply invokes
|
|
# go install. If coverage is enabled, builds covered binaries using go test, temporarily
|
|
# producing the required unit test files and then cleaning up after itself.
|
|
# Non-covered binaries are then built using go install as usual.
|
|
#
|
|
# See comments in kube::golang::setup_env regarding where built binaries go.
|
|
kube::golang::build_some_binaries() {
|
|
if [[ -n "${KUBE_BUILD_WITH_COVERAGE:-}" ]]; then
|
|
local -a uncovered=()
|
|
for package in "$@"; do
|
|
if kube::golang::is_instrumented_package "${package}"; then
|
|
V=2 kube::log::info "Building ${package} with coverage..."
|
|
|
|
kube::golang::create_coverage_dummy_test "${package}"
|
|
kube::util::trap_add "kube::golang::delete_coverage_dummy_test \"${package}\"" EXIT
|
|
|
|
go test -c -o "$(kube::golang::outfile_for_binary "${package}" "${platform}")" \
|
|
-covermode count \
|
|
-coverpkg k8s.io/... \
|
|
"${build_args[@]}" \
|
|
-tags coverage \
|
|
"${package}"
|
|
else
|
|
uncovered+=("${package}")
|
|
fi
|
|
done
|
|
if [[ "${#uncovered[@]}" != 0 ]]; then
|
|
V=2 kube::log::info "Building ${uncovered[*]} without coverage..."
|
|
GOPROXY=off go install "${build_args[@]}" "${uncovered[@]}"
|
|
else
|
|
V=2 kube::log::info "Nothing to build without coverage."
|
|
fi
|
|
else
|
|
V=2 kube::log::info "Coverage is disabled."
|
|
GOPROXY=off go install "${build_args[@]}" "$@"
|
|
fi
|
|
}
|
|
|
|
# Args:
|
|
# $1: platform (e.g. darwin/amd64)
|
|
kube::golang::build_binaries_for_platform() {
|
|
# This is for sanity. Without it, user umasks can leak through.
|
|
umask 0022
|
|
|
|
local platform=$1
|
|
|
|
local -a statics=()
|
|
local -a nonstatics=()
|
|
local -a tests=()
|
|
|
|
for binary in "${binaries[@]}"; do
|
|
if [[ "${binary}" =~ ".test"$ ]]; then
|
|
tests+=("${binary}")
|
|
kube::log::info " ${binary} (test)"
|
|
elif kube::golang::is_statically_linked "${binary}"; then
|
|
statics+=("${binary}")
|
|
kube::log::info " ${binary} (static)"
|
|
else
|
|
nonstatics+=("${binary}")
|
|
kube::log::info " ${binary} (non-static)"
|
|
fi
|
|
done
|
|
|
|
V=2 kube::log::info "Env for ${platform}: GOOS=${GOOS-} GOARCH=${GOARCH-} GOROOT=${GOROOT-} CGO_ENABLED=${CGO_ENABLED-} CC=${CC-}"
|
|
V=3 kube::log::info "Building binaries with GCFLAGS=${gogcflags} LDFLAGS=${goldflags}"
|
|
|
|
local -a build_args
|
|
if [[ "${#statics[@]}" != 0 ]]; then
|
|
build_args=(
|
|
-installsuffix=static
|
|
${goflags:+"${goflags[@]}"}
|
|
-gcflags="${gogcflags}"
|
|
-ldflags="${goldflags}"
|
|
-tags="${gotags:-}"
|
|
)
|
|
CGO_ENABLED=0 kube::golang::build_some_binaries "${statics[@]}"
|
|
fi
|
|
|
|
if [[ "${#nonstatics[@]}" != 0 ]]; then
|
|
build_args=(
|
|
${goflags:+"${goflags[@]}"}
|
|
-gcflags="${gogcflags}"
|
|
-ldflags="${goldflags}"
|
|
-tags="${gotags:-}"
|
|
)
|
|
kube::golang::build_some_binaries "${nonstatics[@]}"
|
|
fi
|
|
|
|
for test in "${tests[@]:+${tests[@]}}"; do
|
|
local outfile testpkg
|
|
outfile=$(kube::golang::outfile_for_binary "${test}" "${platform}")
|
|
testpkg=$(dirname "${test}")
|
|
|
|
mkdir -p "$(dirname "${outfile}")"
|
|
go test -c \
|
|
${goflags:+"${goflags[@]}"} \
|
|
-gcflags="${gogcflags}" \
|
|
-ldflags="${goldflags}" \
|
|
-tags="${gotags:-}" \
|
|
-o "${outfile}" \
|
|
"${testpkg}"
|
|
done
|
|
}
|
|
|
|
# Return approximate physical memory available in gigabytes.
|
|
kube::golang::get_physmem() {
|
|
local mem
|
|
|
|
# Linux kernel version >=3.14, in kb
|
|
if mem=$(grep MemAvailable /proc/meminfo | awk '{ print $2 }'); then
|
|
echo $(( mem / 1048576 ))
|
|
return
|
|
fi
|
|
|
|
# Linux, in kb
|
|
if mem=$(grep MemTotal /proc/meminfo | awk '{ print $2 }'); then
|
|
echo $(( mem / 1048576 ))
|
|
return
|
|
fi
|
|
|
|
# OS X, in bytes. Note that get_physmem, as used, should only ever
|
|
# run in a Linux container (because it's only used in the multiple
|
|
# platform case, which is a Dockerized build), but this is provided
|
|
# for completeness.
|
|
if mem=$(sysctl -n hw.memsize 2>/dev/null); then
|
|
echo $(( mem / 1073741824 ))
|
|
return
|
|
fi
|
|
|
|
# If we can't infer it, just give up and assume a low memory system
|
|
echo 1
|
|
}
|
|
|
|
# Build binaries targets specified
|
|
#
|
|
# Input:
|
|
# $@ - targets and go flags. If no targets are set then all binaries targets
|
|
# are built.
|
|
# KUBE_BUILD_PLATFORMS - Incoming variable of targets to build for. If unset
|
|
# then just the host architecture is built.
|
|
kube::golang::build_binaries() {
|
|
# Create a sub-shell so that we don't pollute the outer environment
|
|
(
|
|
# Check for `go` binary and set ${GOPATH}.
|
|
kube::golang::setup_env
|
|
V=2 kube::log::info "Go version: $(GOFLAGS='' go version)"
|
|
|
|
local host_platform
|
|
host_platform=$(kube::golang::host_platform)
|
|
|
|
# These are "local" but are visible to and relied on by functions this
|
|
# function calls. They are effectively part of the calling API to
|
|
# build_binaries_for_platform.
|
|
local goflags goldflags gogcflags gotags
|
|
|
|
goflags=()
|
|
gogcflags="${GOGCFLAGS:-}"
|
|
goldflags="all=$(kube::version::ldflags) ${GOLDFLAGS:-}"
|
|
|
|
if [[ "${DBG:-}" == 1 ]]; then
|
|
# Debugging - disable optimizations and inlining and trimPath
|
|
gogcflags="${gogcflags} all=-N -l"
|
|
else
|
|
# Not debugging - disable symbols and DWARF, trim embedded paths
|
|
goldflags="${goldflags} -s -w"
|
|
goflags+=("-trimpath")
|
|
fi
|
|
|
|
# Extract tags if any specified in GOFLAGS
|
|
gotags="selinux,notest,$(echo "${GOFLAGS:-}" | sed -ne 's|.*-tags=\([^-]*\).*|\1|p')"
|
|
|
|
local -a targets=()
|
|
local arg
|
|
|
|
for arg; do
|
|
if [[ "${arg}" == -* ]]; then
|
|
# Assume arguments starting with a dash are flags to pass to go.
|
|
goflags+=("${arg}")
|
|
else
|
|
targets+=("${arg}")
|
|
fi
|
|
done
|
|
|
|
local -a platforms
|
|
IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS:-}"
|
|
if [[ ${#platforms[@]} -eq 0 ]]; then
|
|
platforms=("${host_platform}")
|
|
fi
|
|
|
|
if [[ ${#targets[@]} -eq 0 ]]; then
|
|
targets=("${KUBE_ALL_TARGETS[@]}")
|
|
fi
|
|
kube::util::read-array targets < <(kube::golang::dedup "${targets[@]}")
|
|
|
|
local -a binaries
|
|
kube::util::read-array binaries < <(kube::golang::normalize_go_targets "${targets[@]}")
|
|
kube::util::read-array binaries < <(kube::golang::dedup "${binaries[@]}")
|
|
|
|
local parallel=false
|
|
if [[ ${#platforms[@]} -gt 1 ]]; then
|
|
local gigs
|
|
gigs=$(kube::golang::get_physmem)
|
|
|
|
if [[ ${gigs} -ge ${KUBE_PARALLEL_BUILD_MEMORY} ]]; then
|
|
kube::log::status "Multiple platforms requested and available ${gigs}G >= threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in parallel"
|
|
parallel=true
|
|
else
|
|
kube::log::status "Multiple platforms requested, but available ${gigs}G < threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in serial"
|
|
parallel=false
|
|
fi
|
|
fi
|
|
|
|
if [[ "${parallel}" == "true" ]]; then
|
|
kube::log::status "Building go targets for {${platforms[*]}} in parallel (output will appear in a burst when complete):" "${targets[@]}"
|
|
local platform
|
|
for platform in "${platforms[@]}"; do (
|
|
kube::golang::set_platform_envs "${platform}"
|
|
kube::log::status "${platform}: build started"
|
|
kube::golang::build_binaries_for_platform "${platform}"
|
|
kube::log::status "${platform}: build finished"
|
|
) &> "/tmp//${platform//\//_}.build" &
|
|
done
|
|
|
|
local fails=0
|
|
for job in $(jobs -p); do
|
|
wait "${job}" || (( fails+=1 ))
|
|
done
|
|
|
|
for platform in "${platforms[@]}"; do
|
|
cat "/tmp//${platform//\//_}.build"
|
|
done
|
|
|
|
exit "${fails}"
|
|
else
|
|
for platform in "${platforms[@]}"; do
|
|
kube::log::status "Building go targets for ${platform}"
|
|
(
|
|
kube::golang::set_platform_envs "${platform}"
|
|
kube::golang::build_binaries_for_platform "${platform}"
|
|
)
|
|
done
|
|
fi
|
|
)
|
|
}
|