Change:
Filing for This code is wrong The local needs to be on a separate line from the command execution, and we need to be resilient to not existing on OS X, like:
For once I was actually watching the output of a build and noticed that I was seeing an error about md5sum not being found. I'm building on a mac using boot2docker. The short_hash function () works fine on my mac as md5 does exist.
Actually, I'm just going to fix this as a follow-on to .
Thank you!

Discussion:
# Clean out any old releases rm -rf "${RELEASE_DIR}" mkdir -p "${RELEASE_DIR}" <del> kube::release::package_client_tarballs kube::release::package_server_tarballs kube::release::package_salt_tarball kube::release::package_test_tarball kube::release::package_full_tarball </del> <ins> kube::release::package_client_tarballs & kube::release::package_server_tarballs & kube::release::package_salt_tarball & wait || { kube::log::error "previous tarball phase failed"; return 1; } kube::release::package_full_tarball & # _full depends on all the previous phases kube::release::package_test_tarball & # _test doesn't depend on anything wait || { kube::log::error "previous tarball phase failed"; return 1; } </ins> } # Package up all of the cross compiled clients.  Over time this should grow into

# Find all of the built client binaries local platform platforms platforms=($(cd "${LOCAL_OUTPUT_BINPATH}" ; echo */*)) <del> for platform in "${platforms[@]}" ; do </del> <ins> for platform in "${platforms[@]}"; do </ins> local platform_tag=${platform///-} # Replace a "/" for a "-" <del> kube::log::status "Building tarball: client $platform_tag" </del> <ins> kube::log::status "Starting tarball: client $platform_tag" </ins> <del> local release_stage="${RELEASE_STAGE}/client/${platform_tag}/kubernetes" rm -rf "${release_stage}" mkdir -p "${release_stage}/client/bin" </del> <ins> ( local release_stage="${RELEASE_STAGE}/client/${platform_tag}/kubernetes" rm -rf "${release_stage}" mkdir -p "${release_stage}/client/bin" </ins> <del> local client_bins=("${KUBE_CLIENT_BINARIES[@]}") if [[ "${platform%/*}" == "windows" ]]; then client_bins=("${KUBE_CLIENT_BINARIES_WIN[@]}") fi </del> <ins> local client_bins=("${KUBE_CLIENT_BINARIES[@]}") if [[ "${platform%/*}" == "windows" ]]; then client_bins=("${KUBE_CLIENT_BINARIES_WIN[@]}") fi </ins> <del> # This fancy expression will expand to prepend a path # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the # KUBE_CLIENT_BINARIES array. cp "${client_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}"  "${release_stage}/client/bin/" </del> <ins> # This fancy expression will expand to prepend a path # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the # KUBE_CLIENT_BINARIES array. cp "${client_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}"  "${release_stage}/client/bin/" </ins> <del> kube::release::clean_cruft </del> <ins> kube::release::clean_cruft </ins> <del> local package_name="${RELEASE_DIR}/kubernetes-client-${platform_tag}.tar.gz" kube::release::create_tarball "${package_name}" "${release_stage}/.." </del> <ins> local package_name="${RELEASE_DIR}/kubernetes-client-${platform_tag}.tar.gz" kube::release::create_tarball "${package_name}" "${release_stage}/.." ) & </ins> done <ins> kube::log::status "Waiting on tarballs" wait || { kube::log::error "client tarball creation failed"; exit 1; } </ins> } # Package up all of the server binaries

done } <ins> function kube::release::md5() { if which md5 >/dev/null 2>&1; then md5 -q "$1" else md5sum "$1" | awk '{ print $1 }' fi } </ins> # This will take binaries that run on master and creates Docker images # that wrap the binary in them. (One docker image per binary) function kube::release::create_docker_images_for_server() { # Create a sub-shell so that we don't pollute the outer environment ( <del> local binary_name; </del> <ins> local binary_name </ins> for binary_name in "${KUBE_DOCKER_WRAPPED_BINARIES[@]}"; do <del> echo "+++ Building docker image: ${binary_name}"; local docker_file_path="$1/Dockerfile"; local binary_file_path="$1/${binary_name}"; if [ -f ${docker_file_path} ]; then rm ${docker_file_path}; fi; printf " FROM scratch n ADD ${binary_name} /${binary_name} n ENTRYPOINT [ "/${binary_name}" ]n" >> ${docker_file_path}; local md5_sum=$(md5sum ${binary_file_path} | awk '{print $1}') local docker_image_tag=gcr.io/google_containers/$binary_name:$md5_sum docker build -t "${docker_image_tag}" ${1}; docker save ${docker_image_tag} > ${1}/${binary_name}.tar; echo $md5_sum > ${1}/${binary_name}.docker_tag; rm ${docker_file_path}; </del> <ins> kube::log::status "Starting Docker build for image: ${binary_name}" ( local docker_file_path="$1/${binary_name}.Dockerfile" local binary_file_path="$1/${binary_name}" if [ -f ${docker_file_path} ]; then rm ${docker_file_path} fi printf " FROM scratch n ADD ${binary_name} /${binary_name} n ENTRYPOINT [ "/${binary_name}" ]n" >> ${docker_file_path} local md5_sum=$(kube::release::md5 ${binary_file_path}) local docker_image_tag=gcr.io/google_containers/$binary_name:$md5_sum docker build -q -f "${docker_file_path}" -t "${docker_image_tag}" ${1} >/dev/null docker save ${docker_image_tag} > ${1}/${binary_name}.tar echo $md5_sum > ${1}/${binary_name}.docker_tag rm ${docker_file_path} ) & </ins> done <ins> wait || { kube::log::error "previous Docker build failed"; return 1; } kube::log::status "Docker builds done" </ins> ) }

