Change:
(cc Despite doing very, very similar things, doesn't rely on . It should, or the logic should otherwise be consolidated. I'm going to maybe work on this to make easier.

Discussion:
# KUBE_GCS_RELEASE_BUCKET default: kubernetes-releases-${project_hash} readonly KUBE_GCS_RELEASE_PREFIX=${KUBE_GCS_RELEASE_PREFIX-devel}/ readonly KUBE_GCS_DOCKER_REG_PREFIX=${KUBE_GCS_DOCKER_REG_PREFIX-docker-reg}/ <del> readonly KUBE_GCS_LATEST_FILE=${KUBE_GCS_LATEST_FILE:-} readonly KUBE_GCS_LATEST_CONTENTS=${KUBE_GCS_LATEST_CONTENTS:-} </del> <ins> readonly KUBE_GCS_PUBLISH_VERSION=${KUBE_GCS_PUBLISH_VERSION:-} </ins> readonly KUBE_GCS_DELETE_EXISTING="${KUBE_GCS_DELETE_EXISTING:-n}" # Constants

"${DOCKER[@]}" rm -f -v "$1" >/dev/null 2>&1 || true } <ins> # Validate a release version # # Globals: #   None # Arguments: #   version # Returns: #   If version is a valid release version # Sets: #  BASH_REMATCH, so you can do something like: #    local -r version_major="${BASH_REMATCH[1]}" #    local -r version_minor="${BASH_REMATCH[2]}" #    local -r version_patch="${BASH_REMATCH[3]}" function kube::release::parse_and_validate_release_version() { local -r version_regex="^v(0|[1-9][0-9]*).(0|[1-9][0-9]*).(0|[1-9][0-9]*)$" local -r version="${1-}" [[ "${version}" =~ ${version_regex} ]] || { kube::log::error "Invalid release version: '${version}'" return 1 } } </ins> # --------------------------------------------------------------------------- # Building

kube::log::error "${gcs_destination} not empty." [[ ${KUBE_GCS_DELETE_EXISTING} =~ ^[yY]$ ]] || { read -p "Delete everything under ${gcs_destination}? [y/n] " -r || { <del> echo "EOF on prompt.  Skipping upload" </del> <ins> kube::log::status "EOF on prompt.  Skipping upload" </ins> return } [[ $REPLY =~ ^[yY]$ ]] || { <del> echo "Skipping upload" </del> <ins> kube::log::status "Skipping upload" </ins> return } } <ins> kube::log::status "Deleting everything under ${gcs_destination}" </ins> gsutil -q -m rm -f -R "${gcs_destination}" || return 1 fi

