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