windows/amd64 ) <ins> # Gigabytes desired for parallel platform builds. 8 is fairly # arbitrary, but is a reasonable splitting point for 2015 # laptops-versus-not. readonly KUBE_PARALLEL_BUILD_MEMORY=8 </ins> readonly KUBE_ALL_TARGETS=( "${KUBE_SERVER_TARGETS[@]}" "${KUBE_CLIENT_TARGETS[@]}"

exit 0; } <ins> kube::golang::build_binaries_for_platform() { local platform=$1 local use_go_build=${2-} local -a statics=() local -a nonstatics=() for binary in "${binaries[@]}"; do if kube::golang::is_statically_linked_library "${binary}"; then kube::golang::exit_if_stdlib_not_installed; statics+=($binary) else nonstatics+=($binary) fi done if [[ -n ${use_go_build:-} ]]; then # Try and replicate the native binary placement of go install without # calling go install.  This means we have to iterate each binary. local output_path="${KUBE_GOPATH}/bin" if [[ $platform != $host_platform ]]; then output_path="${output_path}/${platform////_}" fi for binary in "${binaries[@]}"; do local bin=$(basename "${binary}") if [[ ${GOOS} == "windows" ]]; then bin="${bin}.exe" fi if kube::golang::is_statically_linked_library "${binary}"; then kube::golang::exit_if_stdlib_not_installed; CGO_ENABLED=0 go build -installsuffix cgo -o "${output_path}/${bin}"  "${goflags[@]:+${goflags[@]}}"  -ldflags "${version_ldflags}"  "${binary}" else go build -o "${output_path}/${bin}"  "${goflags[@]:+${goflags[@]}}"  -ldflags "${version_ldflags}"  "${binary}" fi done else # Use go install. if [[ "${#nonstatics[@]}" != 0 ]]; then go install "${goflags[@]:+${goflags[@]}}"  -ldflags "${version_ldflags}"  "${nonstatics[@]:+${nonstatics[@]}}" fi if [[ "${#statics[@]}" != 0 ]]; then CGO_ENABLED=0 go install -installsuffix cgo "${goflags[@]:+${goflags[@]}}"  -ldflags "${version_ldflags}"  "${statics[@]:+${statics[@]}}" fi fi } # Return approximate physical memory in gigabytes. kube::golang::get_physmem() { local mem # 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 } </ins> # Build binaries targets specified # # Input:

local binaries binaries=($(kube::golang::binaries_from_targets "${targets[@]}")) <del> local platform for platform in "${platforms[@]}"; do kube::golang::set_platform_envs "${platform}" kube::log::status "Building go targets for ${platform}:" "${targets[@]}" local -a statics=() local -a nonstatics=() for binary in "${binaries[@]}"; do if kube::golang::is_statically_linked_library "${binary}"; then kube::golang::exit_if_stdlib_not_installed; statics+=($binary) else nonstatics+=($binary) fi done </del> <ins> local parallel=false if [[ ${#platforms[@]} -gt 1 ]]; then local gigs gigs=$(kube::golang::get_physmem) </ins> <del> if [[ -n ${use_go_build:-} ]]; then # Try and replicate the native binary placement of go install without # calling go install.  This means we have to iterate each binary. local output_path="${KUBE_GOPATH}/bin" if [[ $platform != $host_platform ]]; then output_path="${output_path}/${platform////_}" fi for binary in "${binaries[@]}"; do local bin=$(basename "${binary}") if [[ ${GOOS} == "windows" ]]; then bin="${bin}.exe" fi if kube::golang::is_statically_linked_library "${binary}"; then kube::golang::exit_if_stdlib_not_installed; CGO_ENABLED=0 go build -installsuffix cgo -o "${output_path}/${bin}"  "${goflags[@]:+${goflags[@]}}"  -ldflags "${version_ldflags}"  "${binary}" else go build -o "${output_path}/${bin}"  "${goflags[@]:+${goflags[@]}}"  -ldflags "${version_ldflags}"  "${binary}" fi done </del> <ins> if [[ ${gigs} -gt ${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 </ins> else <del> # Use go install. if [[ "${#nonstatics[@]}" != 0 ]]; then go install "${goflags[@]:+${goflags[@]}}"  -ldflags "${version_ldflags}"  "${nonstatics[@]:+${nonstatics[@]}}" fi if [[ "${#statics[@]}" != 0 ]]; then CGO_ENABLED=0 go install -installsuffix cgo "${goflags[@]:+${goflags[@]}}"  -ldflags "${version_ldflags}"  "${statics[@]:+${statics[@]}}" fi </del> <ins> kube::log::status "Multiple platforms requested, but available ${gigs}G < threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in serial" parallel=false </ins> fi <del> done </del> <ins> 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}: go build started" kube::golang::build_binaries_for_platform ${platform} ${use_go_build:-} kube::log::status "${platform}: go build finished" ) &> "/tmp//${platform////_}.build" & done local fails=0 for job in $(jobs -p); do wait ${job} || let "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}:" "${targets[@]}" kube::golang::set_platform_envs "${platform}" kube::golang::build_binaries_for_platform ${platform} ${use_go_build:-} done fi </ins> ) }