Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com>pull/462/head
parent
dc318bbdb5
commit
91979285eb
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
#Define color variables
|
||||
#Feature
|
||||
COLOR_NORMAL='\033[0m';COLOR_BOLD='\033[1m';COLOR_DIM='\033[2m';COLOR_UNDER='\033[4m';
|
||||
COLOR_ITALIC='\033[3m';COLOR_NOITALIC='\033[23m';COLOR_BLINK='\033[5m';
|
||||
COLOR_REVERSE='\033[7m';COLOR_CONCEAL='\033[8m';COLOR_NOBOLD='\033[22m';
|
||||
COLOR_NOUNDER='\033[24m';COLOR_NOBLINK='\033[25m';
|
||||
|
||||
#Front color
|
||||
COLOR_BLACK='\033[30m';COLOR_RED='\033[31m';COLOR_GREEN='\033[32m';COLOR_YELLOW='\033[33m';
|
||||
COLOR_BLUE='\033[34m';COLOR_MAGENTA='\033[35m';COLOR_CYAN='\033[36m';COLOR_WHITE='\033[37m';
|
||||
|
||||
#background color
|
||||
COLOR_BBLACK='\033[40m';COLOR_BRED='\033[41m';
|
||||
COLOR_BGREEN='\033[42m';COLOR_BYELLOW='\033[43m';
|
||||
COLOR_BBLUE='\033[44m';COLOR_BMAGENTA='\033[45m';
|
||||
COLOR_BCYAN='\033[46m';COLOR_BWHITE='\033[47m';
|
||||
|
||||
# Print colors you can use
|
||||
iam::color::print_color()
|
||||
{
|
||||
echo
|
||||
echo -e ${bmagenta}--back-color:${normal}
|
||||
echo "bblack; bgreen; bblue; bcyan; bred; byellow; bmagenta; bwhite"
|
||||
echo
|
||||
echo -e ${red}--font-color:${normal}
|
||||
echo "black; red; green; yellow; blue; magenta; cyan; white"
|
||||
echo
|
||||
echo -e ${bold}--font:${normal}
|
||||
echo "normal; italic; reverse; nounder; bold; noitalic; conceal; noblink;
|
||||
dim; blink; nobold; under"
|
||||
echo
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# shellcheck disable=SC2034 # Variables sourced in other scripts.
|
||||
|
||||
# The server platform we are building on.
|
||||
readonly IAM_SUPPORTED_SERVER_PLATFORMS=(
|
||||
linux/amd64
|
||||
linux/arm64
|
||||
)
|
||||
|
||||
# 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 IAM_SUPPORTED_CLIENT_PLATFORMS=(
|
||||
linux/amd64
|
||||
linux/arm64
|
||||
)
|
||||
|
||||
# The set of server targets that we are only building for Linux
|
||||
# If you update this list, please also update build/BUILD.
|
||||
iam::golang::server_targets() {
|
||||
local targets=(
|
||||
iam-apiserver
|
||||
iam-authz-server
|
||||
iam-pump
|
||||
iam-watcher
|
||||
)
|
||||
echo "${targets[@]}"
|
||||
}
|
||||
|
||||
IFS=" " read -ra IAM_SERVER_TARGETS <<< "$(iam::golang::server_targets)"
|
||||
readonly IAM_SERVER_TARGETS
|
||||
readonly IAM_SERVER_BINARIES=("${IAM_SERVER_TARGETS[@]##*/}")
|
||||
|
||||
# The set of server targets we build docker images for
|
||||
iam::golang::server_image_targets() {
|
||||
# NOTE: this contains cmd targets for iam::build::get_docker_wrapped_binaries
|
||||
local targets=(
|
||||
cmd/iam-apiserver
|
||||
cmd/iam-authz-server
|
||||
cmd/iam-pump
|
||||
cmd/iam-watcher
|
||||
)
|
||||
echo "${targets[@]}"
|
||||
}
|
||||
|
||||
IFS=" " read -ra IAM_SERVER_IMAGE_TARGETS <<< "$(iam::golang::server_image_targets)"
|
||||
readonly IAM_SERVER_IMAGE_TARGETS
|
||||
readonly IAM_SERVER_IMAGE_BINARIES=("${IAM_SERVER_IMAGE_TARGETS[@]##*/}")
|
||||
|
||||
# ------------
|
||||
# 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 iam::util::read-array:
|
||||
# iam::util::read-array FOO < <(iam::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.
|
||||
iam::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.
|
||||
iam::golang::dedup() {
|
||||
# We use printf to insert newlines, which are required by sort.
|
||||
printf "%s\n" "$@" | sort -u
|
||||
}
|
||||
|
||||
# Depends on values of user-facing IAM_BUILD_PLATFORMS, IAM_FASTBUILD,
|
||||
# and IAM_BUILDER_OS.
|
||||
# Configures IAM_SERVER_PLATFORMS and IAM_CLIENT_PLATFORMS, then sets them
|
||||
# to readonly.
|
||||
# The configured vars will only contain platforms allowed by the
|
||||
# IAM_SUPPORTED* vars at the top of this file.
|
||||
declare -a IAM_SERVER_PLATFORMS
|
||||
declare -a IAM_CLIENT_PLATFORMS
|
||||
iam::golang::setup_platforms() {
|
||||
if [[ -n "${IAM_BUILD_PLATFORMS:-}" ]]; then
|
||||
# IAM_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 <<< "${IAM_BUILD_PLATFORMS}"
|
||||
|
||||
# Deduplicate to ensure the intersection trick with iam::golang::dups
|
||||
# is not defeated by duplicates in user input.
|
||||
iam::util::read-array platforms < <(iam::golang::dedup "${platforms[@]}")
|
||||
|
||||
# Use iam::golang::dups to restrict the builds to the platforms in
|
||||
# IAM_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.
|
||||
iam::util::read-array IAM_SERVER_PLATFORMS < <(iam::golang::dups \
|
||||
"${platforms[@]}" \
|
||||
"${IAM_SUPPORTED_SERVER_PLATFORMS[@]}" \
|
||||
)
|
||||
readonly IAM_SERVER_PLATFORMS
|
||||
|
||||
iam::util::read-array IAM_CLIENT_PLATFORMS < <(iam::golang::dups \
|
||||
"${platforms[@]}" \
|
||||
"${IAM_SUPPORTED_CLIENT_PLATFORMS[@]}" \
|
||||
)
|
||||
readonly IAM_CLIENT_PLATFORMS
|
||||
|
||||
elif [[ "${IAM_FASTBUILD:-}" == "true" ]]; then
|
||||
IAM_SERVER_PLATFORMS=(linux/amd64)
|
||||
readonly IAM_SERVER_PLATFORMS
|
||||
IAM_CLIENT_PLATFORMS=(linux/amd64)
|
||||
readonly IAM_CLIENT_PLATFORMS
|
||||
else
|
||||
IAM_SERVER_PLATFORMS=("${IAM_SUPPORTED_SERVER_PLATFORMS[@]}")
|
||||
readonly IAM_SERVER_PLATFORMS
|
||||
|
||||
IAM_CLIENT_PLATFORMS=("${IAM_SUPPORTED_CLIENT_PLATFORMS[@]}")
|
||||
readonly IAM_CLIENT_PLATFORMS
|
||||
fi
|
||||
}
|
||||
|
||||
iam::golang::setup_platforms
|
||||
|
||||
# The set of client targets that we are building for all platforms
|
||||
# If you update this list, please also update build/BUILD.
|
||||
readonly IAM_CLIENT_TARGETS=(
|
||||
iamctl
|
||||
)
|
||||
readonly IAM_CLIENT_BINARIES=("${IAM_CLIENT_TARGETS[@]##*/}")
|
||||
|
||||
readonly IAM_ALL_TARGETS=(
|
||||
"${IAM_SERVER_TARGETS[@]}"
|
||||
"${IAM_CLIENT_TARGETS[@]}"
|
||||
)
|
||||
readonly IAM_ALL_BINARIES=("${IAM_ALL_TARGETS[@]##*/}")
|
||||
|
||||
# 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.
|
||||
iam::golang::host_platform() {
|
||||
echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)"
|
||||
}
|
||||
|
||||
# Ensure the go tool exists and is a viable version.
|
||||
iam::golang::verify_go_version() {
|
||||
if [[ -z "$(command -v go)" ]]; then
|
||||
iam::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 <<< "$(go version)"
|
||||
local minimum_go_version
|
||||
minimum_go_version=go1.13.4
|
||||
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
|
||||
iam::log::usage_from_stdin <<EOF
|
||||
Detected go version: ${go_version[*]}.
|
||||
IAM requires ${minimum_go_version} or greater.
|
||||
Please install ${minimum_go_version} or later.
|
||||
EOF
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
|
||||
# iam::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
|
||||
# IAM build.
|
||||
#
|
||||
# Outputs:
|
||||
# env-var GOBIN is unset (we want binaries in a predictable place)
|
||||
# env-var GO15VENDOREXPERIMENT=1
|
||||
# env-var GO111MODULE=on
|
||||
iam::golang::setup_env() {
|
||||
iam::golang::verify_go_version
|
||||
|
||||
# Unset GOBIN in case it already exists in the current session.
|
||||
unset GOBIN
|
||||
|
||||
# This seems to matter to some tools
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
|
||||
# Open go module feature
|
||||
export GO111MODULE=on
|
||||
|
||||
# This is for sanity. Without it, user umasks leak through into release
|
||||
# artifacts.
|
||||
umask 0022
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
set -o errexit
|
||||
set +o nounset
|
||||
set -o pipefail
|
||||
|
||||
# Unset CDPATH so that path interpolation can work correctly
|
||||
# https://github.com/iamrnetes/iamrnetes/issues/52255
|
||||
unset CDPATH
|
||||
|
||||
# Default use go modules
|
||||
export GO111MODULE=on
|
||||
|
||||
# The root of the build/dist directory
|
||||
IAM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)"
|
||||
|
||||
source "${IAM_ROOT}/scripts/lib/util.sh"
|
||||
source "${IAM_ROOT}/scripts/lib/logging.sh"
|
||||
source "${IAM_ROOT}/scripts/lib/color.sh"
|
||||
|
||||
iam::log::install_errexit
|
||||
|
||||
source "${IAM_ROOT}/scripts/lib/version.sh"
|
||||
source "${IAM_ROOT}/scripts/lib/golang.sh"
|
@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# Controls verbosity of the script output and logging.
|
||||
IAM_VERBOSE="${IAM_VERBOSE:-5}"
|
||||
|
||||
# Handler for when we exit automatically on an error.
|
||||
# Borrowed from https://gist.github.com/ahendrix/7030300
|
||||
iam::log::errexit() {
|
||||
local err="${PIPESTATUS[*]}"
|
||||
|
||||
# If the shell we are in doesn't have errexit set (common in subshells) then
|
||||
# don't dump stacks.
|
||||
set +o | grep -qe "-o errexit" || return
|
||||
|
||||
set +o xtrace
|
||||
local code="${1:-1}"
|
||||
# Print out the stack trace described by $function_stack
|
||||
if [ ${#FUNCNAME[@]} -gt 2 ]
|
||||
then
|
||||
iam::log::error "Call tree:"
|
||||
for ((i=1;i<${#FUNCNAME[@]}-1;i++))
|
||||
do
|
||||
iam::log::error " ${i}: ${BASH_SOURCE[${i}+1]}:${BASH_LINENO[${i}]} ${FUNCNAME[${i}]}(...)"
|
||||
done
|
||||
fi
|
||||
iam::log::error_exit "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}' exited with status ${err}" "${1:-1}" 1
|
||||
}
|
||||
|
||||
iam::log::install_errexit() {
|
||||
# trap ERR to provide an error handler whenever a command exits nonzero this
|
||||
# is a more verbose version of set -o errexit
|
||||
trap 'iam::log::errexit' ERR
|
||||
|
||||
# setting errtrace allows our ERR trap handler to be propagated to functions,
|
||||
# expansions and subshells
|
||||
set -o errtrace
|
||||
}
|
||||
|
||||
# Print out the stack trace
|
||||
#
|
||||
# Args:
|
||||
# $1 The number of stack frames to skip when printing.
|
||||
iam::log::stack() {
|
||||
local stack_skip=${1:-0}
|
||||
stack_skip=$((stack_skip + 1))
|
||||
if [[ ${#FUNCNAME[@]} -gt ${stack_skip} ]]; then
|
||||
echo "Call stack:" >&2
|
||||
local i
|
||||
for ((i=1 ; i <= ${#FUNCNAME[@]} - stack_skip ; i++))
|
||||
do
|
||||
local frame_no=$((i - 1 + stack_skip))
|
||||
local source_file=${BASH_SOURCE[${frame_no}]}
|
||||
local source_lineno=${BASH_LINENO[$((frame_no - 1))]}
|
||||
local funcname=${FUNCNAME[${frame_no}]}
|
||||
echo " ${i}: ${source_file}:${source_lineno} ${funcname}(...)" >&2
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Log an error and exit.
|
||||
# Args:
|
||||
# $1 Message to log with the error
|
||||
# $2 The error code to return
|
||||
# $3 The number of stack frames to skip when printing.
|
||||
iam::log::error_exit() {
|
||||
local message="${1:-}"
|
||||
local code="${2:-1}"
|
||||
local stack_skip="${3:-0}"
|
||||
stack_skip=$((stack_skip + 1))
|
||||
|
||||
if [[ ${IAM_VERBOSE} -ge 4 ]]; then
|
||||
local source_file=${BASH_SOURCE[${stack_skip}]}
|
||||
local source_line=${BASH_LINENO[$((stack_skip - 1))]}
|
||||
echo "!!! Error in ${source_file}:${source_line}" >&2
|
||||
[[ -z ${1-} ]] || {
|
||||
echo " ${1}" >&2
|
||||
}
|
||||
|
||||
iam::log::stack ${stack_skip}
|
||||
|
||||
echo "Exiting with status ${code}" >&2
|
||||
fi
|
||||
|
||||
exit "${code}"
|
||||
}
|
||||
|
||||
# Log an error but keep going. Don't dump the stack or exit.
|
||||
iam::log::error() {
|
||||
timestamp=$(date +"[%m%d %H:%M:%S]")
|
||||
echo "!!! ${timestamp} ${1-}" >&2
|
||||
shift
|
||||
for message; do
|
||||
echo " ${message}" >&2
|
||||
done
|
||||
}
|
||||
|
||||
# Print an usage message to stderr. The arguments are printed directly.
|
||||
iam::log::usage() {
|
||||
echo >&2
|
||||
local message
|
||||
for message; do
|
||||
echo "${message}" >&2
|
||||
done
|
||||
echo >&2
|
||||
}
|
||||
|
||||
iam::log::usage_from_stdin() {
|
||||
local messages=()
|
||||
while read -r line; do
|
||||
messages+=("${line}")
|
||||
done
|
||||
|
||||
iam::log::usage "${messages[@]}"
|
||||
}
|
||||
|
||||
# Print out some info that isn't a top level status line
|
||||
iam::log::info() {
|
||||
local V="${V:-0}"
|
||||
if [[ ${IAM_VERBOSE} < ${V} ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
for message; do
|
||||
echo "${message}"
|
||||
done
|
||||
}
|
||||
|
||||
# Just like iam::log::info, but no \n, so you can make a progress bar
|
||||
iam::log::progress() {
|
||||
for message; do
|
||||
echo -e -n "${message}"
|
||||
done
|
||||
}
|
||||
|
||||
iam::log::info_from_stdin() {
|
||||
local messages=()
|
||||
while read -r line; do
|
||||
messages+=("${line}")
|
||||
done
|
||||
|
||||
iam::log::info "${messages[@]}"
|
||||
}
|
||||
|
||||
# Print a status line. Formatted to show up in a stream of output.
|
||||
iam::log::status() {
|
||||
local V="${V:-0}"
|
||||
if [[ ${IAM_VERBOSE} < ${V} ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
timestamp=$(date +"[%m%d %H:%M:%S]")
|
||||
echo "+++ ${timestamp} ${1}"
|
||||
shift
|
||||
for message; do
|
||||
echo " ${message}"
|
||||
done
|
||||
}
|
@ -0,0 +1,597 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# This file creates release artifacts (tar files, container images) that are
|
||||
# ready to distribute to install or distribute to end users.
|
||||
|
||||
###############################################################################
|
||||
# Most of the ::release:: namespace functions have been moved to
|
||||
# github.com/iam/release. Have a look in that repo and specifically in
|
||||
# lib/releaselib.sh for ::release::-related functionality.
|
||||
###############################################################################
|
||||
|
||||
# Tencent cos configuration
|
||||
readonly BUCKET="marmotedu-1254073058"
|
||||
readonly REGION="ap-beijing"
|
||||
readonly COS_RELEASE_DIR="iam-release"
|
||||
readonly COSTOOL="coscmd"
|
||||
|
||||
# This is where the final release artifacts are created locally
|
||||
readonly RELEASE_STAGE="${LOCAL_OUTPUT_ROOT}/release-stage"
|
||||
readonly RELEASE_TARS="${LOCAL_OUTPUT_ROOT}/release-tars"
|
||||
readonly RELEASE_IMAGES="${LOCAL_OUTPUT_ROOT}/release-images"
|
||||
|
||||
# IAM github account info
|
||||
readonly IAM_GITHUB_ORG=marmotedu
|
||||
readonly IAM_GITHUB_REPO=iam
|
||||
|
||||
readonly ARTIFACT=iam.tar.gz
|
||||
readonly CHECKSUM=${ARTIFACT}.sha1sum
|
||||
|
||||
IAM_BUILD_CONFORMANCE=${IAM_BUILD_CONFORMANCE:-y}
|
||||
IAM_BUILD_PULL_LATEST_IMAGES=${IAM_BUILD_PULL_LATEST_IMAGES:-y}
|
||||
|
||||
# Validate a ci version
|
||||
#
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# version
|
||||
# Returns:
|
||||
# If version is a valid ci version
|
||||
# Sets: (e.g. for '1.2.3-alpha.4.56+abcdef12345678')
|
||||
# VERSION_MAJOR (e.g. '1')
|
||||
# VERSION_MINOR (e.g. '2')
|
||||
# VERSION_PATCH (e.g. '3')
|
||||
# VERSION_PRERELEASE (e.g. 'alpha')
|
||||
# VERSION_PRERELEASE_REV (e.g. '4')
|
||||
# VERSION_BUILD_INFO (e.g. '.56+abcdef12345678')
|
||||
# VERSION_COMMITS (e.g. '56')
|
||||
function iam::release::parse_and_validate_ci_version() {
|
||||
# Accept things like "v1.2.3-alpha.4.56+abcdef12345678" or "v1.2.3-beta.4"
|
||||
local -r version_regex="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)-([a-zA-Z0-9]+)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*)\\+[0-9a-f]{7,40})?$"
|
||||
local -r version="${1-}"
|
||||
[[ "${version}" =~ ${version_regex} ]] || {
|
||||
iam::log::error "Invalid ci version: '${version}', must match regex ${version_regex}"
|
||||
return 1
|
||||
}
|
||||
|
||||
# The VERSION variables are used when this file is sourced, hence
|
||||
# the shellcheck SC2034 'appears unused' warning is to be ignored.
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
VERSION_MAJOR="${BASH_REMATCH[1]}"
|
||||
# shellcheck disable=SC2034
|
||||
VERSION_MINOR="${BASH_REMATCH[2]}"
|
||||
# shellcheck disable=SC2034
|
||||
VERSION_PATCH="${BASH_REMATCH[3]}"
|
||||
# shellcheck disable=SC2034
|
||||
VERSION_PRERELEASE="${BASH_REMATCH[4]}"
|
||||
# shellcheck disable=SC2034
|
||||
VERSION_PRERELEASE_REV="${BASH_REMATCH[5]}"
|
||||
# shellcheck disable=SC2034
|
||||
VERSION_BUILD_INFO="${BASH_REMATCH[6]}"
|
||||
# shellcheck disable=SC2034
|
||||
VERSION_COMMITS="${BASH_REMATCH[7]}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build final release artifacts
|
||||
function iam::release::clean_cruft() {
|
||||
# Clean out cruft
|
||||
find "${RELEASE_STAGE}" -name '*~' -exec rm {} \;
|
||||
find "${RELEASE_STAGE}" -name '#*#' -exec rm {} \;
|
||||
find "${RELEASE_STAGE}" -name '.DS*' -exec rm {} \;
|
||||
}
|
||||
|
||||
function iam::release::package_tarballs() {
|
||||
# Clean out any old releases
|
||||
rm -rf "${RELEASE_STAGE}" "${RELEASE_TARS}" "${RELEASE_IMAGES}"
|
||||
mkdir -p "${RELEASE_TARS}"
|
||||
iam::release::package_src_tarball &
|
||||
iam::release::package_client_tarballs &
|
||||
iam::release::package_iam_manifests_tarball &
|
||||
iam::release::package_server_tarballs &
|
||||
iam::util::wait-for-jobs || { iam::log::error "previous tarball phase failed"; return 1; }
|
||||
|
||||
iam::release::package_final_tarball & # _final depends on some of the previous phases
|
||||
iam::util::wait-for-jobs || { iam::log::error "previous tarball phase failed"; return 1; }
|
||||
}
|
||||
|
||||
function iam::release::updload_tarballs() {
|
||||
iam::log::info "upload ${RELEASE_TARS}/* to cos bucket ${BUCKET}."
|
||||
for file in $(ls ${RELEASE_TARS}/*)
|
||||
do
|
||||
if [ "${COSTOOL}" == "coscli" ];then
|
||||
coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/${IAM_GIT_VERSION}/${file##*/}"
|
||||
coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/latest/${file##*/}"
|
||||
else
|
||||
coscmd upload "${file}" "${COS_RELEASE_DIR}/${IAM_GIT_VERSION}/"
|
||||
coscmd upload "${file}" "${COS_RELEASE_DIR}/latest/"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Package the source code we built, for compliance/licensing/audit/yadda.
|
||||
function iam::release::package_src_tarball() {
|
||||
local -r src_tarball="${RELEASE_TARS}/iam-src.tar.gz"
|
||||
iam::log::status "Building tarball: src"
|
||||
if [[ "${IAM_GIT_TREE_STATE-}" = 'clean' ]]; then
|
||||
git archive -o "${src_tarball}" HEAD
|
||||
else
|
||||
find "${IAM_ROOT}" -mindepth 1 -maxdepth 1 \
|
||||
! \( \
|
||||
\( -path "${IAM_ROOT}"/_\* -o \
|
||||
-path "${IAM_ROOT}"/.git\* -o \
|
||||
-path "${IAM_ROOT}"/.gitignore\* -o \
|
||||
-path "${IAM_ROOT}"/.gsemver.yaml\* -o \
|
||||
-path "${IAM_ROOT}"/.config\* -o \
|
||||
-path "${IAM_ROOT}"/.chglog\* -o \
|
||||
-path "${IAM_ROOT}"/.gitlint -o \
|
||||
-path "${IAM_ROOT}"/.golangci.yaml -o \
|
||||
-path "${IAM_ROOT}"/.goreleaser.yml -o \
|
||||
-path "${IAM_ROOT}"/.note.md -o \
|
||||
-path "${IAM_ROOT}"/.todo.md \
|
||||
\) -prune \
|
||||
\) -print0 \
|
||||
| "${TAR}" czf "${src_tarball}" --transform "s|${IAM_ROOT#/*}|iam|" --null -T -
|
||||
fi
|
||||
}
|
||||
|
||||
# Package up all of the server binaries
|
||||
function iam::release::package_server_tarballs() {
|
||||
# Find all of the built client binaries
|
||||
local long_platforms=("${LOCAL_OUTPUT_BINPATH}"/*/*)
|
||||
if [[ -n ${IAM_BUILD_PLATFORMS-} ]]; then
|
||||
read -ra long_platforms <<< "${IAM_BUILD_PLATFORMS}"
|
||||
fi
|
||||
|
||||
for platform_long in "${long_platforms[@]}"; do
|
||||
local platform
|
||||
local platform_tag
|
||||
platform=${platform_long##${LOCAL_OUTPUT_BINPATH}/} # Strip LOCAL_OUTPUT_BINPATH
|
||||
platform_tag=${platform/\//-} # Replace a "/" for a "-"
|
||||
iam::log::status "Starting tarball: server $platform_tag"
|
||||
|
||||
(
|
||||
local release_stage="${RELEASE_STAGE}/server/${platform_tag}/iam"
|
||||
rm -rf "${release_stage}"
|
||||
mkdir -p "${release_stage}/server/bin"
|
||||
|
||||
local server_bins=("${IAM_SERVER_BINARIES[@]}")
|
||||
|
||||
# This fancy expression will expand to prepend a path
|
||||
# (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the
|
||||
# server_bins array.
|
||||
cp "${server_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \
|
||||
"${release_stage}/server/bin/"
|
||||
|
||||
iam::release::clean_cruft
|
||||
|
||||
local package_name="${RELEASE_TARS}/iam-server-${platform_tag}.tar.gz"
|
||||
iam::release::create_tarball "${package_name}" "${release_stage}/.."
|
||||
) &
|
||||
done
|
||||
|
||||
iam::log::status "Waiting on tarballs"
|
||||
iam::util::wait-for-jobs || { iam::log::error "server tarball creation failed"; exit 1; }
|
||||
}
|
||||
|
||||
# Package up all of the cross compiled clients. Over time this should grow into
|
||||
# a full SDK
|
||||
function iam::release::package_client_tarballs() {
|
||||
# Find all of the built client binaries
|
||||
local long_platforms=("${LOCAL_OUTPUT_BINPATH}"/*/*)
|
||||
if [[ -n ${IAM_BUILD_PLATFORMS-} ]]; then
|
||||
read -ra long_platforms <<< "${IAM_BUILD_PLATFORMS}"
|
||||
fi
|
||||
|
||||
for platform_long in "${long_platforms[@]}"; do
|
||||
local platform
|
||||
local platform_tag
|
||||
platform=${platform_long##${LOCAL_OUTPUT_BINPATH}/} # Strip LOCAL_OUTPUT_BINPATH
|
||||
platform_tag=${platform/\//-} # Replace a "/" for a "-"
|
||||
iam::log::status "Starting tarball: client $platform_tag"
|
||||
|
||||
(
|
||||
local release_stage="${RELEASE_STAGE}/client/${platform_tag}/iam"
|
||||
rm -rf "${release_stage}"
|
||||
mkdir -p "${release_stage}/client/bin"
|
||||
|
||||
local client_bins=("${IAM_CLIENT_BINARIES[@]}")
|
||||
|
||||
# This fancy expression will expand to prepend a path
|
||||
# (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the
|
||||
# client_bins array.
|
||||
cp "${client_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \
|
||||
"${release_stage}/client/bin/"
|
||||
|
||||
iam::release::clean_cruft
|
||||
|
||||
local package_name="${RELEASE_TARS}/iam-client-${platform_tag}.tar.gz"
|
||||
iam::release::create_tarball "${package_name}" "${release_stage}/.."
|
||||
) &
|
||||
done
|
||||
|
||||
iam::log::status "Waiting on tarballs"
|
||||
iam::util::wait-for-jobs || { iam::log::error "client tarball creation failed"; exit 1; }
|
||||
}
|
||||
|
||||
# Package up all of the server binaries in docker images
|
||||
function iam::release::build_server_images() {
|
||||
# Clean out any old images
|
||||
rm -rf "${RELEASE_IMAGES}"
|
||||
local platform
|
||||
for platform in "${IAM_SERVER_PLATFORMS[@]}"; do
|
||||
local platform_tag
|
||||
local arch
|
||||
platform_tag=${platform/\//-} # Replace a "/" for a "-"
|
||||
arch=$(basename "${platform}")
|
||||
iam::log::status "Building images: $platform_tag"
|
||||
|
||||
local release_stage
|
||||
release_stage="${RELEASE_STAGE}/server/${platform_tag}/iam"
|
||||
rm -rf "${release_stage}"
|
||||
mkdir -p "${release_stage}/server/bin"
|
||||
|
||||
# This fancy expression will expand to prepend a path
|
||||
# (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the
|
||||
# IAM_SERVER_IMAGE_BINARIES array.
|
||||
cp "${IAM_SERVER_IMAGE_BINARIES[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \
|
||||
"${release_stage}/server/bin/"
|
||||
|
||||
iam::release::create_docker_images_for_server "${release_stage}/server/bin" "${arch}"
|
||||
done
|
||||
}
|
||||
|
||||
function iam::release::md5() {
|
||||
if which md5 >/dev/null 2>&1; then
|
||||
md5 -q "$1"
|
||||
else
|
||||
md5sum "$1" | awk '{ print $1 }'
|
||||
fi
|
||||
}
|
||||
|
||||
function iam::release::sha1() {
|
||||
if which sha1sum >/dev/null 2>&1; then
|
||||
sha1sum "$1" | awk '{ print $1 }'
|
||||
else
|
||||
shasum -a1 "$1" | awk '{ print $1 }'
|
||||
fi
|
||||
}
|
||||
|
||||
function iam::release::build_conformance_image() {
|
||||
local -r arch="$1"
|
||||
local -r registry="$2"
|
||||
local -r version="$3"
|
||||
local -r save_dir="${4-}"
|
||||
iam::log::status "Building conformance image for arch: ${arch}"
|
||||
ARCH="${arch}" REGISTRY="${registry}" VERSION="${version}" \
|
||||
make -C cluster/images/conformance/ build >/dev/null
|
||||
|
||||
local conformance_tag
|
||||
conformance_tag="${registry}/conformance-${arch}:${version}"
|
||||
if [[ -n "${save_dir}" ]]; then
|
||||
"${DOCKER[@]}" save "${conformance_tag}" > "${save_dir}/conformance-${arch}.tar"
|
||||
fi
|
||||
iam::log::status "Deleting conformance image ${conformance_tag}"
|
||||
"${DOCKER[@]}" rmi "${conformance_tag}" &>/dev/null || true
|
||||
}
|
||||
|
||||
# This builds all the release docker images (One docker image per binary)
|
||||
# Args:
|
||||
# $1 - binary_dir, the directory to save the tared images to.
|
||||
# $2 - arch, architecture for which we are building docker images.
|
||||
function iam::release::create_docker_images_for_server() {
|
||||
# Create a sub-shell so that we don't pollute the outer environment
|
||||
(
|
||||
local binary_dir
|
||||
local arch
|
||||
local binaries
|
||||
local images_dir
|
||||
binary_dir="$1"
|
||||
arch="$2"
|
||||
binaries=$(iam::build::get_docker_wrapped_binaries "${arch}")
|
||||
images_dir="${RELEASE_IMAGES}/${arch}"
|
||||
mkdir -p "${images_dir}"
|
||||
|
||||
# k8s.gcr.io is the constant tag in the docker archives, this is also the default for config scripts in GKE.
|
||||
# We can use IAM_DOCKER_REGISTRY to include and extra registry in the docker archive.
|
||||
# If we use IAM_DOCKER_REGISTRY="k8s.gcr.io", then the extra tag (same) is ignored, see release_docker_image_tag below.
|
||||
local -r docker_registry="k8s.gcr.io"
|
||||
# Docker tags cannot contain '+'
|
||||
local docker_tag="${IAM_GIT_VERSION/+/_}"
|
||||
if [[ -z "${docker_tag}" ]]; then
|
||||
iam::log::error "git version information missing; cannot create Docker tag"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# provide `--pull` argument to `docker build` if `IAM_BUILD_PULL_LATEST_IMAGES`
|
||||
# is set to y or Y; otherwise try to build the image without forcefully
|
||||
# pulling the latest base image.
|
||||
local docker_build_opts
|
||||
docker_build_opts=
|
||||
if [[ "${IAM_BUILD_PULL_LATEST_IMAGES}" =~ [yY] ]]; then
|
||||
docker_build_opts='--pull'
|
||||
fi
|
||||
|
||||
for wrappable in $binaries; do
|
||||
|
||||
local binary_name=${wrappable%%,*}
|
||||
local base_image=${wrappable##*,}
|
||||
local binary_file_path="${binary_dir}/${binary_name}"
|
||||
local docker_build_path="${binary_file_path}.dockerbuild"
|
||||
local docker_file_path="${docker_build_path}/Dockerfile"
|
||||
local docker_image_tag="${docker_registry}/${binary_name}-${arch}:${docker_tag}"
|
||||
|
||||
iam::log::status "Starting docker build for image: ${binary_name}-${arch}"
|
||||
(
|
||||
rm -rf "${docker_build_path}"
|
||||
mkdir -p "${docker_build_path}"
|
||||
ln "${binary_file_path}" "${docker_build_path}/${binary_name}"
|
||||
ln "${IAM_ROOT}/build/nsswitch.conf" "${docker_build_path}/nsswitch.conf"
|
||||
chmod 0644 "${docker_build_path}/nsswitch.conf"
|
||||
cat <<EOF > "${docker_file_path}"
|
||||
FROM ${base_image}
|
||||
COPY ${binary_name} /usr/local/bin/${binary_name}
|
||||
EOF
|
||||
# ensure /etc/nsswitch.conf exists so go's resolver respects /etc/hosts
|
||||
if [[ "${base_image}" =~ busybox ]]; then
|
||||
echo "COPY nsswitch.conf /etc/" >> "${docker_file_path}"
|
||||
fi
|
||||
|
||||
"${DOCKER[@]}" build ${docker_build_opts:+"${docker_build_opts}"} -q -t "${docker_image_tag}" "${docker_build_path}" >/dev/null
|
||||
# If we are building an official/alpha/beta release we want to keep
|
||||
# docker images and tag them appropriately.
|
||||
local -r release_docker_image_tag="${IAM_DOCKER_REGISTRY-$docker_registry}/${binary_name}-${arch}:${IAM_DOCKER_IMAGE_TAG-$docker_tag}"
|
||||
if [[ "${release_docker_image_tag}" != "${docker_image_tag}" ]]; then
|
||||
iam::log::status "Tagging docker image ${docker_image_tag} as ${release_docker_image_tag}"
|
||||
"${DOCKER[@]}" rmi "${release_docker_image_tag}" 2>/dev/null || true
|
||||
"${DOCKER[@]}" tag "${docker_image_tag}" "${release_docker_image_tag}" 2>/dev/null
|
||||
fi
|
||||
"${DOCKER[@]}" save -o "${binary_file_path}.tar" "${docker_image_tag}" "${release_docker_image_tag}"
|
||||
echo "${docker_tag}" > "${binary_file_path}.docker_tag"
|
||||
rm -rf "${docker_build_path}"
|
||||
ln "${binary_file_path}.tar" "${images_dir}/"
|
||||
|
||||
iam::log::status "Deleting docker image ${docker_image_tag}"
|
||||
"${DOCKER[@]}" rmi "${docker_image_tag}" &>/dev/null || true
|
||||
) &
|
||||
done
|
||||
|
||||
if [[ "${IAM_BUILD_CONFORMANCE}" =~ [yY] ]]; then
|
||||
iam::release::build_conformance_image "${arch}" "${docker_registry}" \
|
||||
"${docker_tag}" "${images_dir}" &
|
||||
fi
|
||||
|
||||
iam::util::wait-for-jobs || { iam::log::error "previous Docker build failed"; return 1; }
|
||||
iam::log::status "Docker builds done"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
# This will pack iam-system manifests files for distros such as COS.
|
||||
function iam::release::package_iam_manifests_tarball() {
|
||||
iam::log::status "Building tarball: manifests"
|
||||
|
||||
local src_dir="${IAM_ROOT}/deployments"
|
||||
|
||||
local release_stage="${RELEASE_STAGE}/manifests/iam"
|
||||
rm -rf "${release_stage}"
|
||||
|
||||
local dst_dir="${release_stage}"
|
||||
mkdir -p "${dst_dir}"
|
||||
cp -r ${src_dir}/* "${dst_dir}"
|
||||
#cp "${src_dir}/iam-apiserver.yaml" "${dst_dir}"
|
||||
#cp "${src_dir}/iam-authz-server.yaml" "${dst_dir}"
|
||||
#cp "${src_dir}/iam-pump.yaml" "${dst_dir}"
|
||||
#cp "${src_dir}/iam-watcher.yaml" "${dst_dir}"
|
||||
#cp "${IAM_ROOT}/cluster/gce/gci/health-monitor.sh" "${dst_dir}/health-monitor.sh"
|
||||
|
||||
iam::release::clean_cruft
|
||||
|
||||
local package_name="${RELEASE_TARS}/iam-manifests.tar.gz"
|
||||
iam::release::create_tarball "${package_name}" "${release_stage}/.."
|
||||
}
|
||||
|
||||
# This is all the platform-independent stuff you need to run/install iam.
|
||||
# Arch-specific binaries will need to be downloaded separately (possibly by
|
||||
# using the bundled cluster/get-iam-binaries.sh script).
|
||||
# Included in this tarball:
|
||||
# - Cluster spin up/down scripts and configs for various cloud providers
|
||||
# - Tarballs for manifest configs that are ready to be uploaded
|
||||
# - Examples (which may or may not still work)
|
||||
# - The remnants of the docs/ directory
|
||||
function iam::release::package_final_tarball() {
|
||||
iam::log::status "Building tarball: final"
|
||||
|
||||
# This isn't a "full" tarball anymore, but the release lib still expects
|
||||
# artifacts under "full/iam/"
|
||||
local release_stage="${RELEASE_STAGE}/full/iam"
|
||||
rm -rf "${release_stage}"
|
||||
mkdir -p "${release_stage}"
|
||||
|
||||
mkdir -p "${release_stage}/client"
|
||||
cat <<EOF > "${release_stage}/client/README"
|
||||
Client binaries are no longer included in the IAM final tarball.
|
||||
|
||||
Run release/get-iam-binaries.sh to download client and server binaries.
|
||||
EOF
|
||||
|
||||
# We want everything in /scripts.
|
||||
mkdir -p "${release_stage}/release"
|
||||
cp -R "${IAM_ROOT}/scripts/release" "${release_stage}/"
|
||||
cat <<EOF > "${release_stage}/release/get-iam-binaries.sh"
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# This file download iam client and server binaries from tencent cos bucket.
|
||||
|
||||
os=linux arch=amd64 version=${IAM_GIT_VERSION} && wget https://${BUCKET}.cos.${REGION}.myqcloud.com/${COS_RELEASE_DIR}/\$version/{iam-client-\$os-\$arch.tar.gz,iam-server-\$os-\$arch.tar.gz}
|
||||
EOF
|
||||
chmod +x ${release_stage}/release/get-iam-binaries.sh
|
||||
|
||||
mkdir -p "${release_stage}/server"
|
||||
cp "${RELEASE_TARS}/iam-manifests.tar.gz" "${release_stage}/server/"
|
||||
cat <<EOF > "${release_stage}/server/README"
|
||||
Server binary tarballs are no longer included in the IAM final tarball.
|
||||
|
||||
Run release/get-iam-binaries.sh to download client and server binaries.
|
||||
EOF
|
||||
|
||||
# Include hack/lib as a dependency for the cluster/ scripts
|
||||
#mkdir -p "${release_stage}/hack"
|
||||
#cp -R "${IAM_ROOT}/hack/lib" "${release_stage}/hack/"
|
||||
|
||||
cp -R ${IAM_ROOT}/{docs,configs,scripts,deployments,init,README.md,LICENSE} "${release_stage}/"
|
||||
|
||||
echo "${IAM_GIT_VERSION}" > "${release_stage}/version"
|
||||
|
||||
iam::release::clean_cruft
|
||||
|
||||
local package_name="${RELEASE_TARS}/${ARTIFACT}"
|
||||
iam::release::create_tarball "${package_name}" "${release_stage}/.."
|
||||
}
|
||||
|
||||
# Build a release tarball. $1 is the output tar name. $2 is the base directory
|
||||
# of the files to be packaged. This assumes that ${2}/iamis what is
|
||||
# being packaged.
|
||||
function iam::release::create_tarball() {
|
||||
iam::build::ensure_tar
|
||||
|
||||
local tarfile=$1
|
||||
local stagingdir=$2
|
||||
|
||||
"${TAR}" czf "${tarfile}" -C "${stagingdir}" iam --owner=0 --group=0
|
||||
}
|
||||
|
||||
function iam::release::install_github_release(){
|
||||
GO111MODULE=on go install github.com/github-release/github-release@latest
|
||||
}
|
||||
|
||||
# Require the following tools:
|
||||
# - github-release
|
||||
# - gsemver
|
||||
# - git-chglog
|
||||
# - coscmd or coscli
|
||||
function iam::release::verify_prereqs(){
|
||||
if [ -z "$(which github-release 2>/dev/null)" ]; then
|
||||
iam::log::info "'github-release' tool not installed, try to install it."
|
||||
|
||||
if ! iam::release::install_github_release; then
|
||||
iam::log::error "failed to install 'github-release'"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$(which git-chglog 2>/dev/null)" ]; then
|
||||
iam::log::info "'git-chglog' tool not installed, try to install it."
|
||||
|
||||
if ! go install github.com/git-chglog/git-chglog/cmd/git-chglog@latest &>/dev/null; then
|
||||
iam::log::error "failed to install 'git-chglog'"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$(which gsemver 2>/dev/null)" ]; then
|
||||
iam::log::info "'gsemver' tool not installed, try to install it."
|
||||
|
||||
if ! go install github.com/arnaud-deprez/gsemver@latest &>/dev/null; then
|
||||
iam::log::error "failed to install 'gsemver'"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -z "$(which ${COSTOOL} 2>/dev/null)" ]; then
|
||||
iam::log::info "${COSTOOL} tool not installed, try to install it."
|
||||
|
||||
if ! make -C "${IAM_ROOT}" tools.install.${COSTOOL}; then
|
||||
iam::log::error "failed to install ${COSTOOL}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${TENCENT_SECRET_ID}" -o -z "${TENCENT_SECRET_KEY}" ];then
|
||||
iam::log::error "can not find env: TENCENT_SECRET_ID and TENCENT_SECRET_KEY"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "${COSTOOL}" == "coscli" ];then
|
||||
if [ ! -f "${HOME}/.cos.yaml" ];then
|
||||
cat << EOF > "${HOME}/.cos.yaml"
|
||||
cos:
|
||||
base:
|
||||
secretid: ${TENCENT_SECRET_ID}
|
||||
secretkey: ${TENCENT_SECRET_KEY}
|
||||
sessiontoken: ""
|
||||
buckets:
|
||||
- name: ${BUCKET}
|
||||
alias: ${BUCKET}
|
||||
region: ${REGION}
|
||||
EOF
|
||||
fi
|
||||
else
|
||||
if [ ! -f "${HOME}/.cos.conf" ];then
|
||||
cat << EOF > "${HOME}/.cos.conf"
|
||||
[common]
|
||||
secret_id = ${TENCENT_SECRET_ID}
|
||||
secret_key = ${TENCENT_SECRET_KEY}
|
||||
bucket = ${BUCKET}
|
||||
region =${REGION}
|
||||
max_thread = 5
|
||||
part_size = 1
|
||||
schema = https
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Create a github release with specified tarballs.
|
||||
# NOTICE: Must export 'GITHUB_TOKEN' env in the shell, details:
|
||||
# https://github.com/github-release/github-release
|
||||
function iam::release::github_release() {
|
||||
# create a github release
|
||||
iam::log::info "create a new github release with tag ${IAM_GIT_VERSION}"
|
||||
github-release release \
|
||||
--user ${IAM_GITHUB_ORG} \
|
||||
--repo ${IAM_GITHUB_REPO} \
|
||||
--tag ${IAM_GIT_VERSION} \
|
||||
--description "" \
|
||||
--pre-release
|
||||
|
||||
# update iam tarballs
|
||||
iam::log::info "upload ${ARTIFACT} to release ${IAM_GIT_VERSION}"
|
||||
github-release upload \
|
||||
--user ${IAM_GITHUB_ORG} \
|
||||
--repo ${IAM_GITHUB_REPO} \
|
||||
--tag ${IAM_GIT_VERSION} \
|
||||
--name ${ARTIFACT} \
|
||||
--file ${RELEASE_TARS}/${ARTIFACT}
|
||||
|
||||
iam::log::info "upload iam-src.tar.gz to release ${IAM_GIT_VERSION}"
|
||||
github-release upload \
|
||||
--user ${IAM_GITHUB_ORG} \
|
||||
--repo ${IAM_GITHUB_REPO} \
|
||||
--tag ${IAM_GIT_VERSION} \
|
||||
--name "iam-src.tar.gz" \
|
||||
--file ${RELEASE_TARS}/iam-src.tar.gz
|
||||
}
|
||||
|
||||
function iam::release::generate_changelog() {
|
||||
iam::log::info "generate CHANGELOG-${IAM_GIT_VERSION#v}.md and commit it"
|
||||
|
||||
git-chglog ${IAM_GIT_VERSION} > ${IAM_ROOT}/CHANGELOG/CHANGELOG-${IAM_GIT_VERSION#v}.md
|
||||
|
||||
set +o errexit
|
||||
git add ${IAM_ROOT}/CHANGELOG/CHANGELOG-${IAM_GIT_VERSION#v}.md
|
||||
git commit -a -m "docs(changelog): add CHANGELOG-${IAM_GIT_VERSION#v}.md"
|
||||
git push -f origin master # 最后将 CHANGELOG 也 push 上去
|
||||
}
|
||||
|
@ -0,0 +1,683 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
function iam::util::sourced_variable {
|
||||
# Call this function to tell shellcheck that a variable is supposed to
|
||||
# be used from other calling context. This helps quiet an "unused
|
||||
# variable" warning from shellcheck and also document your code.
|
||||
true
|
||||
}
|
||||
|
||||
iam::util::sortable_date() {
|
||||
date "+%Y%m%d-%H%M%S"
|
||||
}
|
||||
|
||||
# arguments: target, item1, item2, item3, ...
|
||||
# returns 0 if target is in the given items, 1 otherwise.
|
||||
iam::util::array_contains() {
|
||||
local search="$1"
|
||||
local element
|
||||
shift
|
||||
for element; do
|
||||
if [[ "${element}" == "${search}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
iam::util::wait_for_url() {
|
||||
local url=$1
|
||||
local prefix=${2:-}
|
||||
local wait=${3:-1}
|
||||
local times=${4:-30}
|
||||
local maxtime=${5:-1}
|
||||
|
||||
command -v curl >/dev/null || {
|
||||
iam::log::usage "curl must be installed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
local i
|
||||
for i in $(seq 1 "${times}"); do
|
||||
local out
|
||||
if out=$(curl --max-time "${maxtime}" -gkfs "${url}" 2>/dev/null); then
|
||||
iam::log::status "On try ${i}, ${prefix}: ${out}"
|
||||
return 0
|
||||
fi
|
||||
sleep "${wait}"
|
||||
done
|
||||
iam::log::error "Timed out waiting for ${prefix} to answer at ${url}; tried ${times} waiting ${wait} between each"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Example: iam::util::wait_for_success 120 5 "iamctl get nodes|grep localhost"
|
||||
# arguments: wait time, sleep time, shell command
|
||||
# returns 0 if the shell command get output, 1 otherwise.
|
||||
iam::util::wait_for_success(){
|
||||
local wait_time="$1"
|
||||
local sleep_time="$2"
|
||||
local cmd="$3"
|
||||
while [ "$wait_time" -gt 0 ]; do
|
||||
if eval "$cmd"; then
|
||||
return 0
|
||||
else
|
||||
sleep "$sleep_time"
|
||||
wait_time=$((wait_time-sleep_time))
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Example: iam::util::trap_add 'echo "in trap DEBUG"' DEBUG
|
||||
# See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal
|
||||
iam::util::trap_add() {
|
||||
local trap_add_cmd
|
||||
trap_add_cmd=$1
|
||||
shift
|
||||
|
||||
for trap_add_name in "$@"; do
|
||||
local existing_cmd
|
||||
local new_cmd
|
||||
|
||||
# Grab the currently defined trap commands for this trap
|
||||
existing_cmd=$(trap -p "${trap_add_name}" | awk -F"'" '{print $2}')
|
||||
|
||||
if [[ -z "${existing_cmd}" ]]; then
|
||||
new_cmd="${trap_add_cmd}"
|
||||
else
|
||||
new_cmd="${trap_add_cmd};${existing_cmd}"
|
||||
fi
|
||||
|
||||
# Assign the test. Disable the shellcheck warning telling that trap
|
||||
# commands should be single quoted to avoid evaluating them at this
|
||||
# point instead evaluating them at run time. The logic of adding new
|
||||
# commands to a single trap requires them to be evaluated right away.
|
||||
# shellcheck disable=SC2064
|
||||
trap "${new_cmd}" "${trap_add_name}"
|
||||
done
|
||||
}
|
||||
|
||||
# Opposite of iam::util::ensure-temp-dir()
|
||||
iam::util::cleanup-temp-dir() {
|
||||
rm -rf "${IAM_TEMP}"
|
||||
}
|
||||
|
||||
# Create a temp dir that'll be deleted at the end of this bash session.
|
||||
#
|
||||
# Vars set:
|
||||
# IAM_TEMP
|
||||
iam::util::ensure-temp-dir() {
|
||||
if [[ -z ${IAM_TEMP-} ]]; then
|
||||
IAM_TEMP=$(mktemp -d 2>/dev/null || mktemp -d -t iamrnetes.XXXXXX)
|
||||
iam::util::trap_add iam::util::cleanup-temp-dir EXIT
|
||||
fi
|
||||
}
|
||||
|
||||
iam::util::host_os() {
|
||||
local host_os
|
||||
case "$(uname -s)" in
|
||||
Darwin)
|
||||
host_os=darwin
|
||||
;;
|
||||
Linux)
|
||||
host_os=linux
|
||||
;;
|
||||
*)
|
||||
iam::log::error "Unsupported host OS. Must be Linux or Mac OS X."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo "${host_os}"
|
||||
}
|
||||
|
||||
iam::util::host_arch() {
|
||||
local host_arch
|
||||
case "$(uname -m)" in
|
||||
x86_64*)
|
||||
host_arch=amd64
|
||||
;;
|
||||
i?86_64*)
|
||||
host_arch=amd64
|
||||
;;
|
||||
amd64*)
|
||||
host_arch=amd64
|
||||
;;
|
||||
aarch64*)
|
||||
host_arch=arm64
|
||||
;;
|
||||
arm64*)
|
||||
host_arch=arm64
|
||||
;;
|
||||
arm*)
|
||||
host_arch=arm
|
||||
;;
|
||||
i?86*)
|
||||
host_arch=x86
|
||||
;;
|
||||
s390x*)
|
||||
host_arch=s390x
|
||||
;;
|
||||
ppc64le*)
|
||||
host_arch=ppc64le
|
||||
;;
|
||||
*)
|
||||
iam::log::error "Unsupported host arch. Must be x86_64, 386, arm, arm64, s390x or ppc64le."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo "${host_arch}"
|
||||
}
|
||||
|
||||
# This figures out the host platform without relying on golang. We need this as
|
||||
# we don't want a golang install to be a prerequisite to building yet we need
|
||||
# this info to figure out where the final binaries are placed.
|
||||
iam::util::host_platform() {
|
||||
echo "$(iam::util::host_os)/$(iam::util::host_arch)"
|
||||
}
|
||||
|
||||
# looks for $1 in well-known output locations for the platform ($2)
|
||||
# $IAM_ROOT must be set
|
||||
iam::util::find-binary-for-platform() {
|
||||
local -r lookfor="$1"
|
||||
local -r platform="$2"
|
||||
local locations=(
|
||||
"${IAM_ROOT}/_output/bin/${lookfor}"
|
||||
"${IAM_ROOT}/_output/${platform}/${lookfor}"
|
||||
"${IAM_ROOT}/_output/local/bin/${platform}/${lookfor}"
|
||||
"${IAM_ROOT}/_output/platforms/${platform}/${lookfor}"
|
||||
)
|
||||
|
||||
# List most recently-updated location.
|
||||
local -r bin=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 )
|
||||
echo -n "${bin}"
|
||||
}
|
||||
|
||||
# looks for $1 in well-known output locations for the host platform
|
||||
# $IAM_ROOT must be set
|
||||
iam::util::find-binary() {
|
||||
iam::util::find-binary-for-platform "$1" "$(iam::util::host_platform)"
|
||||
}
|
||||
|
||||
# Run all known doc generators (today gendocs and genman for iamctl)
|
||||
# $1 is the directory to put those generated documents
|
||||
iam::util::gen-docs() {
|
||||
local dest="$1"
|
||||
|
||||
# Find binary
|
||||
gendocs=$(iam::util::find-binary "gendocs")
|
||||
geniamdocs=$(iam::util::find-binary "geniamdocs")
|
||||
genman=$(iam::util::find-binary "genman")
|
||||
genyaml=$(iam::util::find-binary "genyaml")
|
||||
genfeddocs=$(iam::util::find-binary "genfeddocs")
|
||||
|
||||
# TODO: If ${genfeddocs} is not used from anywhere (it isn't used at
|
||||
# least from k/k tree), remove it completely.
|
||||
iam::util::sourced_variable "${genfeddocs}"
|
||||
|
||||
mkdir -p "${dest}/docs/guide/en-US/cmd/iamctl/"
|
||||
"${gendocs}" "${dest}/docs/guide/en-US/cmd/iamctl/"
|
||||
|
||||
mkdir -p "${dest}/docs/guide/en-US/cmd/"
|
||||
"${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-apiserver"
|
||||
"${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-authz-server"
|
||||
"${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-pump"
|
||||
"${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-watcher"
|
||||
"${geniamdocs}" "${dest}/docs/guide/en-US/cmd/iamctl" "iamctl"
|
||||
|
||||
mkdir -p "${dest}/docs/man/man1/"
|
||||
"${genman}" "${dest}/docs/man/man1/" "iam-apiserver"
|
||||
"${genman}" "${dest}/docs/man/man1/" "iam-authz-server"
|
||||
"${genman}" "${dest}/docs/man/man1/" "iam-pump"
|
||||
"${genman}" "${dest}/docs/man/man1/" "iam-watcher"
|
||||
"${genman}" "${dest}/docs/man/man1/" "iamctl"
|
||||
|
||||
mkdir -p "${dest}/docs/guide/en-US/yaml/iamctl/"
|
||||
"${genyaml}" "${dest}/docs/guide/en-US/yaml/iamctl/"
|
||||
|
||||
# create the list of generated files
|
||||
pushd "${dest}" > /dev/null || return 1
|
||||
touch docs/.generated_docs
|
||||
find . -type f | cut -sd / -f 2- | LC_ALL=C sort > docs/.generated_docs
|
||||
popd > /dev/null || return 1
|
||||
}
|
||||
|
||||
# Removes previously generated docs-- we don't want to check them in. $IAM_ROOT
|
||||
# must be set.
|
||||
iam::util::remove-gen-docs() {
|
||||
if [ -e "${IAM_ROOT}/docs/.generated_docs" ]; then
|
||||
# remove all of the old docs; we don't want to check them in.
|
||||
while read -r file; do
|
||||
rm "${IAM_ROOT}/${file}" 2>/dev/null || true
|
||||
done <"${IAM_ROOT}/docs/.generated_docs"
|
||||
# The docs/.generated_docs file lists itself, so we don't need to explicitly
|
||||
# delete it.
|
||||
fi
|
||||
}
|
||||
|
||||
# Returns the name of the upstream remote repository name for the local git
|
||||
# repo, e.g. "upstream" or "origin".
|
||||
iam::util::git_upstream_remote_name() {
|
||||
git remote -v | grep fetch |\
|
||||
grep -E 'github.com[/:]marmotedu/iam|marmotedu.io/iam' |\
|
||||
head -n 1 | awk '{print $1}'
|
||||
}
|
||||
|
||||
# Exits script if working directory is dirty. If it's run interactively in the terminal
|
||||
# the user can commit changes in a second terminal. This script will wait.
|
||||
iam::util::ensure_clean_working_dir() {
|
||||
while ! git diff HEAD --exit-code &>/dev/null; do
|
||||
echo -e "\nUnexpected dirty working directory:\n"
|
||||
if tty -s; then
|
||||
git status -s
|
||||
else
|
||||
git diff -a # be more verbose in log files without tty
|
||||
exit 1
|
||||
fi | sed 's/^/ /'
|
||||
echo -e "\nCommit your changes in another terminal and then continue here by pressing enter."
|
||||
read -r
|
||||
done 1>&2
|
||||
}
|
||||
|
||||
# Find the base commit using:
|
||||
# $PULL_BASE_SHA if set (from Prow)
|
||||
# current ref from the remote upstream branch
|
||||
iam::util::base_ref() {
|
||||
local -r git_branch=$1
|
||||
|
||||
if [[ -n ${PULL_BASE_SHA:-} ]]; then
|
||||
echo "${PULL_BASE_SHA}"
|
||||
return
|
||||
fi
|
||||
|
||||
full_branch="$(iam::util::git_upstream_remote_name)/${git_branch}"
|
||||
|
||||
# make sure the branch is valid, otherwise the check will pass erroneously.
|
||||
if ! git describe "${full_branch}" >/dev/null; then
|
||||
# abort!
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "${full_branch}"
|
||||
}
|
||||
|
||||
# Checks whether there are any files matching pattern $2 changed between the
|
||||
# current branch and upstream branch named by $1.
|
||||
# Returns 1 (false) if there are no changes
|
||||
# 0 (true) if there are changes detected.
|
||||
iam::util::has_changes() {
|
||||
local -r git_branch=$1
|
||||
local -r pattern=$2
|
||||
local -r not_pattern=${3:-totallyimpossiblepattern}
|
||||
|
||||
local base_ref
|
||||
base_ref=$(iam::util::base_ref "${git_branch}")
|
||||
echo "Checking for '${pattern}' changes against '${base_ref}'"
|
||||
|
||||
# notice this uses ... to find the first shared ancestor
|
||||
if git diff --name-only "${base_ref}...HEAD" | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then
|
||||
return 0
|
||||
fi
|
||||
# also check for pending changes
|
||||
if git status --porcelain | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then
|
||||
echo "Detected '${pattern}' uncommitted changes."
|
||||
return 0
|
||||
fi
|
||||
echo "No '${pattern}' changes detected."
|
||||
return 1
|
||||
}
|
||||
|
||||
iam::util::download_file() {
|
||||
local -r url=$1
|
||||
local -r destination_file=$2
|
||||
|
||||
rm "${destination_file}" 2&> /dev/null || true
|
||||
|
||||
for i in $(seq 5)
|
||||
do
|
||||
if ! curl -fsSL --retry 3 --keepalive-time 2 "${url}" -o "${destination_file}"; then
|
||||
echo "Downloading ${url} failed. $((5-i)) retries left."
|
||||
sleep 1
|
||||
else
|
||||
echo "Downloading ${url} succeed"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Test whether openssl is installed.
|
||||
# Sets:
|
||||
# OPENSSL_BIN: The path to the openssl binary to use
|
||||
function iam::util::test_openssl_installed {
|
||||
if ! openssl version >& /dev/null; then
|
||||
echo "Failed to run openssl. Please ensure openssl is installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OPENSSL_BIN=$(command -v openssl)
|
||||
}
|
||||
|
||||
# creates a client CA, args are sudo, dest-dir, ca-id, purpose
|
||||
# purpose is dropped in after "key encipherment", you usually want
|
||||
# '"client auth"'
|
||||
# '"server auth"'
|
||||
# '"client auth","server auth"'
|
||||
function iam::util::create_signing_certkey {
|
||||
local sudo=$1
|
||||
local dest_dir=$2
|
||||
local id=$3
|
||||
local purpose=$4
|
||||
# Create client ca
|
||||
${sudo} /usr/bin/env bash -e <<EOF
|
||||
rm -f "${dest_dir}/${id}-ca.crt" "${dest_dir}/${id}-ca.key"
|
||||
${OPENSSL_BIN} req -x509 -sha256 -new -nodes -days 365 -newkey rsa:2048 -keyout "${dest_dir}/${id}-ca.key" -out "${dest_dir}/${id}-ca.crt" -subj "/C=xx/ST=x/L=x/O=x/OU=x/CN=ca/emailAddress=x/"
|
||||
echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment",${purpose}]}}}' > "${dest_dir}/${id}-ca-config.json"
|
||||
EOF
|
||||
}
|
||||
|
||||
# signs a client certificate: args are sudo, dest-dir, CA, filename (roughly), username, groups...
|
||||
function iam::util::create_client_certkey {
|
||||
local sudo=$1
|
||||
local dest_dir=$2
|
||||
local ca=$3
|
||||
local id=$4
|
||||
local cn=${5:-$4}
|
||||
local groups=""
|
||||
local SEP=""
|
||||
shift 5
|
||||
while [ -n "${1:-}" ]; do
|
||||
groups+="${SEP}{\"O\":\"$1\"}"
|
||||
SEP=","
|
||||
shift 1
|
||||
done
|
||||
${sudo} /usr/bin/env bash -e <<EOF
|
||||
cd ${dest_dir}
|
||||
echo '{"CN":"${cn}","names":[${groups}],"hosts":[""],"key":{"algo":"rsa","size":2048}}' | ${CFSSL_BIN} gencert -ca=${ca}.crt -ca-key=${ca}.key -config=${ca}-config.json - | ${CFSSLJSON_BIN} -bare client-${id}
|
||||
mv "client-${id}-key.pem" "client-${id}.key"
|
||||
mv "client-${id}.pem" "client-${id}.crt"
|
||||
rm -f "client-${id}.csr"
|
||||
EOF
|
||||
}
|
||||
|
||||
# signs a serving certificate: args are sudo, dest-dir, ca, filename (roughly), subject, hosts...
|
||||
function iam::util::create_serving_certkey {
|
||||
local sudo=$1
|
||||
local dest_dir=$2
|
||||
local ca=$3
|
||||
local id=$4
|
||||
local cn=${5:-$4}
|
||||
local hosts=""
|
||||
local SEP=""
|
||||
shift 5
|
||||
while [ -n "${1:-}" ]; do
|
||||
hosts+="${SEP}\"$1\""
|
||||
SEP=","
|
||||
shift 1
|
||||
done
|
||||
${sudo} /usr/bin/env bash -e <<EOF
|
||||
cd ${dest_dir}
|
||||
echo '{"CN":"${cn}","hosts":[${hosts}],"key":{"algo":"rsa","size":2048}}' | ${CFSSL_BIN} gencert -ca=${ca}.crt -ca-key=${ca}.key -config=${ca}-config.json - | ${CFSSLJSON_BIN} -bare serving-${id}
|
||||
mv "serving-${id}-key.pem" "serving-${id}.key"
|
||||
mv "serving-${id}.pem" "serving-${id}.crt"
|
||||
rm -f "serving-${id}.csr"
|
||||
EOF
|
||||
}
|
||||
|
||||
# creates a self-contained iamconfig: args are sudo, dest-dir, ca file, host, port, client id, token(optional)
|
||||
function iam::util::write_client_iamconfig {
|
||||
local sudo=$1
|
||||
local dest_dir=$2
|
||||
local ca_file=$3
|
||||
local api_host=$4
|
||||
local api_port=$5
|
||||
local client_id=$6
|
||||
local token=${7:-}
|
||||
cat <<EOF | ${sudo} tee "${dest_dir}"/"${client_id}".iamconfig > /dev/null
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority: ${ca_file}
|
||||
server: https://${api_host}:${api_port}/
|
||||
name: local-up-cluster
|
||||
users:
|
||||
- user:
|
||||
token: ${token}
|
||||
client-certificate: ${dest_dir}/client-${client_id}.crt
|
||||
client-key: ${dest_dir}/client-${client_id}.key
|
||||
name: local-up-cluster
|
||||
contexts:
|
||||
- context:
|
||||
cluster: local-up-cluster
|
||||
user: local-up-cluster
|
||||
name: local-up-cluster
|
||||
current-context: local-up-cluster
|
||||
EOF
|
||||
|
||||
# flatten the iamconfig files to make them self contained
|
||||
username=$(whoami)
|
||||
${sudo} /usr/bin/env bash -e <<EOF
|
||||
$(iam::util::find-binary iamctl) --iamconfig="${dest_dir}/${client_id}.iamconfig" config view --minify --flatten > "/tmp/${client_id}.iamconfig"
|
||||
mv -f "/tmp/${client_id}.iamconfig" "${dest_dir}/${client_id}.iamconfig"
|
||||
chown ${username} "${dest_dir}/${client_id}.iamconfig"
|
||||
EOF
|
||||
}
|
||||
|
||||
# Determines if docker can be run, failures may simply require that the user be added to the docker group.
|
||||
function iam::util::ensure_docker_daemon_connectivity {
|
||||
IFS=" " read -ra DOCKER <<< "${DOCKER_OPTS}"
|
||||
# Expand ${DOCKER[@]} only if it's not unset. This is to work around
|
||||
# Bash 3 issue with unbound variable.
|
||||
DOCKER=(docker ${DOCKER[@]:+"${DOCKER[@]}"})
|
||||
if ! "${DOCKER[@]}" info > /dev/null 2>&1 ; then
|
||||
cat <<'EOF' >&2
|
||||
Can't connect to 'docker' daemon. please fix and retry.
|
||||
|
||||
Possible causes:
|
||||
- Docker Daemon not started
|
||||
- Linux: confirm via your init system
|
||||
- macOS w/ docker-machine: run `docker-machine ls` and `docker-machine start <name>`
|
||||
- macOS w/ Docker for Mac: Check the menu bar and start the Docker application
|
||||
- DOCKER_HOST hasn't been set or is set incorrectly
|
||||
- Linux: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}`
|
||||
- macOS w/ docker-machine: run `eval "$(docker-machine env <name>)"`
|
||||
- macOS w/ Docker for Mac: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}`
|
||||
- Other things to check:
|
||||
- Linux: User isn't in 'docker' group. Add and relogin.
|
||||
- Something like 'sudo usermod -a -G docker ${USER}'
|
||||
- RHEL7 bug and workaround: https://bugzilla.redhat.com/show_bug.cgi?id=1119282#c8
|
||||
EOF
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Wait for background jobs to finish. Return with
|
||||
# an error status if any of the jobs failed.
|
||||
iam::util::wait-for-jobs() {
|
||||
local fail=0
|
||||
local job
|
||||
for job in $(jobs -p); do
|
||||
wait "${job}" || fail=$((fail + 1))
|
||||
done
|
||||
return ${fail}
|
||||
}
|
||||
|
||||
# iam::util::join <delim> <list...>
|
||||
# Concatenates the list elements with the delimiter passed as first parameter
|
||||
#
|
||||
# Ex: iam::util::join , a b c
|
||||
# -> a,b,c
|
||||
function iam::util::join {
|
||||
local IFS="$1"
|
||||
shift
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
# Downloads cfssl/cfssljson/cfssl-certinfo into $1 directory if they do not already exist in PATH
|
||||
#
|
||||
# Assumed vars:
|
||||
# $1 (cfssl directory) (optional)
|
||||
#
|
||||
# Sets:
|
||||
# CFSSL_BIN: The path of the installed cfssl binary
|
||||
# CFSSLJSON_BIN: The path of the installed cfssljson binary
|
||||
# CFSSLCERTINFO_BIN: The path of the installed cfssl-certinfo binary
|
||||
#
|
||||
function iam::util::ensure-cfssl {
|
||||
if command -v cfssl &>/dev/null && command -v cfssljson &>/dev/null && command -v cfssl-certinfo &>/dev/null; then
|
||||
CFSSL_BIN=$(command -v cfssl)
|
||||
CFSSLJSON_BIN=$(command -v cfssljson)
|
||||
CFSSLCERTINFO_BIN=$(command -v cfssl-certinfo)
|
||||
return 0
|
||||
fi
|
||||
|
||||
host_arch=$(iam::util::host_arch)
|
||||
|
||||
if [[ "${host_arch}" != "amd64" ]]; then
|
||||
echo "Cannot download cfssl on non-amd64 hosts and cfssl does not appear to be installed."
|
||||
echo "Please install cfssl, cfssljson and cfssl-certinfo and verify they are in \$PATH."
|
||||
echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create a temp dir for cfssl if no directory was given
|
||||
local cfssldir=${1:-}
|
||||
if [[ -z "${cfssldir}" ]]; then
|
||||
cfssldir="$HOME/bin"
|
||||
fi
|
||||
|
||||
mkdir -p "${cfssldir}"
|
||||
pushd "${cfssldir}" > /dev/null || return 1
|
||||
|
||||
echo "Unable to successfully run 'cfssl' from ${PATH}; downloading instead..."
|
||||
kernel=$(uname -s)
|
||||
case "${kernel}" in
|
||||
Linux)
|
||||
curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
|
||||
curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
|
||||
curl --retry 10 -L -o cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
|
||||
;;
|
||||
Darwin)
|
||||
curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_darwin-amd64
|
||||
curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_darwin-amd64
|
||||
curl --retry 10 -L -o cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_darwin-amd64
|
||||
;;
|
||||
*)
|
||||
echo "Unknown, unsupported platform: ${kernel}." >&2
|
||||
echo "Supported platforms: Linux, Darwin." >&2
|
||||
exit 2
|
||||
esac
|
||||
|
||||
chmod +x cfssl || true
|
||||
chmod +x cfssljson || true
|
||||
chmod +x cfssl-certinfo || true
|
||||
|
||||
CFSSL_BIN="${cfssldir}/cfssl"
|
||||
CFSSLJSON_BIN="${cfssldir}/cfssljson"
|
||||
CFSSLCERTINFO_BIN="${cfssldir}/cfssl-certinfo"
|
||||
if [[ ! -x ${CFSSL_BIN} || ! -x ${CFSSLJSON_BIN} || ! -x ${CFSSLCERTINFO_BIN} ]]; then
|
||||
echo "Failed to download 'cfssl'."
|
||||
echo "Please install cfssl, cfssljson and cfssl-certinfo and verify they are in \$PATH."
|
||||
echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..."
|
||||
exit 1
|
||||
fi
|
||||
popd > /dev/null || return 1
|
||||
}
|
||||
|
||||
# iam::util::ensure-gnu-sed
|
||||
# Determines which sed binary is gnu-sed on linux/darwin
|
||||
#
|
||||
# Sets:
|
||||
# SED: The name of the gnu-sed binary
|
||||
#
|
||||
function iam::util::ensure-gnu-sed {
|
||||
# NOTE: the echo below is a workaround to ensure sed is executed before the grep.
|
||||
# see: https://github.com/iamrnetes/iamrnetes/issues/87251
|
||||
sed_help="$(LANG=C sed --help 2>&1 || true)"
|
||||
if echo "${sed_help}" | grep -q "GNU\|BusyBox"; then
|
||||
SED="sed"
|
||||
elif command -v gsed &>/dev/null; then
|
||||
SED="gsed"
|
||||
else
|
||||
iam::log::error "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2
|
||||
return 1
|
||||
fi
|
||||
iam::util::sourced_variable "${SED}"
|
||||
}
|
||||
|
||||
# iam::util::check-file-in-alphabetical-order <file>
|
||||
# Check that the file is in alphabetical order
|
||||
#
|
||||
function iam::util::check-file-in-alphabetical-order {
|
||||
local failure_file="$1"
|
||||
if ! diff -u "${failure_file}" <(LC_ALL=C sort "${failure_file}"); then
|
||||
{
|
||||
echo
|
||||
echo "${failure_file} is not in alphabetical order. Please sort it:"
|
||||
echo
|
||||
echo " LC_ALL=C sort -o ${failure_file} ${failure_file}"
|
||||
echo
|
||||
} >&2
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
# iam::util::require-jq
|
||||
# Checks whether jq is installed.
|
||||
function iam::util::require-jq {
|
||||
if ! command -v jq &>/dev/null; then
|
||||
echo "jq not found. Please install." 1>&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# outputs md5 hash of $1, works on macOS and Linux
|
||||
function iam::util::md5() {
|
||||
if which md5 >/dev/null 2>&1; then
|
||||
md5 -q "$1"
|
||||
else
|
||||
md5sum "$1" | awk '{ print $1 }'
|
||||
fi
|
||||
}
|
||||
|
||||
# iam::util::read-array
|
||||
# Reads in stdin and adds it line by line to the array provided. This can be
|
||||
# used instead of "mapfile -t", and is bash 3 compatible.
|
||||
#
|
||||
# Assumed vars:
|
||||
# $1 (name of array to create/modify)
|
||||
#
|
||||
# Example usage:
|
||||
# iam::util::read-array files < <(ls -1)
|
||||
#
|
||||
function iam::util::read-array {
|
||||
local i=0
|
||||
unset -v "$1"
|
||||
while IFS= read -r "$1[i++]"; do :; done
|
||||
eval "[[ \${$1[--i]} ]]" || unset "$1[i]" # ensures last element isn't empty
|
||||
}
|
||||
|
||||
# Some useful colors.
|
||||
if [[ -z "${color_start-}" ]]; then
|
||||
declare -r color_start="\033["
|
||||
declare -r color_red="${color_start}0;31m"
|
||||
declare -r color_yellow="${color_start}0;33m"
|
||||
declare -r color_green="${color_start}0;32m"
|
||||
declare -r color_blue="${color_start}1;34m"
|
||||
declare -r color_cyan="${color_start}1;36m"
|
||||
declare -r color_norm="${color_start}0m"
|
||||
|
||||
iam::util::sourced_variable "${color_start}"
|
||||
iam::util::sourced_variable "${color_red}"
|
||||
iam::util::sourced_variable "${color_yellow}"
|
||||
iam::util::sourced_variable "${color_green}"
|
||||
iam::util::sourced_variable "${color_blue}"
|
||||
iam::util::sourced_variable "${color_cyan}"
|
||||
iam::util::sourced_variable "${color_norm}"
|
||||
fi
|
||||
|
||||
# ex: ts=2 sw=2 et filetype=sh
|
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Version management helpers. These functions help to set, save and load the
|
||||
# following variables:
|
||||
#
|
||||
# IAM_GIT_COMMIT - The git commit id corresponding to this
|
||||
# source code.
|
||||
# IAM_GIT_TREE_STATE - "clean" indicates no changes since the git commit id
|
||||
# "dirty" indicates source code changes after the git commit id
|
||||
# "archive" indicates the tree was produced by 'git archive'
|
||||
# IAM_GIT_VERSION - "vX.Y" used to indicate the last release version.
|
||||
# IAM_GIT_MAJOR - The major part of the version
|
||||
# IAM_GIT_MINOR - The minor component of the version
|
||||
|
||||
# Grovels through git to set a set of env variables.
|
||||
#
|
||||
# If IAM_GIT_VERSION_FILE, this function will load from that file instead of
|
||||
# querying git.
|
||||
iam::version::get_version_vars() {
|
||||
if [[ -n ${IAM_GIT_VERSION_FILE-} ]]; then
|
||||
iam::version::load_version_vars "${IAM_GIT_VERSION_FILE}"
|
||||
return
|
||||
fi
|
||||
|
||||
# If the iamrnetes source was exported through git archive, then
|
||||
# we likely don't have a git tree, but these magic values may be filled in.
|
||||
# shellcheck disable=SC2016,SC2050
|
||||
# Disabled as we're not expanding these at runtime, but rather expecting
|
||||
# that another tool may have expanded these and rewritten the source (!)
|
||||
if [[ '$Format:%%$' == "%" ]]; then
|
||||
IAM_GIT_COMMIT='$Format:%H$'
|
||||
IAM_GIT_TREE_STATE="archive"
|
||||
# When a 'git archive' is exported, the '$Format:%D$' below will look
|
||||
# something like 'HEAD -> release-1.8, tag: v1.8.3' where then 'tag: '
|
||||
# can be extracted from it.
|
||||
if [[ '$Format:%D$' =~ tag:\ (v[^ ,]+) ]]; then
|
||||
IAM_GIT_VERSION="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
local git=(git --work-tree "${IAM_ROOT}")
|
||||
|
||||
if [[ -n ${IAM_GIT_COMMIT-} ]] || IAM_GIT_COMMIT=$("${git[@]}" rev-parse "HEAD^{commit}" 2>/dev/null); then
|
||||
if [[ -z ${IAM_GIT_TREE_STATE-} ]]; then
|
||||
# Check if the tree is dirty. default to dirty
|
||||
if git_status=$("${git[@]}" status --porcelain 2>/dev/null) && [[ -z ${git_status} ]]; then
|
||||
IAM_GIT_TREE_STATE="clean"
|
||||
else
|
||||
IAM_GIT_TREE_STATE="dirty"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Use git describe to find the version based on tags.
|
||||
if [[ -n ${IAM_GIT_VERSION-} ]] || IAM_GIT_VERSION=$("${git[@]}" describe --tags --always --match='v*' 2>/dev/null); then
|
||||
# This translates the "git describe" to an actual semver.org
|
||||
# compatible semantic version that looks something like this:
|
||||
# v1.1.0-alpha.0.6+84c76d1142ea4d
|
||||
#
|
||||
# TODO: We continue calling this "git version" because so many
|
||||
# downstream consumers are expecting it there.
|
||||
#
|
||||
# These regexes are painful enough in sed...
|
||||
# We don't want to do them in pure shell, so disable SC2001
|
||||
# shellcheck disable=SC2001
|
||||
DASHES_IN_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/[^-]//g")
|
||||
if [[ "${DASHES_IN_VERSION}" == "---" ]] ; then
|
||||
# shellcheck disable=SC2001
|
||||
# We have distance to subversion (v1.1.0-subversion-1-gCommitHash)
|
||||
IAM_GIT_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/-\([0-9]\{1,\}\)-g\([0-9a-f]\{14\}\)$/.\1\+\2/")
|
||||
elif [[ "${DASHES_IN_VERSION}" == "--" ]] ; then
|
||||
# shellcheck disable=SC2001
|
||||
# We have distance to base tag (v1.1.0-1-gCommitHash)
|
||||
IAM_GIT_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/-g\([0-9a-f]\{14\}\)$/+\1/")
|
||||
fi
|
||||
if [[ "${IAM_GIT_TREE_STATE}" == "dirty" ]]; then
|
||||
# git describe --dirty only considers changes to existing files, but
|
||||
# that is problematic since new untracked .go files affect the build,
|
||||
# so use our idea of "dirty" from git status instead.
|
||||
# TODO?
|
||||
#IAM_GIT_VERSION+="-dirty"
|
||||
:
|
||||
fi
|
||||
|
||||
|
||||
# Try to match the "git describe" output to a regex to try to extract
|
||||
# the "major" and "minor" versions and whether this is the exact tagged
|
||||
# version or whether the tree is between two tagged versions.
|
||||
if [[ "${IAM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?([-].*)?([+].*)?$ ]]; then
|
||||
IAM_GIT_MAJOR=${BASH_REMATCH[1]}
|
||||
IAM_GIT_MINOR=${BASH_REMATCH[2]}
|
||||
if [[ -n "${BASH_REMATCH[4]}" ]]; then
|
||||
IAM_GIT_MINOR+="+"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If IAM_GIT_VERSION is not a valid Semantic Version, then refuse to build.
|
||||
if ! [[ "${IAM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
|
||||
echo "IAM_GIT_VERSION should be a valid Semantic Version. Current value: ${IAM_GIT_VERSION}"
|
||||
echo "Please see more details here: https://semver.org"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Saves the environment flags to $1
|
||||
iam::version::save_version_vars() {
|
||||
local version_file=${1-}
|
||||
[[ -n ${version_file} ]] || {
|
||||
echo "!!! Internal error. No file specified in iam::version::save_version_vars"
|
||||
return 1
|
||||
}
|
||||
|
||||
cat <<EOF >"${version_file}"
|
||||
IAM_GIT_COMMIT='${IAM_GIT_COMMIT-}'
|
||||
IAM_GIT_TREE_STATE='${IAM_GIT_TREE_STATE-}'
|
||||
IAM_GIT_VERSION='${IAM_GIT_VERSION-}'
|
||||
IAM_GIT_MAJOR='${IAM_GIT_MAJOR-}'
|
||||
IAM_GIT_MINOR='${IAM_GIT_MINOR-}'
|
||||
EOF
|
||||
}
|
||||
|
||||
# Loads up the version variables from file $1
|
||||
iam::version::load_version_vars() {
|
||||
local version_file=${1-}
|
||||
[[ -n ${version_file} ]] || {
|
||||
echo "!!! Internal error. No file specified in iam::version::load_version_vars"
|
||||
return 1
|
||||
}
|
||||
|
||||
source "${version_file}"
|
||||
}
|
Loading…
Reference in new issue