mirror of
https://github.com/hanzo-ml/kserve.git
synced 2026-10-30 20:53:49 +00:00
1602 lines
53 KiB
Bash
Executable File
Generated
1602 lines
53 KiB
Bash
Executable File
Generated
#!/bin/bash
|
|
|
|
# Copyright 2025 The KServe 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.
|
|
|
|
# Install KServe Knative Mode and all related dependencies using helm.
|
|
#
|
|
# AUTO-GENERATED from: kserve-knative-mode-full-install.definition
|
|
# DO NOT EDIT MANUALLY
|
|
#
|
|
# To regenerate:
|
|
# ./scripts/generate-install-script.py kserve-knative-mode-full-install.definition
|
|
#
|
|
# Usage: kserve-knative-mode-full-install.sh [--reinstall|--uninstall]
|
|
|
|
set -o errexit
|
|
set -o nounset
|
|
set -o pipefail
|
|
|
|
#================================================
|
|
# Helper Functions (from common.sh)
|
|
#================================================
|
|
|
|
# Utility Functions
|
|
# ============================================================================
|
|
|
|
find_repo_root() {
|
|
local current_dir="${1:-$(pwd)}"
|
|
local skip="${2:-false}"
|
|
|
|
while [[ "$current_dir" != "/" ]]; do
|
|
if [[ -e "${current_dir}/.git" ]]; then
|
|
echo "$current_dir"
|
|
return 0
|
|
fi
|
|
current_dir="$(dirname "$current_dir")"
|
|
done
|
|
|
|
# Git repository not found
|
|
if [[ "$skip" == "true" ]]; then
|
|
log_warning "Could not find git repository root, using current directory: $PWD"
|
|
echo "$PWD"
|
|
return 0
|
|
else
|
|
log_error "Could not find git repository root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
ensure_dir() {
|
|
local dir_path="${1}"
|
|
|
|
if [[ -d "${dir_path}" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
mkdir -p "${dir_path}"
|
|
}
|
|
|
|
detect_os() {
|
|
local os=""
|
|
case "$(uname -s)" in
|
|
Linux*) os="linux" ;;
|
|
Darwin*) os="darwin" ;;
|
|
*) log_error "Unsupported OS detected: $(uname -s)" ; exit 1 ;;
|
|
esac
|
|
echo "$os"
|
|
}
|
|
|
|
detect_arch() {
|
|
local arch=""
|
|
case "$(uname -m)" in
|
|
x86_64) arch="amd64" ;;
|
|
aarch64|arm64) arch="arm64" ;;
|
|
*) log_error "Unsupported architecture detected: $(uname -m)" ; exit 1 ;;
|
|
esac
|
|
echo "$arch"
|
|
}
|
|
|
|
cleanup_bin_dir() {
|
|
# Remove BIN_DIR if it was created by this script
|
|
if [[ "${BIN_DIR_CREATED_BY_SCRIPT:-false}" == "true" ]] && [[ -d "${BIN_DIR:-}" ]]; then
|
|
log_info "Cleaning up BIN_DIR: ${BIN_DIR}"
|
|
rm -rf "${BIN_DIR}"
|
|
fi
|
|
}
|
|
|
|
cleanup() {
|
|
# Call all cleanup functions
|
|
cleanup_bin_dir
|
|
}
|
|
|
|
# Set up trap to run cleanup on exit
|
|
trap cleanup EXIT
|
|
|
|
# Color codes (disable if NO_COLOR is set or not a terminal)
|
|
if [[ -z "${NO_COLOR:-}" ]] && [[ -t 1 ]]; then
|
|
BLUE='\033[94m'
|
|
GREEN='\033[92m'
|
|
RED='\033[91m'
|
|
YELLOW='\033[93m'
|
|
RESET='\033[0m'
|
|
else
|
|
BLUE=''
|
|
GREEN=''
|
|
RED=''
|
|
YELLOW=''
|
|
RESET=''
|
|
fi
|
|
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${RESET} $*" >&2
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${RESET} $*" >&2
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${RESET} $*" >&2
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[WARNING]${RESET} $*" >&2
|
|
}
|
|
|
|
is_positive() {
|
|
local input_val="${1:-no}"
|
|
|
|
case "${input_val}" in
|
|
0|true|True|yes|Yes|y|Y)
|
|
return 0 # Success - truthy
|
|
;;
|
|
1|false|False|no|No|n|N)
|
|
return 1 # Failure - falsy
|
|
;;
|
|
*)
|
|
return 2 # Invalid input
|
|
;;
|
|
esac
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# Infrastructure Installation Helper Functions
|
|
# ============================================================================
|
|
|
|
# Detect the platform (kind/minikube/openshift/kubernetes)
|
|
# Returns: kind, minikube, openshift, or kubernetes
|
|
detect_platform() {
|
|
# Check for OpenShift
|
|
if kubectl get clusterversion &>/dev/null; then
|
|
echo "openshift"
|
|
return 0
|
|
fi
|
|
|
|
# Check for Kind
|
|
local node_hostname
|
|
node_hostname=$(kubectl get nodes -o jsonpath='{.items[0].metadata.labels.kubernetes\.io/hostname}' 2>/dev/null || echo "")
|
|
if [[ "$node_hostname" == *"kind"* ]]; then
|
|
echo "kind"
|
|
return 0
|
|
fi
|
|
|
|
# Check for Minikube
|
|
local current_context
|
|
current_context=$(kubectl config current-context 2>/dev/null || echo "")
|
|
if [[ "$current_context" == *"minikube"* ]]; then
|
|
echo "minikube"
|
|
return 0
|
|
fi
|
|
|
|
# Default to standard Kubernetes
|
|
echo "kubernetes"
|
|
return 0
|
|
}
|
|
|
|
# Wait for pods to be created (exist)
|
|
# Usage: wait_for_pods_created <namespace> <label-selector> [timeout_seconds]
|
|
wait_for_pods_created() {
|
|
local namespace="$1"
|
|
local label_selector="$2"
|
|
local timeout="${3:-60}"
|
|
local elapsed=0
|
|
|
|
log_info "Waiting for pods with label '$label_selector' in namespace '$namespace' to be created..."
|
|
|
|
while true; do
|
|
# Exclude terminating pods by filtering out Terminating status
|
|
local pod_count=$(kubectl get pods -n "$namespace" -l "$label_selector" \
|
|
--no-headers 2>/dev/null | grep -v "Terminating" | wc -l)
|
|
|
|
if [ "$pod_count" -gt 0 ]; then
|
|
log_info "Found $pod_count pod(s) with label '$label_selector'"
|
|
return 0
|
|
fi
|
|
|
|
if [ $elapsed -ge $timeout ]; then
|
|
log_error "Timeout waiting for pods with label '$label_selector' to be created"
|
|
return 1
|
|
fi
|
|
|
|
sleep 2
|
|
elapsed=$((elapsed + 2))
|
|
done
|
|
}
|
|
|
|
# Wait for pods to be ready
|
|
# Usage: wait_for_pods_ready <namespace> <label-selector> [timeout]
|
|
wait_for_pods_ready() {
|
|
local namespace="$1"
|
|
local label_selector="$2"
|
|
local timeout="${3:-180s}"
|
|
|
|
log_info "Waiting for pods with label '$label_selector' in namespace '$namespace' to be ready..."
|
|
|
|
# Get list of non-terminating pods and wait for them
|
|
local pods=$(kubectl get pods -n "$namespace" -l "$label_selector" \
|
|
--no-headers 2>/dev/null | grep -v "Terminating" | awk '{print $1}')
|
|
|
|
if [ -z "$pods" ]; then
|
|
log_error "No pods found with label '$label_selector' in namespace '$namespace'"
|
|
return 1
|
|
fi
|
|
|
|
for pod in $pods; do
|
|
kubectl wait --for=condition=Ready pod/"$pod" -n "$namespace" --timeout="$timeout" || return 1
|
|
done
|
|
}
|
|
|
|
# Wait for pods to be ready (combines both creation and ready checks)
|
|
# Usage: wait_for_pods <namespace> <label-selector> [timeout]
|
|
wait_for_pods() {
|
|
local namespace="$1"
|
|
local label_selector="$2"
|
|
local timeout="${3:-180s}"
|
|
|
|
# Convert timeout to seconds for pod creation check
|
|
local timeout_seconds="${timeout%s}"
|
|
local timeout_created=60
|
|
|
|
# If timeout is longer than 60s, use 60s for creation, rest for ready
|
|
# If timeout is shorter, split it
|
|
if [ "$timeout_seconds" -gt 60 ]; then
|
|
timeout_created=60
|
|
else
|
|
timeout_created=$((timeout_seconds / 3))
|
|
fi
|
|
|
|
# First, wait for pods to be created
|
|
wait_for_pods_created "$namespace" "$label_selector" "$timeout_created" || return 1
|
|
|
|
# Then, wait for pods to be ready
|
|
wait_for_pods_ready "$namespace" "$label_selector" "$timeout" || return 1
|
|
|
|
log_success "Pods with label '$label_selector' in namespace '$namespace' are ready!"
|
|
}
|
|
|
|
# Wait for deployment to be available using kubectl wait
|
|
# Usage: wait_for_deployment <namespace> <deployment-name> [timeout]
|
|
# Note: This uses kubectl wait --for=condition=Available, which checks deployment status directly
|
|
wait_for_deployment() {
|
|
local namespace="$1"
|
|
local deployment_name="$2"
|
|
local timeout="${3:-180s}"
|
|
|
|
log_info "Waiting for deployment '$deployment_name' in namespace '$namespace' to be available..."
|
|
kubectl wait --timeout="$timeout" -n "$namespace" deployment/"$deployment_name" --for=condition=Available
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log_success "Deployment '$deployment_name' in namespace '$namespace' is available!"
|
|
else
|
|
log_error "Deployment '$deployment_name' in namespace '$namespace' failed to become available within $timeout"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Wait for CRD to be established
|
|
# Usage: wait_for_crd <crd-name> [timeout]
|
|
wait_for_crd() {
|
|
local crd_name="$1"
|
|
local timeout="${2:-60s}"
|
|
|
|
log_info "Waiting for CRD '$crd_name' to be established..."
|
|
|
|
# Add small delay to allow CRD status to be initialized
|
|
sleep 2
|
|
|
|
# Retry logic to handle race condition where .status.conditions may not be initialized yet
|
|
local max_retries=10
|
|
local retry=0
|
|
while [ $retry -lt $max_retries ]; do
|
|
if kubectl wait --for=condition=Established --timeout="$timeout" crd/"$crd_name" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
retry=$((retry + 1))
|
|
if [ $retry -lt $max_retries ]; then
|
|
log_info "Retry $retry/$max_retries: Waiting for CRD status to be initialized..."
|
|
sleep 3
|
|
fi
|
|
done
|
|
|
|
# Final attempt with error output
|
|
kubectl wait --for=condition=Established --timeout="$timeout" crd/"$crd_name"
|
|
}
|
|
|
|
# Wait for multiple CRDs to be established
|
|
# Usage: wait_for_crds <timeout> <crd1> <crd2> ...
|
|
wait_for_crds() {
|
|
local timeout="$1"
|
|
shift
|
|
|
|
for crd in "$@"; do
|
|
wait_for_crd "$crd" "$timeout" || return 1
|
|
done
|
|
|
|
log_success "All CRDs are established!"
|
|
}
|
|
|
|
# Update multiple fields in KServe inferenceservice-config ConfigMap
|
|
# Usage: update_isvc_config "ingress.enableGatewayApi=true" "deploy.defaultDeploymentMode=Standard"
|
|
# Example:
|
|
# update_isvc_config "ingress.enableGatewayApi=true"
|
|
# update_isvc_config "ingress.enableGatewayApi=true" "ingress.className=\"envoy\""
|
|
update_isvc_config() {
|
|
if [ $# -eq 0 ]; then
|
|
log_error "No update parameters provided"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Updating inferenceservice-config..."
|
|
|
|
# Prepare updates as JSON array
|
|
local updates_file
|
|
updates_file=$(mktemp)
|
|
|
|
for arg in "$@"; do
|
|
local key="${arg%%=*}"
|
|
local raw_value="${arg#*=}"
|
|
local data_key="${key%%.*}"
|
|
local json_path="${key#*.}"
|
|
local value_json="$raw_value"
|
|
|
|
# Smart type detection: auto-quote strings, keep numbers/booleans/JSON as-is
|
|
if [[ ! $raw_value =~ ^\" ]] \
|
|
&& [[ ! $raw_value =~ ^-?[0-9]+(\.[0-9]+)?$ ]] \
|
|
&& [[ ! $raw_value =~ ^(true|false|null)$ ]] \
|
|
&& [[ ! $raw_value =~ ^[{\[] ]]; then
|
|
value_json=$(jq -Rn --arg v "$raw_value" '$v')
|
|
fi
|
|
|
|
jq -n \
|
|
--arg data_key "$data_key" \
|
|
--arg path "$json_path" \
|
|
--argjson value "$value_json" \
|
|
'{data_key:$data_key, path:$path, value:$value}' >> "$updates_file"
|
|
|
|
log_info " ✓ $data_key.$json_path = $value_json"
|
|
done
|
|
|
|
local updates_json
|
|
updates_json=$(jq -s '.' "$updates_file")
|
|
rm -f "$updates_file"
|
|
|
|
# Apply all updates with a single jq invocation
|
|
kubectl get configmap inferenceservice-config -n "${KSERVE_NAMESPACE}" -o json |
|
|
jq --argjson updates "$updates_json" '
|
|
# Helper function to safely set nested paths, creating intermediate objects as needed
|
|
def setpath_safe($parts; $value):
|
|
reduce range(0; ($parts|length)-1) as $i (.;
|
|
$parts[:$i+1] as $prefix
|
|
| if getpath($prefix) == null then setpath($prefix; {}) else . end
|
|
)
|
|
| setpath($parts; $value);
|
|
|
|
# Apply each update
|
|
reduce $updates[] as $item (.;
|
|
if .data[$item.data_key] == null or .data[$item.data_key] == "" then
|
|
.data[$item.data_key] = (
|
|
{}
|
|
| setpath_safe($item.path | split("."); $item.value)
|
|
| tojson
|
|
)
|
|
else
|
|
.data[$item.data_key] |= (
|
|
fromjson
|
|
| setpath_safe($item.path | split("."); $item.value)
|
|
| tojson
|
|
)
|
|
end
|
|
)
|
|
' |
|
|
kubectl apply -f -
|
|
|
|
log_success "ConfigMap updated successfully"
|
|
}
|
|
|
|
# Create namespace if it does not exist (skip if already exists)
|
|
# Usage: create_or_skip_namespace <namespace>
|
|
create_or_skip_namespace() {
|
|
local namespace="$1"
|
|
|
|
if kubectl get namespace "$namespace" &>/dev/null; then
|
|
log_info "Namespace '$namespace' already exists"
|
|
else
|
|
log_info "Creating namespace '$namespace'..."
|
|
kubectl create namespace "$namespace"
|
|
log_success "Namespace '$namespace' created"
|
|
fi
|
|
}
|
|
|
|
# Check if required CLI tools exist
|
|
# Usage: check_cli_exist <tool1> [tool2] [tool3] ...
|
|
check_cli_exist() {
|
|
local missing=()
|
|
for cmd in "$@"; do
|
|
if ! command_exists "$cmd"; then
|
|
missing+=("$cmd")
|
|
fi
|
|
done
|
|
|
|
if [ ${#missing[@]} -gt 0 ]; then
|
|
log_error "Required CLI tool(s) not found: ${missing[*]}"
|
|
log_error "Please install missing tool(s) first."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
command_exists() {
|
|
command -v "$1" &>/dev/null
|
|
}
|
|
|
|
# Retry a command with delay between attempts
|
|
# Usage: retry_command <max_attempts> <delay_seconds> <command...>
|
|
# Example: retry_command 3 5 kubectl apply -k "${RUNTIMES_DIR}"
|
|
# Returns: 0 on success, 1 on failure after all attempts
|
|
retry_command() {
|
|
local max_attempts="$1"
|
|
local delay="$2"
|
|
shift 2
|
|
local attempt=1
|
|
|
|
while [ $attempt -le $max_attempts ]; do
|
|
if "$@" 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
if [ $attempt -lt $max_attempts ]; then
|
|
log_warning "Command failed, retrying in ${delay} seconds... (attempt $attempt/$max_attempts)"
|
|
sleep "$delay"
|
|
else
|
|
log_error "Command failed after $max_attempts attempts"
|
|
return 1
|
|
fi
|
|
attempt=$((attempt + 1))
|
|
done
|
|
}
|
|
|
|
# Compare semantic versions (returns 0 if v1 >= v2, 1 otherwise)
|
|
# Usage: version_gte "v3.17.3" "v3.16.0"
|
|
# Example: version_gte "$current_version" "$required_version" && echo "OK"
|
|
version_gte() {
|
|
[ "$1" = "$(printf '%s\n' "$1" "$2" | sort -V | tail -1)" ]
|
|
}
|
|
|
|
# ============================================================================
|
|
# Shared Resources Configuration (for dual KServe + LLMISVC installation)
|
|
# ============================================================================
|
|
|
|
determine_shared_resources_config() {
|
|
local install_mode="${1}"
|
|
local enable_kserve="${2}"
|
|
local enable_llmisvc="${3}"
|
|
|
|
if ! is_positive "${enable_kserve}" && ! is_positive "${enable_llmisvc}"; then
|
|
return
|
|
fi
|
|
|
|
log_info "Determining shared resources configuration (KSERVE=${enable_kserve}, LLMISVC=${enable_llmisvc})..."
|
|
|
|
if [ "${install_mode}" = "helm" ]; then
|
|
determine_shared_resources_helm "${enable_kserve}" "${enable_llmisvc}"
|
|
elif [ "${install_mode}" = "kustomize" ]; then
|
|
determine_shared_resources_kustomize
|
|
else
|
|
log_error "INSTALL_MODE not set. Must be 'helm' or 'kustomize'"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
determine_shared_resources_helm() {
|
|
local enable_kserve="${1}"
|
|
local enable_llmisvc="${2}"
|
|
|
|
local kserve_installed=$(helm list -n "${KSERVE_NAMESPACE}" -q 2>/dev/null | grep -c "^kserve-resources$" || true)
|
|
local llmisvc_installed=$(helm list -n "${KSERVE_NAMESPACE}" -q 2>/dev/null | grep -c "^kserve-llmisvc-resources$" || true)
|
|
|
|
if [ "${kserve_installed}" = "0" ] && [ "${llmisvc_installed}" = "0" ]; then
|
|
# First installation - check which components are being enabled
|
|
if is_positive "${enable_kserve}" && is_positive "${enable_llmisvc}"; then
|
|
# Both enabled: kserve-resources installs first and creates shared resources
|
|
log_info "[Helm] First install (both) - kserve-resources creates shared resources, llmisvc-resources does not"
|
|
LLMISVC_EXTRA_ARGS="${LLMISVC_EXTRA_ARGS:-} --set kserve.createSharedResources=false"
|
|
elif is_positive "${enable_kserve}" && ! is_positive "${enable_llmisvc}"; then
|
|
# Only kserve enabled: kserve-resources creates shared resources
|
|
log_info "[Helm] First install (kserve only) - kserve-resources will create shared resources"
|
|
# Use default value (true) - no extra args needed
|
|
elif ! is_positive "${enable_kserve}" && is_positive "${enable_llmisvc}"; then
|
|
# Only llmisvc enabled: llmisvc-resources creates shared resources
|
|
log_info "[Helm] First install (llmisvc only) - llmisvc-resources will create shared resources"
|
|
# Use default value (true) - no extra args needed
|
|
else
|
|
# Neither enabled - shouldn't reach here
|
|
log_error "[Helm] No components enabled"
|
|
return 1
|
|
fi
|
|
elif [ "${kserve_installed}" = "1" ] && [ "${llmisvc_installed}" = "0" ]; then
|
|
log_info "[Helm] Only kserve-resources installed - setting createSharedResources=false for LLMISVC"
|
|
LLMISVC_EXTRA_ARGS="${LLMISVC_EXTRA_ARGS:-} --set kserve.createSharedResources=false"
|
|
elif [ "${kserve_installed}" = "0" ] && [ "${llmisvc_installed}" = "1" ]; then
|
|
log_info "[Helm] Only kserve-llmisvc-resources installed - setting createSharedResources=false for KSERVE"
|
|
KSERVE_EXTRA_ARGS="${KSERVE_EXTRA_ARGS:-} --set kserve.createSharedResources=false"
|
|
else
|
|
local kserve_has_false=$(helm get values kserve-resources -n "${KSERVE_NAMESPACE}" 2>/dev/null | grep -c "createSharedResources: false" || true)
|
|
|
|
if [ "${kserve_has_false}" = "1" ]; then
|
|
log_info "[Helm] Maintaining createSharedResources=false for KSERVE"
|
|
KSERVE_EXTRA_ARGS="${KSERVE_EXTRA_ARGS:-} --set kserve.createSharedResources=false"
|
|
else
|
|
log_info "[Helm] Setting createSharedResources=false for LLMISVC"
|
|
LLMISVC_EXTRA_ARGS="${LLMISVC_EXTRA_ARGS:-} --set kserve.createSharedResources=false"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
determine_shared_resources_kustomize() {
|
|
KSERVE_INSTALLED=$(kubectl get deployment kserve-controller-manager -n "${KSERVE_NAMESPACE}" 2>/dev/null | grep -c "kserve-controller-manager" || true)
|
|
LLMISVC_INSTALLED=$(kubectl get deployment llmisvc-controller-manager -n "${KSERVE_NAMESPACE}" 2>/dev/null | grep -c "llmisvc-controller-manager" || true)
|
|
|
|
export KSERVE_INSTALLED
|
|
export LLMISVC_INSTALLED
|
|
|
|
log_info "[Kustomize] Installation status(0: not installed, 1: installed): KSERVE=${KSERVE_INSTALLED}, LLMISVC=${LLMISVC_INSTALLED}"
|
|
}
|
|
|
|
# ============================================================================
|
|
|
|
# Set environment variable based on priority order:
|
|
# Priority: 1. Runtime env > 2. Component env > 3. Global env > 4. Component default
|
|
# Usage: set_env_with_priority VAR_NAME COMPONENT_ENV_VALUE GLOBAL_ENV_VALUE DEFAULT_VALUE
|
|
set_env_with_priority() {
|
|
local var_name="$1"
|
|
local component_value="$2"
|
|
local global_value="$3"
|
|
local default_value="$4"
|
|
|
|
# Get current value
|
|
local current_value
|
|
eval "current_value=\${${var_name}}"
|
|
|
|
# If current value exists and differs from default, it's a runtime value - keep it
|
|
if [ -n "$current_value" ] && [ -n "$default_value" ] && [ "$current_value" != "$default_value" ]; then
|
|
# This is a runtime value, keep it
|
|
return
|
|
fi
|
|
|
|
# Apply priority: component env > global env > default
|
|
if [ -n "$component_value" ]; then
|
|
eval "export $var_name=\"$component_value\""
|
|
elif [ -n "$global_value" ]; then
|
|
eval "export $var_name=\"$global_value\""
|
|
fi
|
|
# If both are empty, variable keeps its default value
|
|
}
|
|
|
|
#================================================
|
|
# Determine repository root using find_repo_root
|
|
#================================================
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" && pwd)"
|
|
REPO_ROOT="$(find_repo_root "${SCRIPT_DIR}" "true")"
|
|
export REPO_ROOT
|
|
|
|
# Set up BIN_DIR - use repo bin if it exists, otherwise use temp directory
|
|
if [[ -d "${REPO_ROOT}/bin" ]]; then
|
|
export BIN_DIR="${REPO_ROOT}/bin"
|
|
else
|
|
export BIN_DIR="$(mktemp -d)"
|
|
log_info "Using temp BIN_DIR: ${BIN_DIR}"
|
|
fi
|
|
|
|
export PATH="${BIN_DIR}:${PATH}"
|
|
|
|
REINSTALL="${REINSTALL:-false}"
|
|
UNINSTALL="${UNINSTALL:-false}"
|
|
FORCE_UPGRADE="${FORCE_UPGRADE:-false}"
|
|
|
|
if [[ "$*" == *"--uninstall"* ]]; then
|
|
UNINSTALL=true
|
|
elif [[ "$*" == *"--reinstall"* ]]; then
|
|
REINSTALL=true
|
|
elif [[ "$*" == *"--force-upgrade"* ]]; then
|
|
FORCE_UPGRADE=true
|
|
fi
|
|
|
|
export REINSTALL
|
|
export UNINSTALL
|
|
export FORCE_UPGRADE
|
|
|
|
# RELEASE mode (from definition file)
|
|
RELEASE="true"
|
|
export RELEASE
|
|
|
|
#================================================
|
|
# Version Dependencies (from kserve-deps.env)
|
|
#================================================
|
|
|
|
GOLANGCI_LINT_VERSION=v2.9.0
|
|
CONTROLLER_TOOLS_VERSION=v0.19.0
|
|
ENVTEST_VERSION=release-0.19
|
|
YQ_VERSION=v4.52.1
|
|
HELM_VERSION=v3.16.3
|
|
KUSTOMIZE_VERSION=v5.8.1
|
|
HELM_DOCS_VERSION=v1.12.0
|
|
POETRY_VERSION=1.8.3
|
|
UV_VERSION=0.7.8
|
|
RUFF_VERSION=0.14.13
|
|
PINACT_VERSION=v3.9.0
|
|
KIND_VERSION=v0.30.0
|
|
CERT_MANAGER_VERSION=v1.17.0
|
|
ENVOY_GATEWAY_VERSION=v1.6.3
|
|
ENVOY_AI_GATEWAY_VERSION=v0.5.0
|
|
KNATIVE_OPERATOR_VERSION=v1.21.1
|
|
KNATIVE_SERVING_VERSION=1.21.1
|
|
KEDA_OTEL_ADDON_VERSION=v0.0.6
|
|
KSERVE_VERSION=v0.18.1
|
|
ISTIO_VERSION=1.27.1
|
|
KEDA_VERSION=2.17.3
|
|
OPENTELEMETRY_OPERATOR_VERSION=0.74.3
|
|
LWS_VERSION=v0.8.0
|
|
GATEWAY_API_VERSION=v1.4.1
|
|
GIE_VERSION=v1.3.1
|
|
WVA_VERSION=v0.6.0
|
|
|
|
#================================================
|
|
# Global Variables (from global-vars.env)
|
|
#================================================
|
|
# These provide default namespace values that can be overridden
|
|
# by environment variables or GLOBAL_ENV settings below
|
|
|
|
KEDA_NAMESPACE="${KEDA_NAMESPACE:-keda}"
|
|
KSERVE_NAMESPACE="${KSERVE_NAMESPACE:-kserve}"
|
|
OTEL_NAMESPACE="${OTEL_NAMESPACE:-opentelemetry-operator}"
|
|
OPERATOR_NAMESPACE="${OPERATOR_NAMESPACE:-knative-operator}"
|
|
SERVING_NAMESPACE="${SERVING_NAMESPACE:-knative-serving}"
|
|
ISTIO_NAMESPACE="${ISTIO_NAMESPACE:-istio-system}"
|
|
GATEWAY_NAMESPACE="${GATEWAY_NAMESPACE:-kserve}"
|
|
DEPLOYMENT_MODE="${DEPLOYMENT_MODE:-Knative}"
|
|
GATEWAY_NETWORK_LAYER="${GATEWAY_NETWORK_LAYER:-false}"
|
|
ENABLE_KSERVE="${ENABLE_KSERVE:-true}"
|
|
ENABLE_LLMISVC="${ENABLE_LLMISVC:-false}"
|
|
ENABLE_LOCALMODEL="${ENABLE_LOCALMODEL:-false}"
|
|
EMBED_MANIFESTS="${EMBED_MANIFESTS:-false}"
|
|
EMBED_TEMPLATES="${EMBED_TEMPLATES:-false}"
|
|
KSERVE_CUSTOM_ISVC_CONFIGS="${KSERVE_CUSTOM_ISVC_CONFIGS:-}"
|
|
|
|
#================================================
|
|
# Component-Specific Variables
|
|
#================================================
|
|
|
|
NETWORK_LAYER="${NETWORK_LAYER:-istio}"
|
|
TEMPLATE_DIR="${SCRIPT_DIR}/templates"
|
|
INSTALL_MODE="helm"
|
|
USE_LOCAL_CHARTS="${USE_LOCAL_CHARTS:-false}"
|
|
CHARTS_DIR="${REPO_ROOT}/charts"
|
|
SET_KSERVE_VERSION="${SET_KSERVE_VERSION:-}"
|
|
SHARED_EXTRA_ARGS="${SHARED_EXTRA_ARGS:-}"
|
|
ENABLE_KSERVE="${ENABLE_KSERVE:-true}"
|
|
ENABLE_LLMISVC="${ENABLE_LLMISVC:-${LLMISVC:-false}}"
|
|
ENABLE_LOCALMODEL="${ENABLE_LOCALMODEL:-${LOCALMODEL:-false}}"
|
|
CRD_CHARTS=()
|
|
RESOURCE_CHARTS=()
|
|
RESOURCE_EXTRA_ARGS_LIST=()
|
|
TARGET_DEPLOYMENT_NAMES=()
|
|
INSTALL_RUNTIMES="${INSTALL_RUNTIMES:-${ENABLE_KSERVE:-false}}"
|
|
INSTALL_LLMISVC_CONFIGS="${INSTALL_LLMISVC_CONFIGS:-${ENABLE_LLMISVC:-false}}"
|
|
RUNTIME_CONFIG_CHART_NAME="kserve-runtime-configs"
|
|
|
|
#================================================
|
|
# Template Functions (EMBED_TEMPLATES MODE)
|
|
#================================================
|
|
|
|
# ============================================================================
|
|
# Template Functions: knative-operator
|
|
# ============================================================================
|
|
|
|
get_knative_serving_istio() {
|
|
cat <<'KNATIVE_SERVING_ISTIO_EOF'
|
|
apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: knative-serving
|
|
---
|
|
apiVersion: operator.knative.dev/v1beta1
|
|
kind: KnativeServing
|
|
metadata:
|
|
name: knative-serving
|
|
namespace: knative-serving
|
|
spec:
|
|
version: "1.21.1"
|
|
config:
|
|
deployment:
|
|
# Skip tag resolution for certain domains
|
|
registries-skipping-tag-resolving: "nvcr.io,index.docker.io"
|
|
domain:
|
|
# Patch the external domain as the default domain svc.cluster.local is not exposed on ingress (from knative 1.8)
|
|
example.com: ""
|
|
workloads:
|
|
- name: controller
|
|
resources:
|
|
- container: controller
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: activator
|
|
resources:
|
|
- container: activator
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: autoscaler
|
|
resources:
|
|
- container: autoscaler
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: domain-mapping
|
|
resources:
|
|
- container: domain-mapping
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: webhook
|
|
resources:
|
|
- container: webhook
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: domainmapping-webhook
|
|
resources:
|
|
- container: domainmapping-webhook
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: net-istio-controller
|
|
resources:
|
|
- container: controller
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: net-istio-webhook
|
|
resources:
|
|
- container: webhook
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
KNATIVE_SERVING_ISTIO_EOF
|
|
}
|
|
|
|
get_knative_serving_kourier() {
|
|
cat <<'KNATIVE_SERVING_KOURIER_EOF'
|
|
apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: knative-serving
|
|
---
|
|
apiVersion: operator.knative.dev/v1beta1
|
|
kind: KnativeServing
|
|
metadata:
|
|
name: knative-serving
|
|
namespace: knative-serving
|
|
spec:
|
|
version: "1.21.1"
|
|
ingress:
|
|
kourier:
|
|
enabled: true
|
|
config:
|
|
network:
|
|
ingress-class: "kourier.ingress.networking.knative.dev"
|
|
deployment:
|
|
# Skip tag resolution for certain domains
|
|
registries-skipping-tag-resolving: "nvcr.io,index.docker.io"
|
|
domain:
|
|
# Patch the external domain as the default domain svc.cluster.local is not exposed on ingress (from knative 1.8)
|
|
example.com: ""
|
|
workloads:
|
|
- name: controller
|
|
resources:
|
|
- container: controller
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: activator
|
|
resources:
|
|
- container: activator
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: autoscaler
|
|
resources:
|
|
- container: autoscaler
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: domain-mapping
|
|
resources:
|
|
- container: domain-mapping
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: webhook
|
|
resources:
|
|
- container: webhook
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: domainmapping-webhook
|
|
resources:
|
|
- container: domainmapping-webhook
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: net-kourier-controller
|
|
resources:
|
|
- container: controller
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
- name: 3scale-kourier-gateway
|
|
resources:
|
|
- container: kourier-gateway
|
|
requests:
|
|
cpu: 200m
|
|
memory: 200Mi
|
|
limits:
|
|
cpu: 300m
|
|
memory: 500Mi
|
|
KNATIVE_SERVING_KOURIER_EOF
|
|
}
|
|
|
|
|
|
|
|
#================================================
|
|
# Component Functions
|
|
#================================================
|
|
|
|
# ----------------------------------------
|
|
# CLI/Component: helm
|
|
# ----------------------------------------
|
|
|
|
|
|
|
|
install_helm() {
|
|
local os=$(detect_os)
|
|
local arch=$(detect_arch)
|
|
local archive_name="helm-${HELM_VERSION}-${os}-${arch}.tar.gz"
|
|
local download_url="https://get.helm.sh/${archive_name}"
|
|
|
|
log_info "Installing Helm ${HELM_VERSION} for ${os}/${arch}..."
|
|
|
|
# Check if helm is already installed in BIN_DIR with the exact required version
|
|
if [[ -f "${BIN_DIR}/helm" ]]; then
|
|
local current_version=$("${BIN_DIR}/helm" version --template='{{.Version}}' 2>/dev/null)
|
|
if [[ "$current_version" == "$HELM_VERSION" ]]; then
|
|
log_info "Helm ${current_version} is already installed in ${BIN_DIR}"
|
|
return 0
|
|
fi
|
|
[[ -n "$current_version" ]] && log_info "Replacing Helm ${current_version} with ${HELM_VERSION} in ${BIN_DIR}..."
|
|
fi
|
|
|
|
local temp_dir=$(mktemp -d)
|
|
local temp_file="${temp_dir}/${archive_name}"
|
|
|
|
if command -v wget &>/dev/null; then
|
|
wget -q "${download_url}" -O "${temp_file}"
|
|
elif command -v curl &>/dev/null; then
|
|
curl -sL "${download_url}" -o "${temp_file}"
|
|
else
|
|
log_error "Neither wget nor curl is available" >&2
|
|
rm -rf "${temp_dir}"
|
|
exit 1
|
|
fi
|
|
|
|
tar -xzf "${temp_file}" -C "${temp_dir}"
|
|
|
|
local binary_path="${temp_dir}/${os}-${arch}/helm"
|
|
|
|
if [[ ! -f "${binary_path}" ]]; then
|
|
log_error "helm binary not found in archive" >&2
|
|
rm -rf "${temp_dir}"
|
|
exit 1
|
|
fi
|
|
|
|
chmod +x "${binary_path}"
|
|
|
|
if [[ -w "${BIN_DIR}" ]]; then
|
|
mv "${binary_path}" "${BIN_DIR}/helm"
|
|
else
|
|
sudo mv "${binary_path}" "${BIN_DIR}/helm"
|
|
fi
|
|
|
|
rm -rf "${temp_dir}"
|
|
|
|
log_success "Successfully installed Helm ${HELM_VERSION} to ${BIN_DIR}/helm"
|
|
"${BIN_DIR}/helm" version
|
|
}
|
|
|
|
# ----------------------------------------
|
|
# CLI/Component: kustomize
|
|
# ----------------------------------------
|
|
|
|
|
|
|
|
install_kustomize() {
|
|
local os=$(detect_os)
|
|
local arch=$(detect_arch)
|
|
local archive_name="kustomize_${KUSTOMIZE_VERSION}_${os}_${arch}.tar.gz"
|
|
local download_url="https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F${KUSTOMIZE_VERSION}/${archive_name}"
|
|
|
|
log_info "Installing Kustomize ${KUSTOMIZE_VERSION} for ${os}/${arch}..."
|
|
|
|
if [[ -x "${BIN_DIR}/kustomize" ]]; then
|
|
local current_version=$("${BIN_DIR}/kustomize" version 2>/dev/null | awk 'match($0, /v[0-9.]+/) {print substr($0, RSTART, RLENGTH)}')
|
|
if [[ -n "$current_version" ]] && version_gte "$current_version" "$KUSTOMIZE_VERSION"; then
|
|
log_info "Kustomize ${current_version} is already installed in ${BIN_DIR} (>= ${KUSTOMIZE_VERSION})"
|
|
return 0
|
|
fi
|
|
[[ -n "$current_version" ]] && log_info "Upgrading Kustomize from ${current_version} to ${KUSTOMIZE_VERSION}..."
|
|
fi
|
|
|
|
local temp_dir=$(mktemp -d)
|
|
local temp_file="${temp_dir}/${archive_name}"
|
|
|
|
if command -v wget &>/dev/null; then
|
|
wget -q "${download_url}" -O "${temp_file}"
|
|
elif command -v curl &>/dev/null; then
|
|
curl -sL "${download_url}" -o "${temp_file}"
|
|
else
|
|
log_error "Neither wget nor curl is available" >&2
|
|
rm -rf "${temp_dir}"
|
|
exit 1
|
|
fi
|
|
|
|
tar -xzf "${temp_file}" -C "${temp_dir}"
|
|
|
|
local binary_path="${temp_dir}/kustomize"
|
|
|
|
if [[ ! -f "${binary_path}" ]]; then
|
|
log_error "kustomize binary not found in archive" >&2
|
|
rm -rf "${temp_dir}"
|
|
exit 1
|
|
fi
|
|
|
|
chmod +x "${binary_path}"
|
|
|
|
if [[ -w "${BIN_DIR}" ]]; then
|
|
mv "${binary_path}" "${BIN_DIR}/kustomize"
|
|
else
|
|
sudo mv "${binary_path}" "${BIN_DIR}/kustomize"
|
|
fi
|
|
|
|
rm -rf "${temp_dir}"
|
|
|
|
log_success "Successfully installed Kustomize ${KUSTOMIZE_VERSION} to ${BIN_DIR}/kustomize"
|
|
"${BIN_DIR}/kustomize" version
|
|
}
|
|
|
|
# ----------------------------------------
|
|
# CLI/Component: yq
|
|
# ----------------------------------------
|
|
|
|
|
|
|
|
install_yq() {
|
|
local os=$(detect_os)
|
|
local arch=$(detect_arch)
|
|
local binary_name="yq_${os}_${arch}"
|
|
local download_url="https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${binary_name}"
|
|
|
|
log_info "Installing yq ${YQ_VERSION} for ${os}/${arch}..."
|
|
|
|
if [[ -x "${BIN_DIR}/yq" ]]; then
|
|
local current_version=$("${BIN_DIR}/yq" --version 2>&1 | awk 'match($0, /v[0-9.]+/) {print substr($0, RSTART, RLENGTH)}')
|
|
# Normalize version format (add 'v' prefix if missing)
|
|
[[ -n "$current_version" && "$current_version" != v* ]] && current_version="v${current_version}"
|
|
if [[ -n "$current_version" ]] && version_gte "$current_version" "$YQ_VERSION"; then
|
|
log_info "yq ${current_version} is already installed in ${BIN_DIR} (>= ${YQ_VERSION})"
|
|
return 0
|
|
fi
|
|
[[ -n "$current_version" ]] && log_info "Upgrading yq from ${current_version} to ${YQ_VERSION}..."
|
|
fi
|
|
|
|
local temp_file=$(mktemp)
|
|
|
|
if command -v wget &>/dev/null; then
|
|
wget -q "${download_url}" -O "${temp_file}"
|
|
elif command -v curl &>/dev/null; then
|
|
curl -sL "${download_url}" -o "${temp_file}"
|
|
else
|
|
log_info "Neither wget nor curl is available" >&2
|
|
rm -f "${temp_file}"
|
|
exit 1
|
|
fi
|
|
|
|
chmod +x "${temp_file}"
|
|
|
|
if [[ -w "${BIN_DIR}" ]]; then
|
|
mv "${temp_file}" "${BIN_DIR}/yq"
|
|
else
|
|
sudo mv "${temp_file}" "${BIN_DIR}/yq"
|
|
fi
|
|
|
|
log_success "Successfully installed yq ${YQ_VERSION} to ${BIN_DIR}/yq"
|
|
"${BIN_DIR}/yq" --version
|
|
}
|
|
|
|
# ----------------------------------------
|
|
# CLI/Component: cert-manager
|
|
# ----------------------------------------
|
|
|
|
uninstall_cert_manager() {
|
|
log_info "Uninstalling cert-manager..."
|
|
helm uninstall cert-manager -n cert-manager 2>/dev/null || true
|
|
kubectl delete all --all -n cert-manager --force --grace-period=0 2>/dev/null || true
|
|
kubectl delete namespace cert-manager --wait=true --timeout=60s --force --grace-period=0 2>/dev/null || true
|
|
log_success "cert-manager uninstalled"
|
|
}
|
|
|
|
install_cert_manager() {
|
|
if helm list -n cert-manager 2>/dev/null | grep -q "cert-manager"; then
|
|
if [ "$REINSTALL" = false ]; then
|
|
log_info "cert-manager is already installed. Use --reinstall to reinstall."
|
|
return 0
|
|
else
|
|
log_info "Reinstalling cert-manager..."
|
|
uninstall_cert_manager
|
|
fi
|
|
fi
|
|
|
|
log_info "Adding cert-manager Helm repository..."
|
|
helm repo add jetstack https://charts.jetstack.io --force-update
|
|
|
|
log_info "Installing cert-manager ${CERT_MANAGER_VERSION}..."
|
|
helm install \
|
|
cert-manager jetstack/cert-manager \
|
|
--namespace cert-manager \
|
|
--create-namespace \
|
|
--version "${CERT_MANAGER_VERSION}" \
|
|
--set crds.enabled=true \
|
|
--wait
|
|
|
|
log_success "Successfully installed cert-manager ${CERT_MANAGER_VERSION} via Helm"
|
|
|
|
wait_for_pods "cert-manager" "app in (cert-manager,webhook,cainjector)" "180s"
|
|
|
|
log_success "cert-manager is ready!"
|
|
}
|
|
|
|
# ----------------------------------------
|
|
# CLI/Component: istio
|
|
# ----------------------------------------
|
|
|
|
uninstall_istio() {
|
|
log_info "Uninstalling Istio..."
|
|
helm uninstall istio-ingressgateway -n "${ISTIO_NAMESPACE}" 2>/dev/null || true
|
|
helm uninstall istiod -n "${ISTIO_NAMESPACE}" 2>/dev/null || true
|
|
helm uninstall istio-base -n "${ISTIO_NAMESPACE}" 2>/dev/null || true
|
|
kubectl delete all --all -n "${ISTIO_NAMESPACE}" --force --grace-period=0 2>/dev/null || true
|
|
kubectl delete namespace "${ISTIO_NAMESPACE}" --wait=true --timeout=60s --force --grace-period=0 2>/dev/null || true
|
|
log_success "Istio uninstalled"
|
|
}
|
|
|
|
install_istio() {
|
|
if helm list -n "${ISTIO_NAMESPACE}" 2>/dev/null | grep -q "istio-base"; then
|
|
if [ "$REINSTALL" = false ]; then
|
|
log_info "Istio is already installed. Use --reinstall to reinstall."
|
|
return 0
|
|
else
|
|
log_info "Reinstalling Istio..."
|
|
uninstall_istio
|
|
fi
|
|
fi
|
|
|
|
log_info "Adding Istio Helm repository..."
|
|
helm repo add istio https://istio-release.storage.googleapis.com/charts --force-update
|
|
|
|
log_info "Installing istio-base ${ISTIO_VERSION}..."
|
|
helm install istio-base istio/base \
|
|
--namespace "${ISTIO_NAMESPACE}" \
|
|
--create-namespace \
|
|
--version "${ISTIO_VERSION}" \
|
|
--set defaultRevision=default \
|
|
--wait \
|
|
${ISTIO_BASE_EXTRA_ARGS:-}
|
|
|
|
log_info "Installing istiod ${ISTIO_VERSION}..."
|
|
helm install istiod istio/istiod \
|
|
--namespace "${ISTIO_NAMESPACE}" \
|
|
--version "${ISTIO_VERSION}" \
|
|
--set proxy.autoInject=disabled \
|
|
--set pilot.env.ENABLE_GATEWAY_API_INFERENCE_EXTENSION=true \
|
|
--set-string pilot.podAnnotations."cluster-autoscaler\.kubernetes\.io/safe-to-evict"=true \
|
|
--wait \
|
|
${ISTIOD_EXTRA_ARGS:-}
|
|
|
|
log_info "Installing istio-ingressgateway ${ISTIO_VERSION}..."
|
|
helm install istio-ingressgateway istio/gateway \
|
|
--namespace "${ISTIO_NAMESPACE}" \
|
|
--version "${ISTIO_VERSION}" \
|
|
--set-string podAnnotations."cluster-autoscaler\.kubernetes\.io/safe-to-evict"=true \
|
|
${ISTIO_GATEWAY_EXTRA_ARGS:-}
|
|
|
|
log_success "Successfully installed Istio ${ISTIO_VERSION} via Helm"
|
|
|
|
wait_for_pods "${ISTIO_NAMESPACE}" "app=istiod" "600s"
|
|
wait_for_pods "${ISTIO_NAMESPACE}" "app=istio-ingressgateway" "600s"
|
|
|
|
log_success "Istio is ready!"
|
|
}
|
|
|
|
# ----------------------------------------
|
|
# CLI/Component: istio-ingress-class
|
|
# ----------------------------------------
|
|
|
|
uninstall_istio_ingress_class() {
|
|
log_info "Deleting Istio IngressClass 'istio'..."
|
|
kubectl delete ingressclass "istio" --ignore-not-found=true --force --grace-period=0 2>/dev/null || true
|
|
log_success "Istio IngressClass 'istio' deleted"
|
|
}
|
|
|
|
install_istio_ingress_class() {
|
|
if kubectl get ingressclass "istio" &>/dev/null; then
|
|
if [ "$REINSTALL" = false ]; then
|
|
log_info "Istio IngressClass 'istio' already exists. Use --reinstall to recreate."
|
|
return 0
|
|
else
|
|
log_info "Recreating Istio IngressClass 'istio'..."
|
|
uninstall_istio_ingress_class
|
|
fi
|
|
fi
|
|
|
|
log_info "Creating Istio IngressClass 'istio'..."
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: IngressClass
|
|
metadata:
|
|
name: istio
|
|
spec:
|
|
controller: istio.io/ingress-controller
|
|
EOF
|
|
|
|
log_success "Istio IngressClass 'istio' created successfully!"
|
|
}
|
|
|
|
# ----------------------------------------
|
|
# CLI/Component: knative-operator
|
|
# ----------------------------------------
|
|
|
|
uninstall_knative_operator() {
|
|
log_info "Uninstalling Knative Serving..."
|
|
|
|
if [ "$EMBED_TEMPLATES" = "true" ]; then
|
|
get_knative_serving_${NETWORK_LAYER} | \
|
|
kubectl delete -f - --ignore-not-found=true --force --grace-period=0 2>/dev/null || true
|
|
else
|
|
kubectl delete -f "${TEMPLATE_DIR}/knative-serving-${NETWORK_LAYER}.yaml" --ignore-not-found=true --force --grace-period=0 2>/dev/null || true
|
|
fi
|
|
|
|
kubectl delete all --all -n "${SERVING_NAMESPACE}" --force --grace-period=0 2>/dev/null || true
|
|
kubectl delete namespace "${SERVING_NAMESPACE}" --wait=true --timeout=60s --force --grace-period=0 2>/dev/null || true
|
|
|
|
log_info "Uninstalling Knative Operator..."
|
|
helm uninstall knative-operator -n "${OPERATOR_NAMESPACE}" 2>/dev/null || true
|
|
kubectl delete all --all -n "${OPERATOR_NAMESPACE}" --force --grace-period=0 2>/dev/null || true
|
|
kubectl delete namespace "${OPERATOR_NAMESPACE}" --wait=true --timeout=60s --force --grace-period=0 2>/dev/null || true
|
|
|
|
log_success "Knative uninstalled"
|
|
}
|
|
|
|
install_knative_operator() {
|
|
log_info "Network layer: ${NETWORK_LAYER}"
|
|
|
|
if helm list -n "${OPERATOR_NAMESPACE}" 2>/dev/null | grep -q "knative-operator"; then
|
|
if [ "$REINSTALL" = false ]; then
|
|
log_info "Knative Operator is already installed. Checking Knative Serving..."
|
|
|
|
if kubectl get knativeserving knative-serving -n "${SERVING_NAMESPACE}" &>/dev/null; then
|
|
log_info "Knative Serving is already deployed. Use --reinstall to reinstall."
|
|
return 0
|
|
fi
|
|
else
|
|
log_info "Reinstalling Knative..."
|
|
uninstall_knative_operator
|
|
fi
|
|
fi
|
|
|
|
log_info "Installing Knative Operator ${KNATIVE_OPERATOR_VERSION}..."
|
|
|
|
if [[ "${KNATIVE_OPERATOR_VERSION}" == v* ]]; then
|
|
OPERATOR_CHART_URL="https://github.com/knative/operator/releases/download/knative-${KNATIVE_OPERATOR_VERSION}/knative-operator-${KNATIVE_OPERATOR_VERSION}.tgz"
|
|
log_info "Using GitHub release: ${OPERATOR_CHART_URL}"
|
|
|
|
# shellcheck disable=SC2086
|
|
helm install knative-operator \
|
|
--namespace "${OPERATOR_NAMESPACE}" \
|
|
--create-namespace \
|
|
--wait \
|
|
${KNATIVE_OPERATOR_EXTRA_ARGS:-} \
|
|
"${OPERATOR_CHART_URL}"
|
|
else
|
|
log_info "Adding Knative Operator Helm repository..."
|
|
helm repo add knative-operator https://knative.github.io/operator --force-update
|
|
|
|
# shellcheck disable=SC2086
|
|
helm install knative-operator knative-operator/knative-operator \
|
|
--namespace "${OPERATOR_NAMESPACE}" \
|
|
--create-namespace \
|
|
--version "${KNATIVE_OPERATOR_VERSION}" \
|
|
--wait \
|
|
${KNATIVE_OPERATOR_EXTRA_ARGS:-}
|
|
fi
|
|
|
|
log_success "Successfully installed Knative Operator ${KNATIVE_OPERATOR_VERSION}"
|
|
|
|
wait_for_pods "${OPERATOR_NAMESPACE}" "name=knative-operator" "300s"
|
|
|
|
log_info "Deploying Knative Serving ${KNATIVE_SERVING_VERSION} with ${NETWORK_LAYER} network layer..."
|
|
|
|
if [ "$EMBED_TEMPLATES" = "true" ]; then
|
|
if [[ "${KNATIVE_SERVING_VERSION}" != "1.21.1" ]]; then
|
|
log_info "Customizing template with version=${KNATIVE_SERVING_VERSION}"
|
|
get_knative_serving_${NETWORK_LAYER} | \
|
|
sed -e "s/version: \".*\"/version: \"${KNATIVE_SERVING_VERSION}\"/" | \
|
|
kubectl apply --server-side -f -
|
|
else
|
|
get_knative_serving_${NETWORK_LAYER} | kubectl apply --server-side -f -
|
|
fi
|
|
else
|
|
TEMPLATE_FILE="${TEMPLATE_DIR}/knative-serving-${NETWORK_LAYER}.yaml"
|
|
|
|
if [[ ! -f "${TEMPLATE_FILE}" ]]; then
|
|
log_error "Template file not found: ${TEMPLATE_FILE}"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${KNATIVE_SERVING_VERSION}" != "1.21.1" ]]; then
|
|
log_info "Customizing template with version=${KNATIVE_SERVING_VERSION}"
|
|
sed -e "s/version: \".*\"/version: \"${KNATIVE_SERVING_VERSION}\"/" \
|
|
"${TEMPLATE_FILE}" | kubectl apply -f -
|
|
else
|
|
kubectl apply -f "${TEMPLATE_FILE}"
|
|
fi
|
|
fi
|
|
|
|
log_success "Knative Serving CR applied"
|
|
|
|
log_info "Waiting for Knative Serving to be ready..."
|
|
kubectl wait --for=condition=Ready -n "${SERVING_NAMESPACE}" KnativeServing knative-serving --timeout=300s
|
|
|
|
log_success "Knative Operator and Serving are ready!"
|
|
}
|
|
|
|
# ----------------------------------------
|
|
# CLI/Component: kserve-helm
|
|
# ----------------------------------------
|
|
|
|
uninstall_kserve_helm() {
|
|
log_info "Uninstalling KServe..."
|
|
if helm list -n "${KSERVE_NAMESPACE}" 2>/dev/null | grep -q "${RUNTIME_CONFIG_CHART_NAME}"; then
|
|
helm uninstall "${RUNTIME_CONFIG_CHART_NAME}" -n "${KSERVE_NAMESPACE}"
|
|
log_success "Successfully uninstalled Runtimes/LLMISVC configs"
|
|
fi
|
|
|
|
local all_charts=("${RESOURCE_CHARTS[@]}" "${CRD_CHARTS[@]}")
|
|
if [ ${#all_charts[@]} -gt 0 ]; then
|
|
log_info "Uninstalling charts: ${all_charts[*]}"
|
|
else
|
|
log_info "No charts to uninstall"
|
|
return 0
|
|
fi
|
|
|
|
for ((i=${#RESOURCE_CHARTS[@]}-1; i>=0; i--)); do
|
|
local chart="${RESOURCE_CHARTS[$i]}"
|
|
log_info "Uninstalling ${chart}..."
|
|
helm uninstall "${chart}" -n "${KSERVE_NAMESPACE}" 2>/dev/null || true
|
|
done
|
|
|
|
# Then uninstall CRD charts (reverse order)
|
|
for ((i=${#CRD_CHARTS[@]}-1; i>=0; i--)); do
|
|
local chart="${CRD_CHARTS[$i]}"
|
|
log_info "Uninstalling ${chart}..."
|
|
helm uninstall "${chart}" -n "${KSERVE_NAMESPACE}" 2>/dev/null || true
|
|
done
|
|
|
|
log_success "KServe charts uninstalled"
|
|
}
|
|
|
|
install_kserve_helm() {
|
|
build_helm_config_args() {
|
|
local -a config_args=()
|
|
|
|
# Update deployment mode if needed
|
|
if [ "${DEPLOYMENT_MODE}" = "Standard" ] || [ "${DEPLOYMENT_MODE}" = "RawDeployment" ]; then
|
|
log_info "Adding deployment mode configuration: ${DEPLOYMENT_MODE}"
|
|
config_args+=(--set "kserve.controller.deploymentMode=${DEPLOYMENT_MODE}")
|
|
fi
|
|
|
|
# Enable Gateway API for KServe(ISVC) if needed
|
|
if [ "${GATEWAY_NETWORK_LAYER}" != "false" ] && ! is_positive "${ENABLE_LLMISVC}"; then
|
|
log_info "Adding Gateway API configuration: enableGatewayApi=true, ingressClassName=${GATEWAY_NETWORK_LAYER}"
|
|
config_args+=(--set "kserve.controller.gateway.ingressGateway.enableGatewayApi=true")
|
|
config_args+=(--set "kserve.controller.gateway.ingressGateway.className=${GATEWAY_NETWORK_LAYER}")
|
|
fi
|
|
|
|
if is_positive "${ENABLE_LOCALMODEL}"; then
|
|
config_args+=(--set "kserve.localModel.enabled=true")
|
|
config_args+=(--set "kserve.localModel.defaultJobImage=kserve/storage-initializer")
|
|
config_args+=(--set "kserve.localModel.defaultJobImageTag=${KSERVE_VERSION}")
|
|
fi
|
|
# Add custom configurations if provided
|
|
if [ -n "${KSERVE_CUSTOM_ISVC_CONFIGS}" ]; then
|
|
log_info "Adding custom configurations: ${KSERVE_CUSTOM_ISVC_CONFIGS}"
|
|
IFS='|' read -ra custom_configs <<< "${KSERVE_CUSTOM_ISVC_CONFIGS}"
|
|
for config in "${custom_configs[@]}"; do
|
|
config_args+=(--set "${config}")
|
|
done
|
|
fi
|
|
|
|
# Only print if array has elements
|
|
if [ ${#config_args[@]} -gt 0 ]; then
|
|
printf '%s\n' "${config_args[@]}"
|
|
fi
|
|
}
|
|
|
|
if [ ${#RESOURCE_CHARTS[@]} -eq 0 ] && [ ${#CRD_CHARTS[@]} -eq 0 ]; then
|
|
log_error "No charts selected for installation. Please enable at least one component (ENABLE_KSERVE, ENABLE_LLMISVC, or ENABLE_LOCALMODEL)."
|
|
exit 1
|
|
fi
|
|
|
|
if [ ${#RESOURCE_CHARTS[@]} -gt 0 ]; then
|
|
local main_chart="${RESOURCE_CHARTS[0]}"
|
|
# Use exact match for helm release name to avoid partial matches
|
|
if helm list -n "${KSERVE_NAMESPACE}" -q 2>/dev/null | grep -x "${main_chart}" &>/dev/null; then
|
|
if ! is_positive "$REINSTALL"; then
|
|
if is_positive "$FORCE_UPGRADE"; then
|
|
log_info "Force upgrading KServe..."
|
|
else
|
|
log_info "KServe is already installed. Use --reinstall to reinstall or --force-upgrade to upgrade."
|
|
return 0
|
|
fi
|
|
else
|
|
log_info "Reinstalling KServe..."
|
|
uninstall_kserve_helm
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Determine chart repository
|
|
local CHART_REPO="oci://ghcr.io/kserve/charts"
|
|
if is_positive "${USE_LOCAL_CHARTS}"; then
|
|
CHART_REPO="${CHARTS_DIR}"
|
|
log_info "Installing KServe using local charts..."
|
|
log_info "📍 Using local charts from ${CHARTS_DIR}/"
|
|
else
|
|
log_info "Installing KServe ${KSERVE_VERSION} from OCI registry..."
|
|
fi
|
|
|
|
# Build chart version flag (only for remote charts, skip for 'latest')
|
|
local VERSION_FLAG=""
|
|
if ! is_positive "${USE_LOCAL_CHARTS}"; then
|
|
VERSION_FLAG="--version ${KSERVE_VERSION}"
|
|
fi
|
|
|
|
# Install CRD charts
|
|
for chart in "${CRD_CHARTS[@]}"; do
|
|
log_info "Installing ${chart}..."
|
|
helm upgrade -i "${chart}" "${CHART_REPO}/${chart}" \
|
|
--namespace "${KSERVE_NAMESPACE}" \
|
|
--create-namespace \
|
|
--wait \
|
|
${VERSION_FLAG} \
|
|
${SHARED_EXTRA_ARGS}
|
|
done
|
|
|
|
# Build configuration arguments for KServe/LLMIsvc
|
|
readarray -t helm_config_args < <(build_helm_config_args)
|
|
|
|
# Adopt any pre-existing GIE CRDs into the llmisvc-resources Helm release
|
|
if is_positive "${ENABLE_LLMISVC}"; then
|
|
adopt_existing_crds_for_release "kserve-llmisvc-resources" "${KSERVE_NAMESPACE}" "${GIE_CRDS[@]}"
|
|
fi
|
|
|
|
# Install resource charts
|
|
for i in "${!RESOURCE_CHARTS[@]}"; do
|
|
local chart="${RESOURCE_CHARTS[$i]}"
|
|
local extra_args="${RESOURCE_EXTRA_ARGS_LIST[$i]}"
|
|
|
|
# Apply config args only to kserve-resources chart (InferenceService configs)
|
|
local -a extra_helm_args=()
|
|
if [[ "${chart}" == "kserve-resources" ]]; then
|
|
extra_helm_args=("${helm_config_args[@]}")
|
|
fi
|
|
|
|
log_info "Installing ${chart}..."
|
|
for attempt in 1 2; do
|
|
if helm upgrade -i "${chart}" "${CHART_REPO}/${chart}" \
|
|
--namespace "${KSERVE_NAMESPACE}" \
|
|
--create-namespace \
|
|
--wait \
|
|
${VERSION_FLAG} \
|
|
--set kserve.version="${KSERVE_VERSION}" \
|
|
${SHARED_EXTRA_ARGS} \
|
|
${extra_args} \
|
|
"${extra_helm_args[@]}"; then
|
|
break
|
|
elif [ $attempt -eq 2 ]; then
|
|
log_error "Failed to install/upgrade ${chart} ${KSERVE_VERSION} after 2 attempts"
|
|
exit 1
|
|
fi
|
|
sleep 5
|
|
done
|
|
done
|
|
|
|
log_success "Successfully installed KServe"
|
|
|
|
# Wait for all controller managers to be ready
|
|
log_info "Waiting for KServe controllers to be ready..."
|
|
for deploy in "${TARGET_DEPLOYMENT_NAMES[@]}"; do
|
|
wait_for_deployment "${KSERVE_NAMESPACE}" "${deploy}" "300s"
|
|
done
|
|
|
|
log_success "KServe is ready!"
|
|
|
|
# Install Runtimes and LLMISVC configs if needed
|
|
if is_positive "${INSTALL_RUNTIMES}" || is_positive "${INSTALL_LLMISVC_CONFIGS}"; then
|
|
log_info "Installing Runtimes(${INSTALL_RUNTIMES}) and LLMISVC configs(${INSTALL_LLMISVC_CONFIGS})..."
|
|
helm upgrade -i ${RUNTIME_CONFIG_CHART_NAME} \
|
|
${CHART_REPO}/${RUNTIME_CONFIG_CHART_NAME} \
|
|
--namespace "${KSERVE_NAMESPACE}" \
|
|
--create-namespace \
|
|
--wait \
|
|
${VERSION_FLAG} \
|
|
--set kserve.version="${KSERVE_VERSION}" \
|
|
--set kserve.servingruntime.enabled=${INSTALL_RUNTIMES} \
|
|
--set kserve.llmisvcConfigs.enabled=${INSTALL_LLMISVC_CONFIGS}
|
|
log_success "Successfully installed Runtimes/LLMISVC configs"
|
|
fi
|
|
}
|
|
|
|
|
|
|
|
#================================================
|
|
# Main Installation Logic
|
|
#================================================
|
|
|
|
main() {
|
|
if [ "$UNINSTALL" = true ]; then
|
|
echo "=========================================="
|
|
echo "Uninstalling components..."
|
|
echo "=========================================="
|
|
uninstall_kserve_helm
|
|
uninstall_knative_operator
|
|
uninstall_istio_ingress_class
|
|
uninstall_istio
|
|
uninstall_cert_manager
|
|
|
|
|
|
|
|
echo "=========================================="
|
|
echo "✅ Uninstallation completed!"
|
|
echo "=========================================="
|
|
exit 0
|
|
fi
|
|
|
|
echo "=========================================="
|
|
echo "Install KServe Knative Mode and all related dependencies using helm."
|
|
echo "=========================================="
|
|
|
|
export EMBED_TEMPLATES="true"
|
|
|
|
install_helm
|
|
install_kustomize
|
|
install_yq
|
|
install_cert_manager
|
|
install_istio
|
|
install_istio_ingress_class
|
|
install_knative_operator
|
|
(
|
|
set_env_with_priority "ENABLE_LLMISVC" "False" "" ""
|
|
set_env_with_priority "ENABLE_KSERVE" "True" "" "true"
|
|
set_env_with_priority "INSTALL_RUNTIMES" "True" "" ""
|
|
set_env_with_priority "INSTALL_LLMISVC_CONFIGS" "False" "" ""
|
|
determine_shared_resources_config "${INSTALL_MODE}" "${ENABLE_KSERVE}" "${ENABLE_LLMISVC}"
|
|
|
|
if [ "${SET_KSERVE_VERSION}" != "" ]; then
|
|
log_info "Setting KServe version to ${SET_KSERVE_VERSION}"
|
|
KSERVE_VERSION="${SET_KSERVE_VERSION}"
|
|
fi
|
|
|
|
# Build chart arrays based on ENABLE_* flags
|
|
if is_positive "${ENABLE_KSERVE}"; then
|
|
log_info "KServe is enabled"
|
|
CRD_CHARTS+=("kserve-crd")
|
|
RESOURCE_CHARTS+=("kserve-resources")
|
|
RESOURCE_EXTRA_ARGS_LIST+=("${KSERVE_EXTRA_ARGS:-}")
|
|
TARGET_DEPLOYMENT_NAMES+=("kserve-controller-manager")
|
|
fi
|
|
|
|
if is_positive "${ENABLE_LLMISVC}"; then
|
|
log_info "LLMIsvc is enabled"
|
|
CRD_CHARTS+=("kserve-llmisvc-crd")
|
|
RESOURCE_CHARTS+=("kserve-llmisvc-resources")
|
|
RESOURCE_EXTRA_ARGS_LIST+=("${LLMISVC_EXTRA_ARGS:-}")
|
|
TARGET_DEPLOYMENT_NAMES+=("llmisvc-controller-manager")
|
|
fi
|
|
|
|
if is_positive "${ENABLE_LOCALMODEL}"; then
|
|
log_info "LocalModel is enabled"
|
|
CRD_CHARTS+=("kserve-localmodel-crd")
|
|
RESOURCE_CHARTS+=("kserve-localmodel-resources")
|
|
RESOURCE_EXTRA_ARGS_LIST+=("${LOCALMODEL_EXTRA_ARGS:-}")
|
|
TARGET_DEPLOYMENT_NAMES+=("kserve-localmodel-controller-manager")
|
|
fi
|
|
|
|
install_kserve_helm
|
|
)
|
|
|
|
echo "=========================================="
|
|
echo "✅ Installation completed successfully!"
|
|
echo "=========================================="
|
|
}
|
|
|
|
|
|
|
|
main "$@"
|