#!/usr/bin/env bash # Copyright 2022 The Karmada Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit set -o nounset set -o pipefail # This script installs karmada cli tools to the install location you want. # Usage: ./install-cli.sh # Example: # ./install-cli.sh # will install the latest karmadactl to /usr/local/bin # ./install-cli.sh kubectl-karmada # will install the latest kubectl-karmada to /usr/local/bin # export INSTALL_CLI_VERSION=1.3.0 # ./install-cli.sh # will install the 1.3.0 karmadactl to /usr/local/bin # INSTALL_CLI_TYPE supports 'karmadactl' or 'kubectl-karmada'. INSTALL_CLI_TYPE=${1:-"karmadactl"} # INSTALL_CLI_VERSION represents which version should be installed. # Defaults to 'latest' means latest release version. INSTALL_CLI_VERSION=${INSTALL_CLI_VERSION:-"latest"} # INSTALL_LOCATION represents where to put the CLI. INSTALL_LOCATION=${INSTALL_LOCATION:-"/usr/local/bin"} GITHUB_REPO="karmada-io/karmada" # Helper functions for logs info() { echo '[INFO] ' "$@" } warn() { echo '[WARN] ' "$@" >&2 } fatal() { echo '[ERROR] ' "$@" >&2 exit 1 } # Set os, fatal if operating system not supported setup_verify_os() { if [[ -z "${OS-}" ]]; then OS=$(uname) fi case ${OS} in Darwin) OS=darwin ;; Linux) OS=linux ;; *) fatal "Unsupported operating system ${OS}" esac } # Set arch, fatal if architecture not supported setup_verify_arch() { if [[ -z "${ARCH-}" ]]; then ARCH=$(uname -m) fi case ${ARCH} in arm64|aarch64|armv8l) ARCH=arm64 ;; amd64|x86_64) ARCH=amd64 ;; *) fatal "Unsupported architecture ${ARCH}" esac } # Set cli tools, install karmadactl by default, fatal if cli tool not supported setup_install_cli() { case ${INSTALL_CLI_TYPE} in karmadactl|kubectl-karmada) ;; *) fatal "Unsupported cli tool ${INSTALL_CLI_TYPE}" esac } # Verify existence of downloader executable verify_downloader() { # Return failure if it doesn't exist or is no executable [[ -x "$(which "$1")" ]] || return 1 # Set verified executable as our downloader program and return success DOWNLOADER=$1 return 0 } # Create temporary directory and cleanup when done setup_tmp() { TMP_DIR=$(mktemp -d -t "${INSTALL_CLI_TYPE}"-install.XXXXXXXXXX) TMP_METADATA="${TMP_DIR}/${INSTALL_CLI_TYPE}.json" TMP_HASH="${TMP_DIR}/${INSTALL_CLI_TYPE}.hash" TMP_BIN="${TMP_DIR}/${INSTALL_CLI_TYPE}.tgz" cleanup() { local code=$? rm -rf "${TMP_DIR}" || true exit ${code} } trap cleanup INT EXIT } # Find version from Github metadata get_release_version() { if [[ "${INSTALL_CLI_VERSION-}" != "latest" ]]; then SUFFIX_URL="tags/v${INSTALL_CLI_VERSION}" else SUFFIX_URL="latest" fi METADATA_URL="https://api.github.com/repos/${GITHUB_REPO}/releases/${SUFFIX_URL}" info "Downloading metadata ${METADATA_URL}" download "${TMP_METADATA}" "${METADATA_URL}" VERSION_KARMADA=$(grep '"tag_name":' "${TMP_METADATA}" | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-) if [[ -n "${VERSION_KARMADA}" ]]; then info "Using ${VERSION_KARMADA} as release" else fatal "Unable to determine release version" fi ret=0 vercomp ${VERSION_KARMADA} 1.2.0 || ret=$? if [[ $ret -eq 2 ]]; then fatal "Installation of cli tools before version 1.2.0 is not supported" fi } # Download from file from URL download() { [[ $# -eq 2 ]] || fatal 'download needs exactly 2 arguments' ret=0 case $DOWNLOADER in curl) curl -o "$1" -sfL "$2" || ret=$? ;; wget) wget -qO "$1" "$2" || ret=$? ;; *) fatal "Incorrect executable '${DOWNLOADER}'" ;; esac # Abort if download command failed [[ $ret -eq 0 ]] || fatal 'Download failed' } # Version comparison # Returns 0 on '=', 1 on '>', and 2 on '<'. # Ref: https://stackoverflow.com/a/4025065 vercomp () { if [[ $1 == $2 ]] then return 0 fi local IFS=. local i ver1=($1) ver2=($2) # fill empty fields in ver1 with zeros for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)) do ver1[i]=0 done for ((i=0; i<${#ver1[@]}; i++)) do if [[ -z ${ver2[i]} ]] then # fill empty fields in ver2 with zeros ver2[i]=0 fi if ((10#${ver1[i]} > 10#${ver2[i]})) then return 1 fi if ((10#${ver1[i]} < 10#${ver2[i]})) then return 2 fi done return 0 } # Download hash from Github URL download_hash() { HASH_URL="https://github.com/${GITHUB_REPO}/releases/download/v${VERSION_KARMADA}/${INSTALL_CLI_TYPE}-${OS}-${ARCH}.tgz.sha256" info "Downloading hash ${HASH_URL}" download "${TMP_HASH}" "${HASH_URL}" HASH_EXPECTED=$(grep "${INSTALL_CLI_TYPE}-${OS}-${ARCH}.tgz" "${TMP_HASH}") HASH_EXPECTED=${HASH_EXPECTED%%[[:blank:]]*} } # Download binary from Github URL download_binary() { BIN_URL="https://github.com/${GITHUB_REPO}/releases/download/v${VERSION_KARMADA}/${INSTALL_CLI_TYPE}-${OS}-${ARCH}.tgz" info "Downloading binary ${BIN_URL}" download "${TMP_BIN}" "${BIN_URL}" } compute_sha256sum() { cmd=$(which sha256sum shasum | head -n 1) case $(basename "$cmd") in sha256sum) sha256sum "$1" | cut -f 1 -d ' ' ;; shasum) shasum -a 256 "$1" | cut -f 1 -d ' ' ;; *) fatal "Can not find sha256sum or shasum to compute checksum" ;; esac } # Verify downloaded binary hash verify_binary() { info "Verifying binary download" HASH_BIN=$(compute_sha256sum "${TMP_BIN}") HASH_BIN=${HASH_BIN%%[[:blank:]]*} if [[ "${HASH_EXPECTED}" != "${HASH_BIN}" ]]; then fatal "Download sha256 does not match ${HASH_EXPECTED}, got ${HASH_BIN}" fi } # Setup permissions and move binary setup_binary() { info "Installing ${INSTALL_CLI_TYPE} to ${INSTALL_LOCATION}/${INSTALL_CLI_TYPE}" tar -xzof "${TMP_BIN}" -C "${TMP_DIR}" --no-same-owner mkdir -p "${INSTALL_LOCATION}" &>/dev/null || sudo mkdir -p "${INSTALL_LOCATION}" local CMD_MOVE="mv -i \"${TMP_DIR}/${INSTALL_CLI_TYPE}\" \"${INSTALL_LOCATION}\"" if [[ -w "${INSTALL_LOCATION}" ]]; then eval "${CMD_MOVE} < /dev/tty" else eval "sudo ${CMD_MOVE} < /dev/tty" fi } # Run the install process { setup_verify_os setup_verify_arch setup_install_cli verify_downloader curl || verify_downloader wget || fatal 'Can not find curl or wget for downloading files' setup_tmp get_release_version download_hash download_binary verify_binary setup_binary }