diff --git a/hack/install-ate.sh b/hack/install-ate.sh index 49c61d827..0b8815e00 100755 --- a/hack/install-ate.sh +++ b/hack/install-ate.sh @@ -16,6 +16,11 @@ set -o errexit -o nounset -o pipefail +if ! command -v git >/dev/null 2>&1 || ! git --version >/dev/null 2>&1; then + echo "Error: git is required but was not found or is not working." >&2 + exit 1 +fi + ROOT="$(git rev-parse --show-toplevel)" cd "${ROOT}" @@ -55,6 +60,135 @@ function log_step() { echo -e "${COLOR_CYAN}[step]: ${step_name}${COLOR_RESET}" } +require_command() { + local command_name="$1" + if ! command -v "${command_name}" >/dev/null 2>&1; then + echo "Error: ${command_name} is required but was not found in PATH." >&2 + return 1 + fi +} + +check_git_worktree() { + local status="" + status="$(git status --porcelain --untracked-files=normal)" + if [[ -n "${status}" ]]; then + echo "Error: ko requires a clean Git worktree, but local changes were found:" >&2 + echo "${status}" >&2 + echo "Hint: commit, stash, or remove these changes before running the installer." >&2 + return 1 + fi +} + +check_go() { + if ! command -v go >/dev/null 2>&1; then + echo "Error: go is required but was not found in PATH." >&2 + return 1 + fi + + if ! go version >/dev/null 2>&1; then + echo "Error: go is installed but not working (failed to run 'go version')." >&2 + return 1 + fi +} + +check_go_tool_ko() { + if ! ( + cd "${ROOT}/hack/tools/ko" && + go tool -n ko >/dev/null 2>&1 && + go tool ko version >/dev/null 2>&1 + ); then + echo "Error: 'go tool ko' is required but not working in hack/tools/ko." >&2 + echo "Hint: run 'cd hack/tools/ko && go tool ko version' to verify your setup." >&2 + return 1 + fi +} + +check_kubectl() { + require_command kubectl + if ! kubectl version --client >/dev/null 2>&1; then + echo "Error: kubectl is installed but not working (failed to get its client version)." >&2 + return 1 + fi +} + +check_make() { + require_command make + if ! printf 'prerequisite-check:\n\t@:\n' | make -f - prerequisite-check >/dev/null 2>&1; then + echo "Error: make is installed but not working." >&2 + return 1 + fi +} + +check_openssl() { + require_command openssl + if ! openssl version >/dev/null 2>&1; then + echo "Error: openssl is installed but not working (failed to run 'openssl version')." >&2 + return 1 + fi +} + +check_jq() { + require_command jq + if ! jq -n 'true' >/dev/null 2>&1; then + echo "Error: jq is installed but not working." >&2 + return 1 + fi +} + +check_docker() { + require_command docker + if ! docker version >/dev/null 2>&1; then + echo "Error: docker is installed but not working or its daemon is unavailable." >&2 + return 1 + fi +} + +check_docker_buildx() { + check_docker + if ! docker buildx version >/dev/null 2>&1; then + echo "Error: 'docker buildx' is required but not working." >&2 + return 1 + fi +} + +check_envsubst() { + require_command envsubst + if [[ "$(ATE_PREREQUISITE_CHECK=working envsubst '${ATE_PREREQUISITE_CHECK}' <<< '${ATE_PREREQUISITE_CHECK}')" != "working" ]]; then + echo "Error: envsubst is installed but not working." >&2 + return 1 + fi +} + +check_cluster_admin() { + local current_context="" + current_context="${KUBECTL_CONTEXT:-$(kubectl config current-context 2>/dev/null || true)}" + if [[ -z "${current_context}" ]]; then + current_context="" + fi + + if ! run_kubectl get --raw=/version >/dev/null 2>&1; then + echo "Error: unable to connect to the Kubernetes cluster on context '${current_context}'." >&2 + echo "Hint: verify your kubeconfig and cluster credentials." >&2 + return 1 + fi + + if ! run_kubectl auth can-i '*' '*' --all-namespaces --quiet >/dev/null 2>&1; then + echo "Error: current Kubernetes user is not cluster-admin on context '${current_context}'." >&2 + echo "Hint: this installer applies cluster-scoped resources and needs full cluster-admin privileges." >&2 + return 1 + fi +} + +check_prerequisites() { + log_step "check_prerequisites" + check_git_worktree + check_go + check_go_tool_ko + check_kubectl + check_make + check_cluster_admin +} + # --- Helper Functions --- function usage() { echo "Usage: $0 [options]" @@ -218,6 +352,7 @@ ca_pool_root_pem() { create_valkey_ca_certs_secret() { log_step "create_valkey_ca_certs_secret" + check_openssl # valkey requires a single tls-ca-cert-file to verify client and server certs it sees, # so it needs both CAs: # - servicedns CA: verifies valkey peers' server certs. @@ -327,6 +462,7 @@ deploy_crds() { deploy_ate_system() { log_step "deploy_ate_system" + check_openssl # Not ensure_crds: its existence check skips upgrades, stranding stale CRD # schemas and RBAC (role.yaml has no other apply path). deploy_crds @@ -390,6 +526,7 @@ ensure_apiserver_prerequisites() { # Redeploy only the ate-apiserver deploy_ate_apiserver() { log_step "deploy_ate_apiserver" + check_openssl ensure_crds # Ensure namespace exists @@ -498,10 +635,7 @@ prepare_actor_for_delete() { # delete_demo_actors ate-demo-counter counter # delete_demo_actors ns-a tmpl-a ns-b tmpl-b delete_demo_actors() { - if ! command -v jq &>/dev/null; then - echo "jq is required to delete demo actors" >&2 - return 1 - fi + check_jq if (($# == 0 || $# % 2 != 0)); then echo "delete_demo_actors expects namespace/template pairs" >&2 @@ -561,16 +695,20 @@ delete_atenet() { deploy_benchmarks() { log_step "deploy_benchmarks (worker_count=${BENCHMARK_WORKER_COUNT})" + check_docker + check_envsubst "${ROOT}/benchmarking/deploy_locust.sh" --deploy --worker-count "${BENCHMARK_WORKER_COUNT}" } delete_benchmarks() { log_step "delete_benchmarks" + check_envsubst "${ROOT}/benchmarking/deploy_locust.sh" --delete } delete_all() { log_step "delete_all" + check_jq for demo_name in "${ATE_DEMOS[@]}"; do if declare -F "${demo_name}_delete" >/dev/null 2>&1; then "${demo_name}_delete" @@ -594,6 +732,8 @@ for arg in "$@"; do esac done +check_prerequisites + # Pre-scan value-bearing flags so they can appear before or after the action # flag they configure (e.g. --benchmark-worker-count before/after # --deploy-benchmarks). The dispatch loop below also accepts these flags but diff --git a/hack/install-demo-claude-code-multiplex.sh b/hack/install-demo-claude-code-multiplex.sh index 5a5e3b977..fe1e749d9 100644 --- a/hack/install-demo-claude-code-multiplex.sh +++ b/hack/install-demo-claude-code-multiplex.sh @@ -34,6 +34,8 @@ demo-claude-code-multiplex_cmdline() { # The workload is a Dockerfile-based Python+Claude-Code wrapper (not a Go # binary), so it uses docker buildx rather than ko. demo-claude-code-multiplex_build_workload() { + check_docker_buildx + check_jq local repo="${KO_DOCKER_REPO}/claude-multiplex-demo-workload" # shellcheck disable=SC2155 # safe initialization local stage_tag="${repo}:build-$(date +%s)"