diff --git a/Makefile b/Makefile index 30f8882018..aadaf22cdf 100644 --- a/Makefile +++ b/Makefile @@ -121,23 +121,11 @@ ifndef GOBIN GOBIN := $(GOPATH)/bin endif -KUSTOMIZE = $(shell pwd)/bin/kustomize - -# go-get-tool will 'go get' any package $2 and install it to $1. +# NOTE: project related tools get installed to tmp dir which is ignored by PROJECT_DIR := $(shell dirname $(abspath $(firstword $(MAKEFILE_LIST)))) -define go-get-tool -@[ -f $(1) ] || { \ -set -e ;\ -TMP_DIR=$$(mktemp -d) ;\ -cd $$TMP_DIR ;\ -go mod init tmp ;\ -echo "Downloading $(2)" ;\ -GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\ -ls $$TMP_DIR;\ -echo $(PROJECT_DIR);\ -rm -rf $$TMP_DIR ;\ -} -endef +TOOLS_DIR=$(PROJECT_DIR)/tmp/bin +KUSTOMIZE = $(TOOLS_DIR)/kustomize +GOVULNCHECK = $(TOOLS_DIR)/govulncheck base_dir := $(patsubst %/,%,$(dir $(realpath $(firstword $(MAKEFILE_LIST))))) @@ -294,10 +282,10 @@ escapes_detect: tidy-vendor @$(GOENV) go build -tags $(GO_BUILD_TAGS) -gcflags="-m -l" ./... 2>&1 | grep "escapes to heap" || true set_govulncheck: - @go install golang.org/x/vuln/cmd/govulncheck@latest + ./hack/tools.sh govulncheck govulncheck: set_govulncheck tidy-vendor - @govulncheck -v ./... || true + @$(GOVULNCHECK) ./... || true format: ./automation/presubmit-tests/gofmt.sh @@ -318,10 +306,14 @@ genbpfassets: genlibbpf: kepler.bpf.o + +tools: + hack/tools.sh +.PHONY: tools + ### k8s ### kustomize: ## Download kustomize locally if necessary. - mkdir -p bin - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4@v4.5.2) + hack/tools.sh kustomize build-manifest: kustomize ./hack/build-manifest.sh "${OPTS}" diff --git a/hack/build-manifest.sh b/hack/build-manifest.sh index fc5eda3898..f01e7a1732 100755 --- a/hack/build-manifest.sh +++ b/hack/build-manifest.sh @@ -17,22 +17,33 @@ # Copyright 2022 The Kepler Contributors # -set -ex +set -eu -o pipefail -export CLUSTER_PROVIDER=${CLUSTER_PROVIDER:-kind} +# constants +PROJECT_ROOT="$(git rev-parse --show-toplevel)" +declare -r PROJECT_ROOT +source "$PROJECT_ROOT/hack/utils.bash" + +export PATH="$PROJECT_ROOT/tmp/bin:$PATH" +command -v jq >/dev/null 2>&1 || { + die "jq is not installed; run make tools to install all necessary tools" +} + +kc_version_ok=$(kubectl version --client -ojson | + jq '.clientVersion | (.major | tonumber) >= 1 and (.minor | tonumber) > 21') +if [[ "$kc_version_ok" != true ]]; then + die "Please update the kubectl version to 1.21+ to support kustomize" +fi + +set -x +export CLUSTER_PROVIDER=${CLUSTER_PROVIDER:-kind} # set options # for example: ./build-manifest.sh "ESTIMATOR_SIDECAR_DEPLOY OPENSHIFT_DEPLOY" DEPLOY_OPTIONS=$1 for opt in ${DEPLOY_OPTIONS}; do export "$opt"=true; done -version=$(kubectl version --short | grep 'Client Version' | sed 's/.*v//g' | cut -b -4) -if [ 1 -eq "$(echo "${version} < 1.21" | bc)" ]; then - echo "You need to update your kubectl version to 1.21+ to support kustomize" - exit 1 -fi - -KUSTOMIZE=${PWD}/bin/kustomize +KUSTOMIZE=${PROJECT_ROOT}/tmp/bin/kustomize SED="sed -i" if [ "$(uname)" == "Darwin" ]; then SED="sed -i .bak " @@ -82,38 +93,38 @@ rm -rf "${MANIFESTS_OUT_DIR}" mkdir -p "${MANIFESTS_OUT_DIR}" cp -r manifests/config/* "${MANIFESTS_OUT_DIR}"/ -if [ -n "${BM_DEPLOY}" ]; then +if [ -n "${BM_DEPLOY:-}" ]; then echo "baremetal deployment" uncomment_patch bm "${MANIFESTS_OUT_DIR}"/exporter/kustomization.yaml fi -if [ -n "${ROOTLESS}" ]; then +if [ -n "${ROOTLESS:-}" ]; then echo "rootless deployment" uncomment_patch rootless "${MANIFESTS_OUT_DIR}"/exporter/kustomization.yaml fi -if [ -n "${OPENSHIFT_DEPLOY}" ]; then +if [ -n "${OPENSHIFT_DEPLOY:-}" ]; then echo "deployment on openshift" uncomment_patch openshift "${MANIFESTS_OUT_DIR}"/exporter/kustomization.yaml uncomment openshift_scc "${MANIFESTS_OUT_DIR}"/exporter/kustomization.yaml fi -if [ -n "${PROMETHEUS_DEPLOY}" ]; then +if [ -n "${PROMETHEUS_DEPLOY:-}" ]; then echo "deployment with prometheus" uncomment prometheus_ "${MANIFESTS_OUT_DIR}"/exporter/kustomization.yaml uncomment prometheus_ "${MANIFESTS_OUT_DIR}"/rbac/kustomization.yaml - if [ -n "${HIGH_GRANULARITY}" ]; then + if [ -n "${HIGH_GRANULARITY:-}" ]; then echo "enable high metric granularity in Prometheus" uncomment_patch high-granularity "${MANIFESTS_OUT_DIR}"/base/kustomization.yaml fi fi -if [ -n "${ESTIMATOR_SIDECAR_DEPLOY}" ]; then +if [ -n "${ESTIMATOR_SIDECAR_DEPLOY:-}" ]; then echo "enable estimator-sidecar" uncomment_patch estimator-sidecar "${MANIFESTS_OUT_DIR}"/exporter/kustomization.yaml fi -if [ -n "${CI_DEPLOY}" ]; then +if [ -n "${CI_DEPLOY:-}" ]; then echo "enable ci ${CLUSTER_PROVIDER}" uncomment_patch ci "${MANIFESTS_OUT_DIR}"/exporter/kustomization.yaml case ${CLUSTER_PROVIDER} in @@ -124,12 +135,12 @@ if [ -n "${CI_DEPLOY}" ]; then esac fi -if [ -n "${DEBUG_DEPLOY}" ]; then +if [ -n "${DEBUG_DEPLOY:-}" ]; then echo "enable debug" uncomment_patch debug "${MANIFESTS_OUT_DIR}"/base/kustomization.yaml fi -if [ -n "${MODEL_SERVER_DEPLOY}" ]; then +if [ -n "${MODEL_SERVER_DEPLOY:-}" ]; then echo "enable model-server" uncomment_path model-server "${MANIFESTS_OUT_DIR}"/base/kustomization.yaml uncomment_patch model-server-kepler-config "${MANIFESTS_OUT_DIR}"/base/kustomization.yaml @@ -146,7 +157,7 @@ if [ -n "${MODEL_SERVER_DEPLOY}" ]; then fi fi -if [ -n "${QAT_DEPLOY}" ]; then +if [ -n "${QAT_DEPLOY:-}" ]; then echo "enable qat" uncomment_patch qat "${MANIFESTS_OUT_DIR}"/exporter/kustomization.yaml fi diff --git a/hack/tools.sh b/hack/tools.sh new file mode 100755 index 0000000000..b7ccdc0e16 --- /dev/null +++ b/hack/tools.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# +# This file is part of the Kepler project +# +# 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. +# +# Copyright 2023 The Kepler Contributors +# + +set -eu -o pipefail + +# constants +PROJECT_ROOT="$(git rev-parse --show-toplevel)" +GOOS="$(go env GOOS)" +GOARCH="$(go env GOARCH)" + +declare -r PROJECT_ROOT GOOS GOARCH +declare -r LOCAL_BIN="$PROJECT_ROOT/tmp/bin" + +# tools +declare -r KUSTOMIZE_VERSION=${KUSTOMIZE_VERSION:-v4.5.2} +declare -r KUSTOMIZE_INSTALL_SCRIPT="https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" + +declare -r JQ_VERSION=${JQ_VERSION:-1.7} +declare -r JQ_INSTALL_URL="https://github.com/jqlang/jq/releases/download/jq-$JQ_VERSION/jq-$GOOS-$GOARCH" + +source "$PROJECT_ROOT/hack/utils.bash" + +validate_version() { + local cmd="$1" + local version_arg="$2" + local version_regex="$3" + shift 3 + + command -v "$cmd" >/dev/null 2>&1 || return 1 + [[ $(eval "$cmd $version_arg" | grep -o "$version_regex") =~ $version_regex ]] || { + return 1 + } + + ok "$cmd installed successfully" +} + +install_kustomize() { + command -v kustomize >/dev/null 2>&1 && + [[ $(kustomize version --short | grep -o 'v[0-9].[0-9].[0-9]') == "$KUSTOMIZE_VERSION" ]] && { + ok "kustomize $KUSTOMIZE_VERSION is already installed" + return 0 + } + + info "installing kustomize version: $KUSTOMIZE_VERSION" + ( + # NOTE: this handles softlinks properly + cd "$LOCAL_BIN" + curl -Ss $KUSTOMIZE_INSTALL_SCRIPT | bash -s -- "${KUSTOMIZE_VERSION:1}" . + ) || { + fail "failed to install kustomize" + return 1 + } + ok "kustomize was installed successfully" +} + +go_install() { + local pkg="$1" + local version="$2" + shift 2 + + info "installing $pkg version: $version" + + GOBIN=$LOCAL_BIN \ + go install "$pkg@$version" || { + fail "failed to install $pkg - $version" + return 1 + } + ok "$pkg - $version was installed successfully" + +} + +curl_install() { + local binary="$1" + local url="$2" + shift 2 + + info "installing $binary" + curl -sSLo "$LOCAL_BIN/$binary" "$url" || { + fail "failed to install $binary" + return 1 + } + + chmod +x "$LOCAL_BIN/$binary" + ok "$binary was installed successfully" +} + +install_jq() { + validate_version jq --version "$JQ_VERSION" && { + return 0 + } + curl_install jq "$JQ_INSTALL_URL" +} + +install_govulncheck() { + go_install golang.org/x/vuln/cmd/govulncheck latest +} + +install_all() { + info "installing all tools ..." + local ret=0 + for tool in $(declare -F | cut -f3 -d ' ' | grep install_ | grep -v 'install_all'); do + "$tool" || ret=1 + done + return $ret +} + +main() { + local op="${1:-all}" + shift || true + + mkdir -p "$LOCAL_BIN" + export PATH="$LOCAL_BIN:$PATH" + install_"$op" +} + +main "$@" diff --git a/hack/utils.bash b/hack/utils.bash new file mode 100644 index 0000000000..92f282f144 --- /dev/null +++ b/hack/utils.bash @@ -0,0 +1,113 @@ +# +# Copyright 2023. +# +# 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. +# + +is_fn() { + [[ $(type -t "$1") == "function" ]] + return $? +} + +header() { + local title=" 🔆🔆🔆 $* 🔆🔆🔆 " + + local len=40 + if [[ ${#title} -gt $len ]]; then + len=${#title} + fi + + echo -e "\n\n \033[1m${title}\033[0m" + echo -n "━━━━━" + printf '━%.0s' $(seq "$len") + echo "━━━━━━━" + +} + +info() { + echo -e " 🔔 $*" >&2 +} + +err() { + echo -e " 😱 $*" >&2 +} + +warn() { + echo -e "  $*" >&2 +} + +ok() { + echo -e " ✅ $*" >&2 +} + +skip() { + echo -e " 🙈 SKIP: $*" >&2 +} + +fail() { + echo -e " ❌ FAIL: $*" >&2 +} + +info_run() { + echo -e "  $*\n" >&2 +} + +run() { + echo -e " ❯ $*\n" >&2 + "$@" +} + +die() { + echo -e "\n ✋ $* " + echo -e "──────────────────── ⛔️⛔️⛔️ ────────────────────────\n" + exit 1 +} + +line() { + local len="$1" + local style="${2:-thin}" + shift + + local ch='─' + [[ "$style" == 'heavy' ]] && ch="━" + + printf "$ch%.0s" $(seq "$len") >&2 + echo +} + +# wait_until +# waits for condition to be true for a max of x seconds +wait_until() { + local max_tries="$1" + local delay="$2" + local msg="$3" + local condition="$4" + shift 4 + + info "Waiting [$max_tries x ${delay}s] for $msg" + local tries=0 + local -i ret=1 + echo " ❯ $condition $*" 2>&1 + while [[ $tries -lt $max_tries ]]; do + $condition "$@" && { + ret=0 + break + } + + tries=$((tries + 1)) + echo " ... [$tries / $max_tries] waiting ($delay secs) - $msg" >&2 + sleep "$delay" + done + + return $ret +}