gsutil ls -lhr "${gcs_destination}" || return 1 } <del> function kube::release::gcs::publish_latest() { local latest_file_dst="gs://${KUBE_GCS_RELEASE_BUCKET}/${KUBE_GCS_LATEST_FILE}" mkdir -p "${RELEASE_STAGE}/upload" || return 1 echo ${KUBE_GCS_LATEST_CONTENTS} > "${RELEASE_STAGE}/upload/latest" || return 1 </del> <ins> # Publish a new ci version, (latest,) but only if the release files actually # exist on GCS. # # Globals: #   See callees # Arguments: #   None # Returns: #   Success function kube::release::gcs::publish_ci() { kube::release::gcs::verify_release_files || return 1 kube::release::gcs::publish 'ci/latest.txt' || return 1 } </ins> <del> gsutil -m cp "${RELEASE_STAGE}/upload/latest" "${latest_file_dst}" || return 1 </del> <ins> # Publish a new official version, (latest or stable,) but only if the release # files actually exist on GCS and the release we're dealing with is newer than # the contents in GCS. # # Globals: #   KUBE_GCS_PUBLISH_VERSION #   See callees # Arguments: #   release_kind: either 'latest' or 'stable' # Returns: #   Success function kube::release::gcs::publish_official() { local -r release_kind="${1-}" kube::release::gcs::verify_release_files || return 1 kube::release::parse_and_validate_release_version "${KUBE_GCS_PUBLISH_VERSION}" || return 1 local -r version_major="${BASH_REMATCH[1]}" local -r version_minor="${BASH_REMATCH[2]}" </ins> <del> if [[ ${KUBE_GCS_MAKE_PUBLIC} =~ ^[yY]$ ]]; then gsutil acl ch -R -g all:R "${latest_file_dst}" >/dev/null 2>&1 || return 1 </del> <ins> local publish_files if [[ "${release_kind}" == 'latest' ]]; then publish_files=(release/latest.txt release/latest-${version_major}.txt release/latest-${version_major}.${version_minor}.txt) elif [[ "${release_kind}" == 'stable' ]]; then publish_files=(release/stable.txt release/stable-${version_major}.txt release/stable-${version_major}.${version_minor}.txt) else kube::log::error "Wrong release_kind: must be 'latest' or 'stable'." return 1 </ins> fi <del> kube::log::status "gsutil cat ${latest_file_dst}:" gsutil cat ${latest_file_dst} || return 1 </del> <ins> for publish_file in ${publish_files[*]}; do # If there's a version that's above the one we're trying to release, don't # do anything, and just try the next one. kube::release::gcs::verify_release_gt "${publish_file}" || continue kube::release::gcs::publish "${publish_file}" || return 1 done </ins> } <del> # Publish a new latest.txt, but only if the release we're dealing with # is newer than the contents in GCS. function kube::release::gcs::publish_latest_official() { local -r new_version=${KUBE_GCS_LATEST_CONTENTS} local -r latest_file_dst="gs://${KUBE_GCS_RELEASE_BUCKET}/${KUBE_GCS_LATEST_FILE}" local -r version_regex="^v(0|[1-9][0-9]*).(0|[1-9][0-9]*).(0|[1-9][0-9]*)$" [[ ${new_version} =~ ${version_regex} ]] || { kube::log::error "publish_latest_official passed bogus value: '${new_version}'" </del> <ins> # Verify that the release files we expect actually exist. # # Globals: #   KUBE_GCS_RELEASE_BUCKET #   KUBE_GCS_RELEASE_PREFIX # Arguments: #   None # Returns: #   If release files exist function kube::release::gcs::verify_release_files() { local -r release_dir="gs://${KUBE_GCS_RELEASE_BUCKET}/${KUBE_GCS_RELEASE_PREFIX}" if ! gsutil ls "${release_dir}" >/dev/null 2>&1 ; then kube::log::error "Release files don't exist at '${release_dir}'" </ins> return 1 <del> } </del> <ins> fi } # Check if the new version is greater than the version currently published on # GCS. # # Globals: #   KUBE_GCS_PUBLISH_VERSION #   KUBE_GCS_RELEASE_BUCKET # Arguments: #   publish_file: the GCS location to look in # Returns: #   If new version is greater than the GCS version function kube::release::gcs::verify_release_gt() { local -r publish_file="${1-}" local -r new_version=${KUBE_GCS_PUBLISH_VERSION} local -r publish_file_dst="gs://${KUBE_GCS_RELEASE_BUCKET}/${publish_file}" kube::release::parse_and_validate_release_version "${new_version}" || return 1 </ins> local -r version_major="${BASH_REMATCH[1]}" local -r version_minor="${BASH_REMATCH[2]}" local -r version_patch="${BASH_REMATCH[3]}" local gcs_version <del> gcs_version=$(gsutil cat "${latest_file_dst}") [[ ${gcs_version} =~ ${version_regex} ]] || { kube::log::error "${latest_file_dst} contains invalid release version, can't compare: '${gcs_version}'" return 1 } </del> <ins> if gcs_version="$(gsutil cat "${publish_file_dst}")"; then kube::release::parse_and_validate_release_version "${gcs_version}" || { kube::log::error "${publish_file_dst} contains invalid release version, can't compare: '${gcs_version}'" return 1 } </ins> <del> local -r gcs_version_major="${BASH_REMATCH[1]}" local -r gcs_version_minor="${BASH_REMATCH[2]}" local -r gcs_version_patch="${BASH_REMATCH[3]}" local greater=true if [[ "${gcs_version_major}" -gt "${version_major}" ]]; then greater=false elif [[ "${gcs_version_major}" -lt "${version_major}" ]]; then : # fall out elif [[ "${gcs_version_minor}" -gt "${version_minor}" ]]; then greater=false elif [[ "${gcs_version_minor}" -lt "${version_minor}" ]]; then : # fall out elif [[ "${gcs_version_patch}" -ge "${version_patch}" ]]; then greater=false fi </del> <ins> local -r gcs_version_major="${BASH_REMATCH[1]}" local -r gcs_version_minor="${BASH_REMATCH[2]}" local -r gcs_version_patch="${BASH_REMATCH[3]}" local greater=true if [[ "${gcs_version_major}" -gt "${version_major}" ]]; then greater=false elif [[ "${gcs_version_major}" -lt "${version_major}" ]]; then : # fall out elif [[ "${gcs_version_minor}" -gt "${version_minor}" ]]; then greater=false elif [[ "${gcs_version_minor}" -lt "${version_minor}" ]]; then : # fall out elif [[ "${gcs_version_patch}" -ge "${version_patch}" ]]; then greater=false fi </ins> <del> if [[ "${greater}" != "true" ]]; then kube::log::status "${gcs_version} (latest on GCS) >= ${new_version} (just uploaded), not updating ${latest_file_dst}" </del> <ins> if [[ "${greater}" != "true" ]]; then kube::log::status "${new_version} (just uploaded) <= ${gcs_version} (latest on GCS), not updating ${publish_file_dst}" return 1 else kube::log::status "${new_version} (just uploaded) > ${gcs_version} (latest on GCS), updating ${publish_file_dst}" fi else  # gsutil cat failed; file does not exist kube::log::error "Release file '${publish_file_dst}' does not exist.  Continuing." </ins> return 0 fi <ins> } </ins> <del> kube::log::status "${new_version} (just uploaded) > ${gcs_version} (latest on GCS), updating ${latest_file_dst}" kube::release::gcs::publish_latest || return 1 </del> <ins> # Publish a release to GCS: upload a version file, if KUBE_GCS_MAKE_PUBLIC, # make it public, and verify the result. # # Globals: #   KUBE_GCS_RELEASE_BUCKET #   RELEASE_STAGE #   KUBE_GCS_PUBLISH_VERSION #   KUBE_GCS_MAKE_PUBLIC # Arguments: #   publish_file: the GCS location to look in # Returns: #   If new version is greater than the GCS version function kube::release::gcs::publish() { local -r publish_file="${1-}" local -r publish_file_dst="gs://${KUBE_GCS_RELEASE_BUCKET}/${publish_file}" mkdir -p "${RELEASE_STAGE}/upload" || return 1 echo "${KUBE_GCS_PUBLISH_VERSION}" > "${RELEASE_STAGE}/upload/latest" || return 1 gsutil -m cp "${RELEASE_STAGE}/upload/latest" "${publish_file_dst}" || return 1 local contents if [[ ${KUBE_GCS_MAKE_PUBLIC} =~ ^[yY]$ ]]; then gsutil acl ch -R -g all:R "${publish_file_dst}" >/dev/null 2>&1 || return 1 # If public, validate public link local -r public_link="https://storage.googleapis.com/${KUBE_GCS_RELEASE_BUCKET}/${publish_file}" kube::log::status "Validating uploaded version file at ${public_link}" contents="$(curl -s "${public_link}")" else # If not public, validate using gsutil kube::log::status "Validating uploaded version file at ${publish_file_dst}" contents="$(gsutil cat "${publish_file_dst}")" fi if [[ "${contents}" == "${KUBE_GCS_PUBLISH_VERSION}" ]]; then kube::log::status "Contents as expected: ${contents}" else kube::log::error "Expected contents of file to be ${KUBE_GCS_PUBLISH_VERSION}, but got ${contents}" return 1 fi </ins> }

set -o nounset set -o pipefail <del> if [[ -z "$1" ]]; then echo "Usage: $0 <version>" </del> <ins> if [ "$#" -ne 1 ]; then echo "Usage: ${0} <version>" </ins> exit 1 fi <del> if ! gsutil ls gs://kubernetes-release/release/${1}/kubernetes.tar.gz; then echo "Release files don't exist, aborting." exit 2 fi STABLE_FILE_LOCATION="kubernetes-release/release/stable.txt" </del> <ins> KUBE_RELEASE_VERSION="${1-}" </ins> <del> version_file=$(mktemp -t stable.XXXXXX) </del> <ins> KUBE_GCS_MAKE_PUBLIC='y' KUBE_GCS_RELEASE_BUCKET='kubernetes-release' KUBE_GCS_RELEASE_PREFIX="release/${KUBE_RELEASE_VERSION}" KUBE_GCS_PUBLISH_VERSION="${KUBE_RELEASE_VERSION}" </ins> <del> echo $1 >> ${version_file} echo "Uploading stable version $1 to google storage" gsutil cp ${version_file} "gs://${STABLE_FILE_LOCATION}" echo "Making it world readable" gsutil acl ch -R -g all:R "gs://${STABLE_FILE_LOCATION}" </del> <ins> KUBE_ROOT="$(dirname "${BASH_SOURCE}")/.." source "${KUBE_ROOT}/build/common.sh" </ins> <del> rm ${version_file} value=$(curl -s https://storage.googleapis.com/${STABLE_FILE_LOCATION}) echo "Validating version file" if [[ "${value}" != "${1}" ]]; then echo "Error validating upload, :${value}: vs expected :${1}:" </del> <ins> if "${KUBE_ROOT}/cluster/kubectl.sh" 'version' | grep 'Client' | grep 'dirty'; then echo "!!! Tag at invalid point, or something else is bad. Build is dirty. Don't push this build." >&2 </ins> exit 1 fi <ins> kube::release::gcs::publish_official 'stable' </ins>

KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. LATEST=$(kube::release::semantic_version) <del> KUBE_GCS_NO_CACHING=n KUBE_GCS_MAKE_PUBLIC=y KUBE_GCS_UPLOAD_RELEASE=y KUBE_GCS_DELETE_EXISTING=y KUBE_GCS_RELEASE_BUCKET=kubernetes-release </del> <ins> KUBE_GCS_NO_CACHING='n' KUBE_GCS_MAKE_PUBLIC='y' KUBE_GCS_UPLOAD_RELEASE='y' KUBE_GCS_DELETE_EXISTING='y' KUBE_GCS_RELEASE_BUCKET='kubernetes-release' </ins> KUBE_GCS_RELEASE_PREFIX="ci/${LATEST}" <del> KUBE_GCS_LATEST_FILE="ci/latest.txt" KUBE_GCS_LATEST_CONTENTS=${LATEST} </del> <ins> KUBE_GCS_PUBLISH_VERSION="${LATEST}" </ins> source "$KUBE_ROOT/build/common.sh" MAX_ATTEMPTS=3 attempt=0 while [[ ${attempt} -lt ${MAX_ATTEMPTS} ]]; do <del> kube::release::gcs::release && kube::release::gcs::publish_latest && break || true </del> <ins> kube::release::gcs::release && kube::release::gcs::publish_ci && break || true </ins> attempt=$((attempt + 1)) sleep 5 done

set -o nounset set -o pipefail <del> KUBE_RELEASE_VERSION=${1-} VERSION_REGEX="^v(0|[1-9][0-9]*).(0|[1-9][0-9]*).(0|[1-9][0-9]*)$" [[ ${KUBE_RELEASE_VERSION} =~ $VERSION_REGEX ]] || { echo "!!! You must specify the version you are releasing in the form of '$VERSION_REGEX'" >&2 </del> <ins> if [ "$#" -ne 1 ]; then echo "Usage: ${0} <version>" </ins> exit 1 <del> } </del> <ins> fi KUBE_RELEASE_VERSION="${1-}" </ins> <del> KUBE_GCS_NO_CACHING=n KUBE_GCS_MAKE_PUBLIC=y KUBE_GCS_UPLOAD_RELEASE=y KUBE_GCS_RELEASE_BUCKET=kubernetes-release KUBE_GCS_RELEASE_PREFIX=release/${KUBE_RELEASE_VERSION} KUBE_GCS_LATEST_FILE="release/latest.txt" KUBE_GCS_LATEST_CONTENTS=${KUBE_RELEASE_VERSION} </del> <ins> KUBE_GCS_NO_CACHING='n' KUBE_GCS_MAKE_PUBLIC='y' KUBE_GCS_UPLOAD_RELEASE='y' KUBE_GCS_RELEASE_BUCKET='kubernetes-release' KUBE_GCS_RELEASE_PREFIX="release/${KUBE_RELEASE_VERSION}" KUBE_GCS_PUBLISH_VERSION="${KUBE_RELEASE_VERSION}" </ins> <del> KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. source "$KUBE_ROOT/build/common.sh" </del> <ins> KUBE_ROOT="$(dirname "${BASH_SOURCE}")/.." source "${KUBE_ROOT}/build/common.sh" </ins> <del> if ${KUBE_ROOT}/cluster/kubectl.sh version | grep Client | grep dirty; then </del> <ins> if "${KUBE_ROOT}/cluster/kubectl.sh" 'version' | grep 'Client' | grep 'dirty'; then </ins> echo "!!! Tag at invalid point, or something else is bad. Build is dirty. Don't push this build." >&2 exit 1 fi <ins> kube::release::parse_and_validate_release_version "${KUBE_RELEASE_VERSION}" </ins> kube::release::gcs::release <del> kube::release::gcs::publish_latest_official </del> <ins> kube::release::gcs::publish_official 'latest' </ins>