diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 00000000..662e2045 --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,2 @@ +external-sources=true +source-path=SCRIPTDIR diff --git a/blueprints/finetuning_service/scripts/setup-keycloak-finetuning.sh b/blueprints/finetuning_service/scripts/setup-keycloak-finetuning.sh index 020e2b08..183d95f2 100644 --- a/blueprints/finetuning_service/scripts/setup-keycloak-finetuning.sh +++ b/blueprints/finetuning_service/scripts/setup-keycloak-finetuning.sh @@ -18,7 +18,7 @@ set -e # Get Keycloak URL from kubectl KEYCLOAK_SERVICE=$(kubectl get svc -n default -l app.kubernetes.io/name=keycloak -o jsonpath='{.items[0].metadata.name}') -KEYCLOAK_PORT=$(kubectl get svc -n default ${KEYCLOAK_SERVICE} -o jsonpath='{.spec.ports[?(@.name=="http")].port}') +KEYCLOAK_PORT=$(kubectl get svc -n default "${KEYCLOAK_SERVICE}" -o jsonpath='{.spec.ports[?(@.name=="http")].port}') KEYCLOAK_URL="http://${KEYCLOAK_SERVICE}.default.svc.cluster.local:${KEYCLOAK_PORT}" # Get admin credentials from inference config diff --git a/blueprints/finetuning_service/src/api/buildkit/deploy-finetuning.sh b/blueprints/finetuning_service/src/api/buildkit/deploy-finetuning.sh index 9dc264c6..3d33c5d8 100644 --- a/blueprints/finetuning_service/src/api/buildkit/deploy-finetuning.sh +++ b/blueprints/finetuning_service/src/api/buildkit/deploy-finetuning.sh @@ -1,11 +1,14 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e # Get the absolute path of the finetuning-service root directory # buildkit is 1 level deep from root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BUILD_CONTEXT="$(cd "$SCRIPT_DIR/.." && pwd)" +BUILD_CONTEXT="$(cd "$SCRIPT_DIR/.." && pwd)" +export BUILD_CONTEXT export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000}" export IMAGE_TAG="${IMAGE_TAG:-latest}" export NAMESPACE="${NAMESPACE:-finetuning}" @@ -49,7 +52,7 @@ sleep 3 # Show logs POD_NAME="" -for i in {1..30}; do +for _ in {1..30}; do POD_NAME=$(kubectl get pods -n "$NAMESPACE" -l job-name=buildkit-finetuning-service -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") if [ -n "$POD_NAME" ]; then break diff --git a/blueprints/finetuning_service/src/api/kaniko/deploy-finetuning.sh b/blueprints/finetuning_service/src/api/kaniko/deploy-finetuning.sh index d1b365b5..7e166a0e 100644 --- a/blueprints/finetuning_service/src/api/kaniko/deploy-finetuning.sh +++ b/blueprints/finetuning_service/src/api/kaniko/deploy-finetuning.sh @@ -1,11 +1,14 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e # Get the absolute path of the finetuning-service root directory # kaniko is 1 level deep from root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BUILD_CONTEXT="$(cd "$SCRIPT_DIR/.." && pwd)" +BUILD_CONTEXT="$(cd "$SCRIPT_DIR/.." && pwd)" +export BUILD_CONTEXT export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000}" export IMAGE_TAG="${IMAGE_TAG:-latest}" export NAMESPACE="${NAMESPACE:-finetuning}" @@ -59,7 +62,7 @@ sleep 3 # Show logs POD_NAME="" -for i in {1..30}; do +for _ in {1..30}; do POD_NAME=$(kubectl get pods -n "$NAMESPACE" -l job-name=kaniko-finetuning-service -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") if [ -n "$POD_NAME" ]; then break diff --git a/blueprints/finetuning_service/src/api/scripts/deploy.sh b/blueprints/finetuning_service/src/api/scripts/deploy.sh index f059335e..7aa3c26c 100644 --- a/blueprints/finetuning_service/src/api/scripts/deploy.sh +++ b/blueprints/finetuning_service/src/api/scripts/deploy.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e # Get the directory where this script is located and go to project root @@ -25,6 +27,7 @@ if [ ! -f .env ]; then fi # Load environment variables +# shellcheck source=/dev/null # generated at runtime by setup.sh source .env # Set defaults @@ -75,7 +78,7 @@ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📦 Step 1: Creating namespace..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f - +kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - echo "✓ Namespace ready" echo "" @@ -86,7 +89,7 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━ if kubectl get secret finetuning-backend-secret -n default >/dev/null 2>&1; then kubectl get secret finetuning-backend-secret -n default -o yaml \ | sed "s/namespace: default/namespace: $NAMESPACE/" \ - | kubectl apply -n $NAMESPACE -f - + | kubectl apply -n "$NAMESPACE" -f - echo "✓ OIDC secret copied from default namespace" else echo "⚠️ Warning: finetuning-backend-secret not found in default namespace" @@ -142,9 +145,9 @@ else echo "Found key: $KEY_FILE" # Check if secret already exists - if kubectl get secret finetuning-api-tls -n $NAMESPACE >/dev/null 2>&1; then + if kubectl get secret finetuning-api-tls -n "$NAMESPACE" >/dev/null 2>&1; then echo "Updating existing TLS secret..." - kubectl delete secret finetuning-api-tls -n $NAMESPACE + kubectl delete secret finetuning-api-tls -n "$NAMESPACE" else echo "Creating new TLS secret..." fi @@ -152,7 +155,7 @@ else kubectl create secret tls finetuning-api-tls \ --cert="$CERT_FILE" \ --key="$KEY_FILE" \ - -n $NAMESPACE + -n "$NAMESPACE" echo "✓ TLS secret created successfully" fi @@ -172,8 +175,7 @@ export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000 # Run kaniko build if [ -f ./kaniko/deploy-finetuning.sh ]; then echo "Building image: $REGISTRY_URL/finetuning-service:$IMAGE_TAG" - ./kaniko/deploy-finetuning.sh - if [ $? -ne 0 ]; then + if ! ./kaniko/deploy-finetuning.sh; then echo "❌ Build failed!" exit 1 fi @@ -189,7 +191,7 @@ echo "🚀 Step 3: Deploying PostgreSQL..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Check if PostgreSQL release exists -if helm list -n $NAMESPACE | grep -q "^finetuning-service-postgresql\s"; then +if helm list -n "$NAMESPACE" | grep -q "^finetuning-service-postgresql\s"; then echo "Upgrading existing PostgreSQL..." HELM_PG_CMD="upgrade" else @@ -199,7 +201,7 @@ fi # Deploy PostgreSQL helm $HELM_PG_CMD finetuning-service-postgresql ./helm-charts/postgresql \ - --namespace $NAMESPACE \ + --namespace "$NAMESPACE" \ --set auth.password="$POSTGRES_PASSWORD" \ --wait --timeout 5m @@ -211,7 +213,7 @@ echo "🚀 Step 4: Deploying Application..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Check if release exists -if helm list -n $NAMESPACE | grep -q "^finetuning-service\s"; then +if helm list -n "$NAMESPACE" | grep -q "^finetuning-service\s"; then echo "Upgrading existing deployment..." HELM_CMD="upgrade" else @@ -221,7 +223,7 @@ fi # Deploy with Helm helm $HELM_CMD finetuning-service ./helm-charts/finetuning-api \ - --namespace $NAMESPACE \ + --namespace "$NAMESPACE" \ --set secrets.databaseUrl="postgresql://finetuning:$POSTGRES_PASSWORD@finetuning-service-postgresql:5432/finetuning" \ --set secrets.nvidiaApiKey="$NVIDIA_API_KEY" \ --set app.config.nvidiaApiUrl="$NVIDIA_API_URL" \ @@ -241,18 +243,18 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━ echo "Waiting for PostgreSQL..." kubectl wait --for=condition=ready pod \ -l app=postgres \ - -n $NAMESPACE \ + -n "$NAMESPACE" \ --timeout=300s || true # Check if database is initialized -POD_NAME=$(kubectl get pods -n $NAMESPACE -l app=postgres -o jsonpath='{.items[0].metadata.name}') +POD_NAME=$(kubectl get pods -n "$NAMESPACE" -l app=postgres -o jsonpath='{.items[0].metadata.name}') if [ ! -z "$POD_NAME" ]; then echo "Checking database schema..." - TABLE_COUNT=$(kubectl exec -n $NAMESPACE $POD_NAME -- psql -U finetuning -d finetuning -tAc "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';" 2>/dev/null || echo "0") + TABLE_COUNT=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -- psql -U finetuning -d finetuning -tAc "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';" 2>/dev/null || echo "0") if [ "$TABLE_COUNT" = "0" ]; then echo "Initializing database schema..." - cat "$PROJECT_ROOT/init-db.sql" | kubectl exec -n $NAMESPACE -i $POD_NAME -- psql -U finetuning -d finetuning + cat "$PROJECT_ROOT/init-db.sql" | kubectl exec -n "$NAMESPACE" -i "$POD_NAME" -- psql -U finetuning -d finetuning echo "✓ Database initialized" else echo "✓ Database already initialized ($TABLE_COUNT tables found)" @@ -266,7 +268,7 @@ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "⏳ Step 6: Waiting for application to be ready..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -kubectl rollout status deployment/finetuning-service -n $NAMESPACE --timeout=5m +kubectl rollout status deployment/finetuning-service -n "$NAMESPACE" --timeout=5m echo "✓ Application is ready" echo "" @@ -276,9 +278,9 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━ echo "📊 Deployment Status" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" -kubectl get pods -n $NAMESPACE +kubectl get pods -n "$NAMESPACE" echo "" -kubectl get ingress -n $NAMESPACE +kubectl get ingress -n "$NAMESPACE" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/blueprints/finetuning_service/src/api/scripts/setup.sh b/blueprints/finetuning_service/src/api/scripts/setup.sh index 0632c2bb..72bdb55b 100644 --- a/blueprints/finetuning_service/src/api/scripts/setup.sh +++ b/blueprints/finetuning_service/src/api/scripts/setup.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e # Get the directory where this script is located and go to project root @@ -42,17 +44,17 @@ echo "" echo "🎯 Training Backend Configuration" echo "This is the URL of your training backend service (e.g., Nvidia GPU server)." echo "" -read -p "Training Backend URL (e.g., https://training.example.com:8443): " NVIDIA_API_URL +read -r -p "Training Backend URL (e.g., https://training.example.com:8443): " NVIDIA_API_URL while [ -z "$NVIDIA_API_URL" ]; do echo "❌ Training Backend URL is required!" - read -p "Training Backend URL: " NVIDIA_API_URL + read -r -p "Training Backend URL: " NVIDIA_API_URL done echo "" -read -p "Training Backend API Key: " NVIDIA_API_KEY +read -r -p "Training Backend API Key: " NVIDIA_API_KEY while [ -z "$NVIDIA_API_KEY" ]; do echo "❌ Training Backend API Key is required!" - read -p "Training Backend API Key: " NVIDIA_API_KEY + read -r -p "Training Backend API Key: " NVIDIA_API_KEY done # Domain Configuration @@ -60,10 +62,10 @@ echo "" echo "🌐 Domain Configuration" echo "The API will be accessible at: https://YOUR_DOMAIN/enterprise-ai" echo "" -read -p "Your domain name (e.g., example.com): " BASE_DOMAIN +read -r -p "Your domain name (e.g., example.com): " BASE_DOMAIN while [ -z "$BASE_DOMAIN" ]; do echo "❌ Domain name is required!" - read -p "Your domain name: " BASE_DOMAIN + read -r -p "Your domain name: " BASE_DOMAIN done # TLS Certificates Path @@ -72,7 +74,7 @@ echo "🔒 TLS Certificates Configuration" echo "Provide the path to directory containing your TLS certificates." echo "Expected files: tls.crt (or cert.pem) and tls.key (or key.pem)" echo "" -read -p "Path to certs directory (default: ../certs): " CERTS_PATH +read -r -p "Path to certs directory (default: ../certs): " CERTS_PATH CERTS_PATH=${CERTS_PATH:-../certs} # Validate certs path @@ -83,14 +85,14 @@ fi # Namespace echo "" -read -p "Kubernetes namespace (default: finetuning): " NAMESPACE +read -r -p "Kubernetes namespace (default: finetuning): " NAMESPACE NAMESPACE=${NAMESPACE:-finetuning} # Optional: Dataprep API echo "" echo "🔧 Optional: Dataprep API" echo "If you have a separate dataprep service, provide the URL. Otherwise, leave empty." -read -p "Dataprep API URL (optional, press Enter to skip): " DATAPREP_API_URL +read -r -p "Dataprep API URL (optional, press Enter to skip): " DATAPREP_API_URL echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/blueprints/finetuning_service/src/dataprep/buildkit/celery/deploy-celery.sh b/blueprints/finetuning_service/src/dataprep/buildkit/celery/deploy-celery.sh index c6b752dd..00f64180 100644 --- a/blueprints/finetuning_service/src/dataprep/buildkit/celery/deploy-celery.sh +++ b/blueprints/finetuning_service/src/dataprep/buildkit/celery/deploy-celery.sh @@ -1,10 +1,13 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e #sudo apt-get install gettext-base # Get the absolute path of the dataprep directory # buildkit/celery is 2 levels deep from dataprep root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../.." && pwd)" +BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../.." && pwd)" +export BUILD_CONTEXT export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000}" echo "Building with context: $BUILD_CONTEXT" @@ -24,7 +27,7 @@ export DB_HOST=postgres.dataprep.svc.cluster.local export DB_PORT=5432 export DB_NAME=dataprep export DB_USER=postgres -export DB_PASSWORD=postgres +export DB_PASSWORD="${DB_PASSWORD:-postgres}" export DB_POOL_SIZE=5 export DB_MAX_OVERFLOW=10 diff --git a/blueprints/finetuning_service/src/dataprep/buildkit/data-prep/deploy-dataprep.sh b/blueprints/finetuning_service/src/dataprep/buildkit/data-prep/deploy-dataprep.sh index 59604301..a779e873 100644 --- a/blueprints/finetuning_service/src/dataprep/buildkit/data-prep/deploy-dataprep.sh +++ b/blueprints/finetuning_service/src/dataprep/buildkit/data-prep/deploy-dataprep.sh @@ -1,11 +1,14 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e # Get the absolute path of the dataprep directory # buildkit/data-prep is 3 levels deep from data-prep-backend root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +export BUILD_CONTEXT export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000}" echo "Building with context: $BUILD_CONTEXT" @@ -25,7 +28,7 @@ export DB_HOST=postgres.dataprep.svc.cluster.local export DB_PORT=5432 export DB_NAME=dataprep export DB_USER=postgres -export DB_PASSWORD=postgres +export DB_PASSWORD="${DB_PASSWORD:-postgres}" export DB_POOL_SIZE=5 export DB_MAX_OVERFLOW=10 diff --git a/blueprints/finetuning_service/src/dataprep/kaniko/celery/deploy-celery.sh b/blueprints/finetuning_service/src/dataprep/kaniko/celery/deploy-celery.sh index afeaef71..51cc96a0 100644 --- a/blueprints/finetuning_service/src/dataprep/kaniko/celery/deploy-celery.sh +++ b/blueprints/finetuning_service/src/dataprep/kaniko/celery/deploy-celery.sh @@ -1,10 +1,13 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e #sudo apt-get install gettext-base # Get the absolute path of the dataprep directory # kaniko/celery is 2 levels deep from dataprep root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../.." && pwd)" +BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../.." && pwd)" +export BUILD_CONTEXT export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000}" echo "Building with context: $BUILD_CONTEXT" @@ -24,7 +27,7 @@ export DB_HOST=postgres.dataprep.svc.cluster.local export DB_PORT=5432 export DB_NAME=dataprep export DB_USER=postgres -export DB_PASSWORD=postgres +export DB_PASSWORD="${DB_PASSWORD:-postgres}" export DB_POOL_SIZE=5 export DB_MAX_OVERFLOW=10 diff --git a/blueprints/finetuning_service/src/dataprep/kaniko/data-prep/deploy-dataprep.sh b/blueprints/finetuning_service/src/dataprep/kaniko/data-prep/deploy-dataprep.sh index 9d9ae38d..2bff38d0 100644 --- a/blueprints/finetuning_service/src/dataprep/kaniko/data-prep/deploy-dataprep.sh +++ b/blueprints/finetuning_service/src/dataprep/kaniko/data-prep/deploy-dataprep.sh @@ -1,11 +1,14 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e # Get the absolute path of the dataprep directory # kaniko/data-prep is 3 levels deep from data-prep-backend root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +export BUILD_CONTEXT export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000}" echo "Building with context: $BUILD_CONTEXT" @@ -25,7 +28,7 @@ export DB_HOST=postgres.dataprep.svc.cluster.local export DB_PORT=5432 export DB_NAME=dataprep export DB_USER=postgres -export DB_PASSWORD=postgres +export DB_PASSWORD="${DB_PASSWORD:-postgres}" export DB_POOL_SIZE=5 export DB_MAX_OVERFLOW=10 diff --git a/blueprints/finetuning_service/src/ui/deployment/buildkit/deploy-frontend.sh b/blueprints/finetuning_service/src/ui/deployment/buildkit/deploy-frontend.sh index 9a172459..f2d11ce4 100644 --- a/blueprints/finetuning_service/src/ui/deployment/buildkit/deploy-frontend.sh +++ b/blueprints/finetuning_service/src/ui/deployment/buildkit/deploy-frontend.sh @@ -1,9 +1,12 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e #sudo apt-get install gettext-base # Get the absolute path of the ui directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../.." && pwd)" +BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../.." && pwd)" +export BUILD_CONTEXT export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000}" echo "Building with context: $BUILD_CONTEXT" @@ -36,7 +39,7 @@ echo "Monitor with: kubectl logs -f job/buildkit-frontend -n ${NAMESPACE:-finetu # Wait for pod to start echo "Waiting for BuildKit pod to start..." -for i in $(seq 1 60); do +for _ in $(seq 1 60); do POD_NAME=$(kubectl get pods -n "${NAMESPACE:-finetuning-ui}" -l job-name=buildkit-frontend -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") if [ -n "$POD_NAME" ]; then echo "Pod started: $POD_NAME" diff --git a/blueprints/finetuning_service/src/ui/deployment/kaniko/deploy-frontend.sh b/blueprints/finetuning_service/src/ui/deployment/kaniko/deploy-frontend.sh index 03d93d4e..cffcb1a4 100644 --- a/blueprints/finetuning_service/src/ui/deployment/kaniko/deploy-frontend.sh +++ b/blueprints/finetuning_service/src/ui/deployment/kaniko/deploy-frontend.sh @@ -1,9 +1,12 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e #sudo apt-get install gettext-base # Get the absolute path of the ui directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../.." && pwd)" +BUILD_CONTEXT="$(cd "$SCRIPT_DIR/../.." && pwd)" +export BUILD_CONTEXT export REGISTRY_URL="${REGISTRY_URL:-registry.kube-system.svc.cluster.local:5000}" echo "Building with context: $BUILD_CONTEXT" @@ -32,7 +35,7 @@ echo "Monitor with: kubectl logs -f job/kaniko-frontend -n ${NAMESPACE:-finetuni # Wait for pod to start echo "Waiting for kaniko pod to start..." -for i in $(seq 1 60); do +for _ in $(seq 1 60); do POD_NAME=$(kubectl get pods -n "${NAMESPACE:-finetuning-ui}" -l job-name=kaniko-frontend -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") if [ -n "$POD_NAME" ]; then echo "Pod started: $POD_NAME" diff --git a/core/inference-stack-deploy.sh b/core/inference-stack-deploy.sh old mode 100644 new mode 100755 index 148ae5b8..fa4b9978 --- a/core/inference-stack-deploy.sh +++ b/core/inference-stack-deploy.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 # Colors RED=$(tput setaf 1) GREEN=$(tput setaf 2) @@ -186,7 +188,7 @@ main_menu() { echo "| ${CYAN}4)${NC} Brownfield Deployment of Enterprise Inference |" echo "${BLUE}|---------------------------------------------------------|${NC}" echo "Please choose an option (${CYAN}1${NC}, ${CYAN}2${NC}, ${CYAN}3${NC} or ${CYAN}4${NC}):" - read -p "${CYAN}> ${NC}" user_choice + read -r -p "${CYAN}> ${NC}" user_choice case $user_choice in 1) fresh_installation "$@" diff --git a/core/lib/add-node.sh b/core/lib/add-node.sh index ae9e01fb..964956d6 100644 --- a/core/lib/add-node.sh +++ b/core/lib/add-node.sh @@ -2,10 +2,16 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + add_inference_nodes_playbook() { echo "Add Inference LLM Nodes playbook..." - read -p "Enter the name of the worker node to be added (as defined in hosts.yml): " worker_node_name + read -r -p "Enter the name of the worker node to be added (as defined in hosts.yml): " worker_node_name if [ -z "$worker_node_name" ]; then echo "Error: No worker node names provided." return 1 diff --git a/core/lib/brownfield/brownfield_deployment.sh b/core/lib/brownfield/brownfield_deployment.sh index 7aa8009d..054f87fb 100644 --- a/core/lib/brownfield/brownfield_deployment.sh +++ b/core/lib/brownfield/brownfield_deployment.sh @@ -1,3 +1,12 @@ +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + manage_kubeconfig() { local max_attempts=3 local attempt=1 @@ -6,7 +15,7 @@ manage_kubeconfig() { # Prompt for kubeconfig file path if [ -z "$kubeconfig_file" ] || [ $attempt -gt 1 ]; then echo "Attempt $attempt of $max_attempts:" - read -p "Enter the full path to the kubeconfig file to provision brownfield deployment: " kubeconfig_file + read -r -p "Enter the full path to the kubeconfig file to provision brownfield deployment: " kubeconfig_file else echo "Using provided kubeconfig file: $kubeconfig_file" fi @@ -61,7 +70,7 @@ prompt_kubernetes_platform() { echo "4) Azure AKS" echo "5) Vanilla Kubernetes" echo "===================================================" - read -p "Enter your choice (1-5): " platform_choice + read -r -p "Enter your choice (1-5): " platform_choice case "$platform_choice" in 1) @@ -104,7 +113,8 @@ detect_kubernetes_platform() { return 1 fi - local platform=$(kubectl version --request-timeout=5s -o json 2>/dev/null | { + local platform + platform=$(kubectl version --request-timeout=5s -o json 2>/dev/null | { if kubectl api-resources --api-group=route.openshift.io --request-timeout=5s 2>/dev/null | grep -q '^routes'; then echo "openshift" elif kubectl version --request-timeout=5s -o json 2>/dev/null | grep -qi "eks"; then @@ -182,7 +192,7 @@ brownfield_deployment() { echo "| 2) Manage Models |" echo "|------------------------------------------------|" echo "Please choose an option (1 or 2):" - read -p "> " brownfield_choice + read -r -p "> " brownfield_choice case $brownfield_choice in 1) brownfield_deployment="yes" diff --git a/core/lib/cluster/config/cluster-config-init.sh b/core/lib/cluster/config/cluster-config-init.sh index 2e5564e2..23f9203e 100644 --- a/core/lib/cluster/config/cluster-config-init.sh +++ b/core/lib/cluster/config/cluster-config-init.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + deploy_cluster_config_playbook() { if [ "${deploy_observability}" = "yes" ]; then tags="deploy_cluster_dashboard" diff --git a/core/lib/cluster/config/label-nodes.sh b/core/lib/cluster/config/label-nodes.sh index cf2269b6..3e1d2d2a 100644 --- a/core/lib/cluster/config/label-nodes.sh +++ b/core/lib/cluster/config/label-nodes.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + run_label_nodes_playbook() { echo "Running the label-nodes.yml playbook to label Kubernetes nodes..." ansible-playbook -i "${INVENTORY_PATH}" playbooks/label-nodes.yml diff --git a/core/lib/cluster/config/setup-user-cluster-config.sh b/core/lib/cluster/config/setup-user-cluster-config.sh index 6f5df92e..03889897 100644 --- a/core/lib/cluster/config/setup-user-cluster-config.sh +++ b/core/lib/cluster/config/setup-user-cluster-config.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + run_kube_conf_copy_playbook() { echo "Running the setup-user-kubeconfig.yml playbook to set up kubeconfig for the user..." ansible-playbook -i "${INVENTORY_PATH}" playbooks/setup-user-kubeconfig.yml diff --git a/core/lib/cluster/deployment/cluster-purge.sh b/core/lib/cluster/deployment/cluster-purge.sh index 95a3f8c6..60c2e33a 100644 --- a/core/lib/cluster/deployment/cluster-purge.sh +++ b/core/lib/cluster/deployment/cluster-purge.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + run_reset_playbook() { echo "Running the Ansible playbook to reset the cluster..." delete_pv_on_purge="yes" @@ -11,9 +17,8 @@ run_reset_playbook() { fi ansible-playbook -i "${INVENTORY_PATH}" playbooks/deploy-keycloak-controller.yml --extra-vars "delete_pv_on_purge=${delete_pv_on_purge}" - ansible-playbook -i "${INVENTORY_PATH}" --become --become-user=root reset.yml -e "confirm_reset=yes reset_nodes=false" # Check the exit status of the Ansible playbook command - if [ $? -eq 0 ]; then + if ansible-playbook -i "${INVENTORY_PATH}" --become --become-user=root reset.yml -e "confirm_reset=yes reset_nodes=false"; then echo "Cluster reset playbook execution completed successfully." else echo "Cluster reset playbook execution failed." @@ -27,15 +32,14 @@ reset_cluster() { echo "-----------------------------------------------------------" echo "${YELLOW}NOTICE: You are initiating a reset of the existing Enterprise Inference Cluster." echo "This action will erase all current configurations, services and resources. Potentially causing service interruptions and data loss. This operation cannot be undone. ${NC}" - read -p "Are you sure you want to proceed? (yes/no): " confirm_reset + read -r -p "Are you sure you want to proceed? (yes/no): " confirm_reset if [[ "$confirm_reset" =~ ^(yes|y|Y)$ ]]; then echo "Resetting the existing Enterprise Inference cluster..." skip_check="true" purge_inference_cluster="purging" invoke_prereq_workflows "$@" - run_reset_playbook # Check if the playbook execution was successful - if [ $? -eq 0 ]; then + if run_reset_playbook; then echo "Cluster reset completed." echo -e "${BLUE}-----------------------------------------------------------------${NC}" echo -e "${GREEN}| Cluster Purge Initiated! |${NC}" diff --git a/core/lib/cluster/deployment/cluster-update.sh b/core/lib/cluster/deployment/cluster-update.sh index ad8d60cb..1c40d7ec 100644 --- a/core/lib/cluster/deployment/cluster-update.sh +++ b/core/lib/cluster/deployment/cluster-update.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + update_cluster() { echo "-------------------------------------------------" echo "| Update Existing Cluster |" @@ -10,7 +12,7 @@ update_cluster() { #echo "| 3) Update Driver and Firmware |" echo "|------------------------------------------------|" echo "Please choose an option (1 or 2):" - read -p "> " update_choice + read -r -p "> " update_choice case $update_choice in 1) manage_worker_nodes "$@" @@ -23,7 +25,7 @@ update_cluster() { # ;; *) echo "Invalid option. Please enter 1 or 2." - update_cluster + update_cluster "$@" ;; esac } \ No newline at end of file diff --git a/core/lib/cluster/deployment/fresh-install.sh b/core/lib/cluster/deployment/fresh-install.sh index 1ec01aae..38751ea7 100644 --- a/core/lib/cluster/deployment/fresh-install.sh +++ b/core/lib/cluster/deployment/fresh-install.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + fresh_installation() { @@ -133,13 +139,12 @@ fresh_installation() { if [[ "$deploy_agenticai_plugin" == "yes" ]]; then echo "Deploying Agentic AI Plugin..." - ansible-playbook -i "${INVENTORY_PATH}" ../../plugins/agenticai/playbooks/deploy-agenticai-plugin.yml \ + if ansible-playbook -i "${INVENTORY_PATH}" ../../plugins/agenticai/playbooks/deploy-agenticai-plugin.yml \ --extra-vars "cluster_url=${cluster_url} \ cert_file=${cert_file} \ key_file=${key_file} \ kubernetes_platform=${kubernetes_platform}" \ - --vault-password-file "$vault_pass_file" - if [ $? -eq 0 ]; then + --vault-password-file "$vault_pass_file"; then echo "Agentic AI Plugin deployed successfully." else echo "Failed to deploy Agentic AI Plugin. Exiting!." @@ -151,13 +156,12 @@ fresh_installation() { if [[ "$deploy_finetune_plugin" == "yes" ]]; then echo "Deploying Fine-Tuning Plugin..." - ansible-playbook -i "${INVENTORY_PATH}" ../../blueprints/finetuning_service/playbooks/deploy-all.yml \ + if ansible-playbook -i "${INVENTORY_PATH}" ../../blueprints/finetuning_service/playbooks/deploy-all.yml \ --extra-vars "cluster_url=${cluster_url} \ cert_file=${cert_file} \ key_file=${key_file} \ kubernetes_platform=${kubernetes_platform}" \ - --vault-password-file "$vault_pass_file" - if [ $? -eq 0 ]; then + --vault-password-file "$vault_pass_file"; then echo "Fine-Tuning Plugin deployed successfully." else echo "Failed to deploy Fine-Tuning Plugin. Exiting!." diff --git a/core/lib/cluster/drv-fw-update.sh b/core/lib/cluster/drv-fw-update.sh index 44367b9c..5a7a743d 100644 --- a/core/lib/cluster/drv-fw-update.sh +++ b/core/lib/cluster/drv-fw-update.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + update_gaudi_drivers() { diff --git a/core/lib/cluster/nodes/add-node.sh b/core/lib/cluster/nodes/add-node.sh index 6e976731..36156230 100644 --- a/core/lib/cluster/nodes/add-node.sh +++ b/core/lib/cluster/nodes/add-node.sh @@ -2,9 +2,15 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + add_inference_nodes_playbook() { echo "Add Inference LLM Nodes playbook..." - read -p "Enter the name of the worker node to be added (as defined in hosts.yml): " worker_node_name + read -r -p "Enter the name of the worker node to be added (as defined in hosts.yml): " worker_node_name if [ -z "$worker_node_name" ]; then echo "Error: No worker node names provided." return 1 diff --git a/core/lib/cluster/nodes/remove-node.sh b/core/lib/cluster/nodes/remove-node.sh index 675c4439..d209b0b1 100644 --- a/core/lib/cluster/nodes/remove-node.sh +++ b/core/lib/cluster/nodes/remove-node.sh @@ -1,10 +1,16 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034 + remove_inference_nodes_playbook() { echo "Remove Inference LLM Nodes playbook..." # Prompt the user for the worker node names to be removed - read -p "Enter the names of the worker nodes to be removed (comma-separated, as defined in hosts.yml): " worker_nodes_to_remove + read -r -p "Enter the names of the worker nodes to be removed (comma-separated, as defined in hosts.yml): " worker_nodes_to_remove if [ -z "$worker_nodes_to_remove" ]; then echo "Error: No worker node names provided." return 1 diff --git a/core/lib/cluster/state/cluster-state-check.sh b/core/lib/cluster/state/cluster-state-check.sh index 4fb6ea9f..64d8f03e 100644 --- a/core/lib/cluster/state/cluster-state-check.sh +++ b/core/lib/cluster/state/cluster-state-check.sh @@ -1,11 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + check_cluster_state() { echo "Checking the state of the Kubernetes cluster..." - ansible-playbook -i inventory/mycluster/hosts.yaml --become --become-user=root upgrade-cluster.yml --check # Check the exit status of the Ansible playbook command - if [ $? -eq 0 ]; then + if ansible-playbook -i inventory/mycluster/hosts.yaml --become --become-user=root upgrade-cluster.yml --check; then echo "Kubernetes cluster state check completed successfully." else echo "Kubernetes cluster state check indicates potential issues." diff --git a/core/lib/components/genai-gateway-controller.sh b/core/lib/components/genai-gateway-controller.sh index f0ef44d5..55ffe9c1 100644 --- a/core/lib/components/genai-gateway-controller.sh +++ b/core/lib/components/genai-gateway-controller.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + run_genai_gateway_playbook() { echo "Deploying GenAI Gateway Service..." echo "************************************" diff --git a/core/lib/components/ingress-controller.sh b/core/lib/components/ingress-controller.sh index fc15d7a4..5355c11c 100644 --- a/core/lib/components/ingress-controller.sh +++ b/core/lib/components/ingress-controller.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + run_ingress_nginx_playbook() { echo "Deploying the Ingress NGINX Controller..." ansible-playbook -i "${INVENTORY_PATH}" playbooks/deploy-ingress-controller.yml --extra-vars "secret_name=${cluster_url} cert_file=${cert_file} key_file=${key_file} ingress_controller=${ingress_controller}" diff --git a/core/lib/components/intel-base-operator.sh b/core/lib/components/intel-base-operator.sh index e7bcbbda..2d0096d3 100644 --- a/core/lib/components/intel-base-operator.sh +++ b/core/lib/components/intel-base-operator.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + run_deploy_habana_ai_operator_playbook() { echo "Running the deploy-habana-ai-operator.yml playbook to deploy the habana-ai-operator..." ansible-galaxy collection install community.kubernetes @@ -11,8 +17,7 @@ run_deploy_habana_ai_operator_playbook() { else gaudi_operator="" fi - ansible-playbook -i "${INVENTORY_PATH}" --become --become-user=root playbooks/deploy-habana-ai-operator.yml --extra-vars "gaudi_operator=${gaudi_operator}" - if [ $? -eq 0 ]; then + if ansible-playbook -i "${INVENTORY_PATH}" --become --become-user=root playbooks/deploy-habana-ai-operator.yml --extra-vars "gaudi_operator=${gaudi_operator}"; then echo "The deploy-habana-ai-operator.yml playbook ran successfully." else echo "The deploy-habana-ai-operator.yml playbook encountered an error." diff --git a/core/lib/components/keycloak-controller.sh b/core/lib/components/keycloak-controller.sh index fc3e786c..725592ca 100644 --- a/core/lib/components/keycloak-controller.sh +++ b/core/lib/components/keycloak-controller.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + run_keycloak_playbook() { echo "Deploying Keycloak using Ansible playbook..." install_ansible_collection diff --git a/core/lib/components/kubernetes-setup.sh b/core/lib/components/kubernetes-setup.sh index d7530651..5611fe23 100644 --- a/core/lib/components/kubernetes-setup.sh +++ b/core/lib/components/kubernetes-setup.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + install_kubernetes() { echo "Starting Kubernetes installation..." execute_and_check "Checking if the K8 is installed ..." run_fresh_install_playbook \ diff --git a/core/lib/components/observability-controller.sh b/core/lib/components/observability-controller.sh index bf37126c..51383fb3 100644 --- a/core/lib/components/observability-controller.sh +++ b/core/lib/components/observability-controller.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + deploy_observability_playbook() { tags="" if [ "${deploy_observability}" = "yes" ]; then diff --git a/core/lib/components/service-mesh/install-istio.sh b/core/lib/components/service-mesh/install-istio.sh index e0694651..5868a2bf 100644 --- a/core/lib/components/service-mesh/install-istio.sh +++ b/core/lib/components/service-mesh/install-istio.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + deploy_istio_playbook() { echo "Deploying Istio service mesh..." if [ "$deploy_istio" != "yes" ]; then diff --git a/core/lib/components/storage/install-ceph-cluster.sh b/core/lib/components/storage/install-ceph-cluster.sh index a8f3c9ca..1d42a42d 100644 --- a/core/lib/components/storage/install-ceph-cluster.sh +++ b/core/lib/components/storage/install-ceph-cluster.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + deploy_ceph_cluster() { echo "Deploying Ceph Cluster..." diff --git a/core/lib/components/storage/uninstall-ceph-cluster.sh b/core/lib/components/storage/uninstall-ceph-cluster.sh index cc280c37..1fb403b3 100644 --- a/core/lib/components/storage/uninstall-ceph-cluster.sh +++ b/core/lib/components/storage/uninstall-ceph-cluster.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + uninstall_ceph_cluster() { echo "Uninstalling Ceph Cluster..." echo "WARNING: This will PERMANENTLY DELETE ALL CEPH DATA!" diff --git a/core/lib/models/install-model-hf.sh b/core/lib/models/install-model-hf.sh index 08a2b146..a84582fe 100644 --- a/core/lib/models/install-model-hf.sh +++ b/core/lib/models/install-model-hf.sh @@ -2,6 +2,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + deploy_from_huggingface() { echo "-------------------------------------------------" echo "| Deploy Model from Huggingface |" @@ -15,9 +21,9 @@ deploy_from_huggingface() { prompt_for_input fi - read -p "Enter the Huggingface Model ID: " huggingface_model_id + read -r -p "Enter the Huggingface Model ID: " huggingface_model_id echo "${YELLOW}NOTICE: The model deployment name will be used as the release identifier for deployment. It must be unique, meaningful, and follow Kubernetes naming conventions — lowercase letters, numbers, and hyphens only. Capital letters or special characters are not allowed. ${NC}" - read -p "Enter Deployment Name for the Model: " huggingface_model_deployment_name + read -r -p "Enter Deployment Name for the Model: " huggingface_model_deployment_name echo "${YELLOW}NOTICE: Ensure the Tensor Parallel size value corresponds to the number of available Gaudi cards. Providing an incorrect value may result in the model being in a not ready state. ${NC}" if [ "$cpu_or_gpu" = "g" ] || [ "$cpu_or_gpu" = "gaudi2" ] || [ "$cpu_or_gpu" = "gaudi3" ]; then read -p "Enter the Tensor Parallel size:" -r huggingface_tensor_parellel_size diff --git a/core/lib/models/install-model.sh b/core/lib/models/install-model.sh index 40321f8d..31dbc408 100644 --- a/core/lib/models/install-model.sh +++ b/core/lib/models/install-model.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + deploy_inference_llm_models_playbook() { echo "Deploying Inference LLM Models playbook..." install_true="true" @@ -72,7 +78,7 @@ deploy_inference_llm_models_playbook() { if [[ "$brownfield_deployment" == "yes" ]]; then echo "Brownfield deployment setup is selected..." INVENTORY_PATH=$brownfield_deployment_host_file - echo $INVENTORY_PATH + echo "$INVENTORY_PATH" echo "Brownfield deployment setup was selected..." fi diff --git a/core/lib/models/list-model.sh b/core/lib/models/list-model.sh index 7cf95534..2ffe4fb4 100644 --- a/core/lib/models/list-model.sh +++ b/core/lib/models/list-model.sh @@ -1,17 +1,23 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + list_inference_llm_models_playbook() { echo "Listing installed Inference LLM Models playbook..." # Read existing parameters # Execute the Ansible playbook with all parameters - echo $model_name_list + echo "$model_name_list" echo "Listing the models..." list_model_true="true" if [[ "$brownfield_deployment" == "yes" ]]; then echo "Brownfield deployment setup is selected..." INVENTORY_PATH=$brownfield_deployment_host_file - echo $INVENTORY_PATH + echo "$INVENTORY_PATH" echo "Brownfield deployment setup was selected..." fi ansible-playbook -i "${INVENTORY_PATH}" playbooks/deploy-inference-models.yml \ diff --git a/core/lib/models/model-selection.sh b/core/lib/models/model-selection.sh index 2d09ee5f..de0ab98b 100644 --- a/core/lib/models/model-selection.sh +++ b/core/lib/models/model-selection.sh @@ -1,16 +1,22 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + model_selection(){ if [ "$list_model_menu" != "skip" ]; then if [ -z "$hugging_face_token" ] && [ "$deploy_llm_models" = "yes" ]; then - read -p "Enter the token for Huggingface: " hugging_face_token + read -r -p "Enter the token for Huggingface: " hugging_face_token else echo "Using provided Huggingface token" fi if [ -z "$deploy_llm_models" ]; then - read -p "Do you want to proceed with deploying Large Language Model (LLM)? (yes/no): " deploy_llm_models + read -r -p "Do you want to proceed with deploying Large Language Model (LLM)? (yes/no): " deploy_llm_models if [ "$deploy_llm_models" == "yes" ]; then model_name_list=$(get_model_names) echo "Proceeding to deploy models: $model_name_list" @@ -40,7 +46,7 @@ model_selection(){ echo "12. BAAI/bge-reranker-base" echo "13. codellama/CodeLlama-34b-Instruct-hf" echo "14. tiiuae/Falcon3-7B-Instruct" - read -p "Enter the numbers of the GPU models you want to deploy/remove (comma-separated, e.g., 1,3,5): " models + read -r -p "Enter the numbers of the GPU models you want to deploy/remove (comma-separated, e.g., 1,3,5): " models # Validate input IFS=',' read -ra selected <<< "$models" for m in "${selected[@]}"; do @@ -59,7 +65,7 @@ model_selection(){ echo "25. Qwen/Qwen3-1.7B" echo "26. Qwen/Qwen3-4B-Instruct-2507" echo "27. Qwen/Qwen3-Coder-30B-A3B-Instruct" - read -p "Enter the number of the CPU model you want to deploy/remove: " cpu_model + read -r -p "Enter the number of the CPU model you want to deploy/remove: " cpu_model # Validate input if ! [[ "$cpu_model" =~ ^(21|22|23|24|25|26|27)$ ]]; then echo "Error: Invalid model selected ($cpu_model). Exiting." >&2 diff --git a/core/lib/models/uninstall-model-hf.sh b/core/lib/models/uninstall-model-hf.sh index f389d6e3..c8c76f68 100644 --- a/core/lib/models/uninstall-model-hf.sh +++ b/core/lib/models/uninstall-model-hf.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + remove_model_deployed_via_huggingface(){ echo "-------------------------------------------------" @@ -22,7 +28,7 @@ remove_model_deployed_via_huggingface(){ exit 1 fi - read -p "Enter the deployment name of the model you wish to deprovision: " hugging_face_model_remove_name + read -r -p "Enter the deployment name of the model you wish to deprovision: " hugging_face_model_remove_name if [ "$cpu_or_gpu" == "c" ]; then hugging_face_model_remove_name="${hugging_face_model_remove_name}-cpu" fi diff --git a/core/lib/models/uninstall-model.sh b/core/lib/models/uninstall-model.sh index 13d16542..4a19717e 100644 --- a/core/lib/models/uninstall-model.sh +++ b/core/lib/models/uninstall-model.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + remove_inference_llm_models_playbook() { echo "Removing Inference LLM Models playbook..." echo "Uninstalling the models..." diff --git a/core/lib/system/config-vars.sh b/core/lib/system/config-vars.sh index a0245c61..806275b5 100644 --- a/core/lib/system/config-vars.sh +++ b/core/lib/system/config-vars.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034 + HOMEDIR="$(pwd)" KUBESPRAYDIR="$(dirname "$(realpath "$0")")/kubespray" VENVDIR="$(dirname "$(realpath "$0")")/kubespray225-venv" diff --git a/core/lib/system/execute-and-check.sh b/core/lib/system/execute-and-check.sh index 265b9f0b..d5a98814 100644 --- a/core/lib/system/execute-and-check.sh +++ b/core/lib/system/execute-and-check.sh @@ -1,14 +1,15 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + execute_and_check() { local description=$1 local command=$2 local success_message=$3 local failure_message=$4 echo "$description" - $command - if [ $? -eq 0 ]; then + if $command; then echo "$success_message" else echo "$failure_message" diff --git a/core/lib/system/precheck/prereq-check.sh b/core/lib/system/precheck/prereq-check.sh index 31122211..8d212908 100644 --- a/core/lib/system/precheck/prereq-check.sh +++ b/core/lib/system/precheck/prereq-check.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + run_system_prerequisites_check() { echo "Running system prerequisites check..." echo "This will verify minimum system dependencies required for deployment." diff --git a/core/lib/system/precheck/read-config-file.sh b/core/lib/system/precheck/read-config-file.sh index 45c6baf1..cd8c3c4e 100644 --- a/core/lib/system/precheck/read-config-file.sh +++ b/core/lib/system/precheck/read-config-file.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + read_config_file() { local config_file="$HOMEDIR/inventory/inference-config.cfg" if [ -f "$config_file" ]; then @@ -20,7 +26,8 @@ read_config_file() { done < "$config_file" # Load the environment variables from the temporary file - source temp_env_vars + # shellcheck source=/dev/null # generated at runtime + source temp_env_vars rm temp_env_vars local metadata_config_file="$HOMEDIR/inventory/metadata/inference-metadata.cfg" if [ -f "$metadata_config_file" ]; then @@ -31,6 +38,7 @@ read_config_file() { value=$(echo "$value" | xargs) printf "%s=%s\n" "$key" "$value" >> temp_env_vars_metadata done < "$metadata_config_file" + # shellcheck source=/dev/null # generated at runtime source temp_env_vars_metadata rm temp_env_vars_metadata else diff --git a/core/lib/system/precheck/readiness-check.sh b/core/lib/system/precheck/readiness-check.sh index 0cdf4f5b..4e3cd9c4 100644 --- a/core/lib/system/precheck/readiness-check.sh +++ b/core/lib/system/precheck/readiness-check.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + run_infrastructure_readiness_check() { echo "Running infrastructure readiness check..." echo "This will verify system compatibility and infrastructure requirements." diff --git a/core/lib/system/setup-env.sh b/core/lib/system/setup-env.sh index 9df77aa6..674ea723 100644 --- a/core/lib/system/setup-env.sh +++ b/core/lib/system/setup-env.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + setup_initial_env() { echo "Setting up the Initial Environment..." @@ -20,8 +26,7 @@ setup_initial_env() { git config --global https.proxy "$https_proxy" fi if [ ! -d "$KUBESPRAYDIR" ]; then - git clone https://github.com/kubernetes-sigs/kubespray.git $KUBESPRAYDIR - if [ $? -ne 0 ] || [ ! -d "$KUBESPRAYDIR/.git" ]; then + if ! git clone https://github.com/kubernetes-sigs/kubespray.git "$KUBESPRAYDIR" || [ ! -d "$KUBESPRAYDIR/.git" ]; then echo -e "${RED}----------------------------------------------------------------------------${NC}" echo -e "${RED}| NOTICE: Failed to clone Kubespray Repository. |${NC}" echo -e "${RED}| Unable to proceed with Inference Stack Deployment |${NC}" @@ -29,11 +34,11 @@ setup_initial_env() { echo -e "${RED}----------------------------------------------------------------------------${NC}" exit 1 fi - cd $KUBESPRAYDIR + cd "$KUBESPRAYDIR" || exit 1 git checkout "$kubespray_version" else echo "Kubespray directory already exists, skipping clone." - cd $KUBESPRAYDIR + cd "$KUBESPRAYDIR" || exit 1 fi if [[ -n "$https_proxy" ]]; then git config --global --unset http.proxy @@ -46,9 +51,9 @@ setup_initial_env() { echo "Installing python3-venv package..." if command -v apt &> /dev/null; then python_version=$($python3_interpreter -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')") - sudo apt install -y python${python_version}-venv || sudo apt install -y python3-venv + sudo apt install -y "python${python_version}-venv" || sudo apt install -y python3-venv fi - if $python3_interpreter -m venv $VENVDIR; then + if $python3_interpreter -m venv "$VENVDIR"; then echo "Virtual environment created within Kubespray directory." else echo -e "${RED}Failed to create virtual environment.${NC}" @@ -57,7 +62,8 @@ setup_initial_env() { else echo "Virtual environment already exists within Kubespray directory, skipping creation." fi - source $VENVDIR/bin/activate + # shellcheck source=/dev/null # created at runtime by python -m venv + source "$VENVDIR/bin/activate" echo "Attempting to activate the virtual environment..." if [ -z "$VIRTUAL_ENV" ]; then rm -rf "$KUBESPRAYDIR" @@ -72,11 +78,11 @@ setup_initial_env() { fi export PIP_BREAK_SYSTEM_PACKAGES=1 - $VENVDIR/bin/python3 -m pip install --upgrade pip - $VENVDIR/bin/python3 -m pip install -U -r requirements.txt + "$VENVDIR"/bin/python3 -m pip install --upgrade pip + "$VENVDIR"/bin/python3 -m pip install -U -r requirements.txt echo "Verifying Ansible Installation..." - if $VENVDIR/bin/python3 -c "import ansible" &> /dev/null; then + if "$VENVDIR"/bin/python3 -c "import ansible" &> /dev/null; then echo -e "${GREEN} Ansible installed successfully${NC}" else echo -e "${RED}----------------------------------------------------------------------------${NC}" @@ -90,18 +96,18 @@ setup_initial_env() { echo -e "${GREEN} Enterprise Inference requirements installed.${NC}" cp -r "$HOMEDIR"/helm-charts "$HOMEDIR"/scripts "$KUBESPRAYDIR"/ cp -r "$KUBESPRAYDIR"/inventory/sample/ "$KUBESPRAYDIR"/inventory/mycluster - cp "$HOMEDIR"/inventory/hosts.yaml $KUBESPRAYDIR/inventory/mycluster/ - cp "$HOMEDIR"/inventory/metadata/addons.yml $KUBESPRAYDIR/inventory/mycluster/group_vars/k8s_cluster/addons.yml + cp "$HOMEDIR"/inventory/hosts.yaml "$KUBESPRAYDIR"/inventory/mycluster/ + cp "$HOMEDIR"/inventory/metadata/addons.yml "$KUBESPRAYDIR"/inventory/mycluster/group_vars/k8s_cluster/addons.yml cp "$HOMEDIR"/playbooks/* "$KUBESPRAYDIR"/playbooks/ gaudi2_values_file_path="$REMOTEDIR/vllm/gaudi-values.yaml" gaudi3_values_file_path="$REMOTEDIR/vllm/gaudi3-values.yaml" xeon_values_file_path="$REMOTEDIR/vllm/xeon-values.yaml" - cp "$HOMEDIR"/inventory/metadata/addons.yml $KUBESPRAYDIR/inventory/mycluster/group_vars/k8s_cluster/addons.yml - cp "$HOMEDIR"/inventory/metadata/all.yml $KUBESPRAYDIR/inventory/mycluster/group_vars/all/all.yml - cp -r "$HOMEDIR"/roles/* $KUBESPRAYDIR/roles/ + cp "$HOMEDIR"/inventory/metadata/addons.yml "$KUBESPRAYDIR"/inventory/mycluster/group_vars/k8s_cluster/addons.yml + cp "$HOMEDIR"/inventory/metadata/all.yml "$KUBESPRAYDIR"/inventory/mycluster/group_vars/all/all.yml + cp -r "$HOMEDIR"/roles/* "$KUBESPRAYDIR"/roles/ mkdir -p "$KUBESPRAYDIR/config" - chmod +x $HOMEDIR/scripts/generate-vault-secrets.sh + chmod +x "$HOMEDIR"/scripts/generate-vault-secrets.sh # Only generate vault secrets if vault.yml doesn't exist or is incomplete vault_file="$HOMEDIR/inventory/metadata/vault.yml" @@ -109,7 +115,7 @@ setup_initial_env() { if [ ! -f "$vault_file" ]; then echo "vault.yml not found at $vault_file, generating vault secrets..." - bash $HOMEDIR/scripts/generate-vault-secrets.sh + bash "$HOMEDIR"/scripts/generate-vault-secrets.sh else echo "Checking vault.yml for mandatory keys..." missing_keys=() @@ -122,7 +128,7 @@ setup_initial_env() { if [ ${#missing_keys[@]} -gt 0 ]; then echo -e "${YELLOW}vault.yml exists but is missing mandatory keys: ${missing_keys[*]}${NC}" echo "Regenerating vault.yml with all mandatory keys..." - bash $HOMEDIR/scripts/generate-vault-secrets.sh + bash "$HOMEDIR"/scripts/generate-vault-secrets.sh else echo -e "${GREEN}vault.yml exists and contains all mandatory keys. Skipping generation...${NC}" fi @@ -140,9 +146,9 @@ setup_initial_env() { fi fi fi - cp "$HOMEDIR"/inventory/metadata/vault.yml $KUBESPRAYDIR/config/vault.yml + cp "$HOMEDIR"/inventory/metadata/vault.yml "$KUBESPRAYDIR"/config/vault.yml mkdir -p "$KUBESPRAYDIR/config/vars" - cp -r "$HOMEDIR"/inventory/metadata/vars/* $KUBESPRAYDIR/config/vars/ + cp -r "$HOMEDIR"/inventory/metadata/vars/* "$KUBESPRAYDIR"/config/vars/ cp "$HOMEDIR"/playbooks/* "$KUBESPRAYDIR"/playbooks/ echo "Additional files and directories copied to Kubespray directory." @@ -163,7 +169,7 @@ setup_initial_env() { invoke_prereq_workflows() { - if [ $prereq_executed -eq 0 ]; then + if [ "$prereq_executed" -eq 0 ]; then read_config_file "$@" if [ -z "$cluster_url" ] || [ -z "$cert_file" ] || [ -z "$key_file" ] || [ -z "$keycloak_client_id" ] || [ -z "$keycloak_admin_user" ] || [ -z "$keycloak_admin_password" ]; then echo "Some required arguments are missing. Prompting for input..." diff --git a/core/lib/user-menu/parse-user-prompts.sh b/core/lib/user-menu/parse-user-prompts.sh index 0928bdf2..4b5984fa 100644 --- a/core/lib/user-menu/parse-user-prompts.sh +++ b/core/lib/user-menu/parse-user-prompts.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2034,SC2154 + parse_arguments() { while [[ "$#" -gt 0 ]]; do case $1 in @@ -25,61 +31,61 @@ parse_arguments() { prompt_for_input() { if [ -z "$deploy_kubernetes_fresh" ]; then - read -p "Do you want to proceed with deploying fresh Kubernetes cluster setup? (yes/no): " deploy_kubernetes_fresh + read -r -p "Do you want to proceed with deploying fresh Kubernetes cluster setup? (yes/no): " deploy_kubernetes_fresh else echo "Proceeding with the setup of Fresh Kubernetes cluster: $deploy_kubernetes_fresh" fi if [ -z "$deploy_habana_ai_operator" ]; then - read -p "Do you want to proceed with deploying Habana AI Operator? (yes/no): " deploy_habana_ai_operator + read -r -p "Do you want to proceed with deploying Habana AI Operator? (yes/no): " deploy_habana_ai_operator else echo "Proceeding with the setup of Habana AI Operator: $deploy_habana_ai_operator" fi if [ -z "$deploy_ingress_controller" ]; then - read -p "Do you want to proceed with deploying Ingress NGINX Controller? (yes/no): " deploy_ingress_controller + read -r -p "Do you want to proceed with deploying Ingress NGINX Controller? (yes/no): " deploy_ingress_controller else echo "Proceeding with the setup of Ingress Controller: $deploy_ingress_controller" fi if [ -z "$deploy_keycloak" ]; then - read -p "Do you want to proceed with deploying Keycloak & APISIX? (yes/no): " deploy_keycloak + read -r -p "Do you want to proceed with deploying Keycloak & APISIX? (yes/no): " deploy_keycloak else echo "Proceeding with the setup of Keycloak : $deploy_keycloak" fi if [ -z "$deploy_apisix" ]; then - read -p "Do you want to proceed with deploying Keycloak & APISIX? (yes/no): " deploy_apisix + read -r -p "Do you want to proceed with deploying Keycloak & APISIX? (yes/no): " deploy_apisix else echo "Proceeding with the setup of Apisix: $deploy_apisix" fi if [ -z "$deploy_genai_gateway" ]; then - read -p "Do you want to proceed with deploying GenAI Gateway? (yes/no): " deploy_genai_gateway + read -r -p "Do you want to proceed with deploying GenAI Gateway? (yes/no): " deploy_genai_gateway else echo "Proceeding with the setup of GenAI Gateway: $deploy_genai_gateway" fi if [ -z "$deploy_observability" ]; then - read -p "Do you want to proceed with deploying Observability? (yes/no): " deploy_observability + read -r -p "Do you want to proceed with deploying Observability? (yes/no): " deploy_observability else echo "Proceeding with the setup of Observability: $deploy_observability" fi if [ -z "$deploy_ceph" ]; then - read -p "Do you want to proceed with deploying Ceph cluster setup? (yes/no): " deploy_ceph + read -r -p "Do you want to proceed with deploying Ceph cluster setup? (yes/no): " deploy_ceph else echo "Proceeding with the setup of Ceph cluster: $deploy_ceph" fi if [ "$deploy_kubernetes_fresh" == "no" ]; then if [ -z "$uninstall_ceph" ]; then - read -p "Do you want to proceed with uninstalling Ceph cluster? (yes/no): " uninstall_ceph + read -r -p "Do you want to proceed with uninstalling Ceph cluster? (yes/no): " uninstall_ceph else echo "Proceeding with Ceph cluster uninstallation: $uninstall_ceph" fi fi if [ -z "$deploy_istio" ]; then - read -p "Do you want to proceed with deploying Istio? (yes/no): " deploy_istio + read -r -p "Do you want to proceed with deploying Istio? (yes/no): " deploy_istio else echo "Proceeding with the setup of Istio: $deploy_istio" fi @@ -102,33 +108,33 @@ prompt_for_input() { model_selection "$@" echo "----- Input -----" if [ -z "$cluster_url" ]; then - read -p "Enter the CLUSTER URL (FQDN): " cluster_url + read -r -p "Enter the CLUSTER URL (FQDN): " cluster_url else echo "Using provided CLUSTER URL: $cluster_url" fi if [ -z "$cert_file" ]; then - read -p "Enter the full path to the certificate file: " cert_file + read -r -p "Enter the full path to the certificate file: " cert_file else echo "Using provided certificate file: $cert_file" fi if [ -z "$key_file" ]; then - read -p "Enter the full path to the key file: " key_file + read -r -p "Enter the full path to the key file: " key_file else echo "Using provided key file: $key_file" fi - if [ $deploy_keycloak == "yes" ]; then + if [ "$deploy_keycloak" == "yes" ]; then if [ -z "$keycloak_client_id" ]; then - read -p "Enter the keycloak client id: " keycloak_client_id + read -r -p "Enter the keycloak client id: " keycloak_client_id else echo "Using provided keycloak client id: $keycloak_client_id" fi if [ -z "$keycloak_admin_user" ]; then - read -p "Enter the Keycloak admin username: " keycloak_admin_user + read -r -p "Enter the Keycloak admin username: " keycloak_admin_user else echo "Using provided Keycloak admin username: $keycloak_admin_user" fi if [ -z "$keycloak_admin_password" ]; then - read -sp "Enter the Keycloak admin password: " keycloak_admin_password + read -r -sp "Enter the Keycloak admin password: " keycloak_admin_password echo else echo "Using provided Keycloak admin password" @@ -136,7 +142,7 @@ prompt_for_input() { fi if [[ -z "$cpu_or_gpu" ]]; then - read -p "Do you want to run on CPU or GPU? (c/g): " cpu_or_gpu + read -r -p "Do you want to run on CPU or GPU? (c/g): " cpu_or_gpu case "$cpu_or_gpu" in c|C) cpu_or_gpu="c" diff --git a/core/lib/user-menu/user-menu.sh b/core/lib/user-menu/user-menu.sh index 15f1d199..9be82a02 100644 --- a/core/lib/user-menu/user-menu.sh +++ b/core/lib/user-menu/user-menu.sh @@ -1,6 +1,8 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash + update_drivers_and_firmware() { echo "-------------------------------------------------" echo "| Update Drivers and Firmware |" @@ -10,7 +12,7 @@ update_drivers_and_firmware() { echo "| 3) Update Both Drivers and Firmware |" echo "|------------------------------------------------|" echo "Please choose an option (1, 2, or 3):" - read -p "> " update_choice + read -r -p "> " update_choice case $update_choice in 1) update_gaudi_drivers @@ -38,7 +40,7 @@ manage_worker_nodes() { echo "| 2) Remove Worker Node |" echo "|------------------------------------------------|" echo "Please choose an option (1 or 2):" - read -p "> " worker_choice + read -r -p "> " worker_choice case $worker_choice in 1) add_worker_node "$@" @@ -48,7 +50,7 @@ manage_worker_nodes() { ;; *) echo "Invalid option. Please enter 1 or 2." - manage_worker_nodes + manage_worker_nodes "$@" ;; esac } @@ -66,7 +68,7 @@ manage_models() { echo "| 5) Remove Model using deployment name |" echo "|------------------------------------------------|" echo "Please choose an option (1, 2, 3, or 4):" - read -p "> " model_choice + read -r -p "> " model_choice case $model_choice in 1) add_model "$@" @@ -85,7 +87,7 @@ manage_models() { ;; *) echo "Invalid option. Please enter 1, 2, 3, or 4." - manage_models + manage_models "$@" ;; esac } diff --git a/core/lib/xeon/ballon-policy.sh b/core/lib/xeon/ballon-policy.sh index 69069fa6..52aa2590 100644 --- a/core/lib/xeon/ballon-policy.sh +++ b/core/lib/xeon/ballon-policy.sh @@ -1,6 +1,12 @@ # Copyright (C) 2025-2026 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +# shellcheck shell=bash +# This file is a library fragment sourced by core/inference-stack-deploy.sh. +# Configuration globals are defined in lib/system/config-vars.sh and populated by +# lib/system/precheck/read-config-file.sh, and are shared across the sourced fragments. +# shellcheck disable=SC2154 + deploy_nri_balloons_playbook() { if [ "$balloon_policy_cpu" = "enabled" ]; then echo "Deploying CPU Optimization (NRI Balloons & Topology Detection)..." @@ -14,10 +20,9 @@ deploy_nri_balloons_playbook() { if [ "$deploy_nri_balloon_policy" == "yes" ] || [ "$cpu_or_gpu" == "c" ]; then echo "${GREEN}Deploying CPU optimization with topology detection and NRI balloon policy${NC}" - ansible-playbook -i "${INVENTORY_PATH}" playbooks/deploy-cpu-optimization.yml \ + if ansible-playbook -i "${INVENTORY_PATH}" playbooks/deploy-cpu-optimization.yml \ --extra-vars "cpu_playbook=true" \ - --extra-vars "kubernetes_platform=${kubernetes_platform}" - if [ $? -eq 0 ]; then + --extra-vars "kubernetes_platform=${kubernetes_platform}"; then echo "${GREEN}CPU optimization deployed successfully${NC}" else echo "${RED}CPU optimization deployment failed${NC}" diff --git a/core/roles/utils/files/compute_reserved_cpus.sh b/core/roles/utils/files/compute_reserved_cpus.sh index 10377a98..2eac7148 100644 --- a/core/roles/utils/files/compute_reserved_cpus.sh +++ b/core/roles/utils/files/compute_reserved_cpus.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -euo pipefail TOTAL_RESERVED=$1 @@ -39,8 +41,7 @@ for i in $(seq 0 $((total_numa - 1))); do all_cpus+=("$seg") fi done - IFS=$'\n' sorted=($(printf '%s\n' "${all_cpus[@]}" | sort -n)) - unset IFS + mapfile -t sorted < <(printf '%s\n' "${all_cpus[@]}" | sort -n) total_cpus_in_numa=${#sorted[@]} if [ "$ht_enabled" = true ] && [ "$total_cpus_in_numa" -ge "$((cpus_per_numa * 2))" ]; then half=$((total_cpus_in_numa / 2)) @@ -58,8 +59,7 @@ for i in $(seq 0 $((total_numa - 1))); do else selected=("${sorted[@]:0:$cpus_per_numa}") fi - IFS=$'\n' selected_sorted=($(printf '%s\n' "${selected[@]}" | sort -n)) - unset IFS + mapfile -t selected_sorted < <(printf '%s\n' "${selected[@]}" | sort -n) for cpu in "${selected_sorted[@]}"; do out="${out}${cpu},"; done done diff --git a/core/scripts/firmware-update.sh b/core/scripts/firmware-update.sh index e1af970f..43fb0c03 100644 --- a/core/scripts/firmware-update.sh +++ b/core/scripts/firmware-update.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e diff --git a/core/scripts/gaudi-firmware-driver-updater.sh b/core/scripts/gaudi-firmware-driver-updater.sh index 536dc0ed..fd63bc65 100644 --- a/core/scripts/gaudi-firmware-driver-updater.sh +++ b/core/scripts/gaudi-firmware-driver-updater.sh @@ -3,7 +3,6 @@ RED=$(tput setaf 1) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) -BLUE=$(tput setaf 4) NC=$(tput sgr0) # Reset color # Copyright (C) 2025-2026 Intel Corporation @@ -65,23 +64,20 @@ update_drivers() { # Download the base Gaudi installer echo -e "${YELLOW}Downloading Gaudi installer...${NC}" echo -e "${YELLOW}Unloading Gaudi drivers...${NC}" - wget -nv https://vault.habana.ai/artifactory/gaudi-installer/1.18.0/habanalabs-installer.sh - if [ $? -ne 0 ]; then + if ! wget -nv https://vault.habana.ai/artifactory/gaudi-installer/1.18.0/habanalabs-installer.sh; then echo -e "${RED}Failed to download Gaudi installer.${NC}" exit 1 fi echo -e "${GREEN}Gaudi installer downloaded successfully.${NC}" echo -e "${YELLOW}Installing Gaudi base components...${NC}" chmod +x habanalabs-installer.sh - ./habanalabs-installer.sh install --type base -y - if [ $? -ne 0 ]; then + if ! ./habanalabs-installer.sh install --type base -y; then echo -e "${RED}Failed to install Gaudi base components.${NC}" exit 1 fi echo -e "${GREEN}Gaudi base components installed successfully.${NC}" echo -e "${YELLOW}Installing Gaudi container runtime...${NC}" - sudo apt install -y habanalabs-container-runtime=1.18.0-524 - if [ $? -ne 0 ]; then + if ! sudo apt install -y habanalabs-container-runtime=1.18.0-524; then echo -e "${RED}Failed to install Gaudi container runtime.${NC}" #exit 1 fi diff --git a/core/scripts/generate-token.sh b/core/scripts/generate-token.sh index 8ce5adf1..3e3652f7 100644 --- a/core/scripts/generate-token.sh +++ b/core/scripts/generate-token.sh @@ -1,14 +1,19 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 # Get the directory where this script is located SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export BASE_URL=api.example.com # The base URL of the Keycloak server, note https:// is omitted -export KEYCLOAK_ADMIN_USERNAME=your-keycloak-admin-user # The username for Keycloak admin login -export KEYCLOAK_PASSWORD=changeme # The password for Keycloak admin login -export KEYCLOAK_CLIENT_ID=my-client-id # The client ID to be created in Keycloak +# Override any of these via the environment instead of editing this file, e.g. +# KEYCLOAK_PASSWORD='...' ./generate-token.sh +export BASE_URL="${BASE_URL:-api.example.com}" # Keycloak server base URL, https:// omitted +export KEYCLOAK_ADMIN_USERNAME="${KEYCLOAK_ADMIN_USERNAME:-your-keycloak-admin-user}" # Keycloak admin username +export KEYCLOAK_PASSWORD="${KEYCLOAK_PASSWORD:?Set KEYCLOAK_PASSWORD in the environment}" # Keycloak admin password +export KEYCLOAK_CLIENT_ID="${KEYCLOAK_CLIENT_ID:-my-client-id}" # Client ID to create in Keycloak -export KEYCLOAK_CLIENT_SECRET=$(bash "${SCRIPT_DIR}/keycloak-fetch-client-secret.sh" ${BASE_URL} ${KEYCLOAK_ADMIN_USERNAME} ${KEYCLOAK_PASSWORD} ${KEYCLOAK_CLIENT_ID} | awk -F': ' '/Client secret:/ {print $2}') +KEYCLOAK_CLIENT_SECRET=$(bash "${SCRIPT_DIR}/keycloak-fetch-client-secret.sh" "${BASE_URL}" "${KEYCLOAK_ADMIN_USERNAME}" "${KEYCLOAK_PASSWORD}" "${KEYCLOAK_CLIENT_ID}" | awk -F': ' '/Client secret:/ {print $2}') +export KEYCLOAK_CLIENT_SECRET # Set token lifespan on the client (in seconds) # 3600 = 1 hour, 86400 = 24 hours, 604800 = 7 days @@ -16,7 +21,7 @@ TOKEN_LIFESPAN=${TOKEN_LIFESPAN:-3600} # default 1 hour, override via env var # Get admin token first ADMIN_TOKEN=$(curl -k -s -X POST \ - https://${BASE_URL}/realms/master/protocol/openid-connect/token \ + "https://${BASE_URL}/realms/master/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password" \ -d "client_id=admin-cli" \ @@ -35,11 +40,12 @@ curl -k -s -X PUT \ -H "Content-Type: application/json" \ -d "{\"attributes\": {\"access.token.lifespan\": \"${TOKEN_LIFESPAN}\"}}" -export TOKEN=$(curl -k -s -X POST \ - https://$BASE_URL/token \ +TOKEN=$(curl -k -s -X POST \ + "https://${BASE_URL}/token" \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "grant_type=client_credentials&client_id=${KEYCLOAK_CLIENT_ID}&client_secret=${KEYCLOAK_CLIENT_SECRET}" \ | jq -r .access_token) +export TOKEN echo "BASE_URL=${BASE_URL}" echo "TOKEN=${TOKEN}" diff --git a/core/scripts/generate-vault-secrets.sh b/core/scripts/generate-vault-secrets.sh old mode 100644 new mode 100755 index 18d20f26..e3697006 --- a/core/scripts/generate-vault-secrets.sh +++ b/core/scripts/generate-vault-secrets.sh @@ -1,16 +1,18 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -e # Function to generate secure passwords generate_password() { local length=${1:-16} - openssl rand -base64 $((length * 3 / 4)) | tr -d "=+/" | cut -c1-$length + openssl rand -base64 "$((length * 3 / 4))" | tr -d "=+/" | cut -c"1-$length" } # Function to generate hex keys generate_hex_key() { local length=${1:-32} - openssl rand -hex $length + openssl rand -hex "$length" } echo "🔧 Generating secure credentials..." @@ -42,10 +44,6 @@ FINETUNE_API_REDIS_PASSWORD=$(generate_password 20) DATAPREP_POSTGRES_PASSWORD=$(generate_password 20) DATAPREP_REDIS_PASSWORD=$(generate_password 20) -# Generate connection strings -DATABASE_URL="postgresql://admin:${POSTGRESQL_PASSWORD}@genai-gateway-postgresql:5432/litellm" -CLICKHOUSE_REDIS_URL="redis://default:${CLICKHOUSE_PASSWORD}@genai-gateway-trace-valkey-primary:6379/0" - echo "Generated secure credentials!" echo "" diff --git a/core/scripts/keycloak-realmcreation.sh b/core/scripts/keycloak-realmcreation.sh index 8cf677b7..fc12e1e3 100644 --- a/core/scripts/keycloak-realmcreation.sh +++ b/core/scripts/keycloak-realmcreation.sh @@ -76,13 +76,13 @@ CLIENT_RESPONSE=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms/master/clients" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ - "clientId": "'$CLIENT_ID'", + "clientId": "'"$CLIENT_ID"'", "protocol": "openid-connect", "publicClient": false, "serviceAccountsEnabled": true }') -if echo "$CLIENT_RESPONSE" | grep -q '"errorMessage":"Client '$CLIENT_ID' already exists"'; then +if echo "$CLIENT_RESPONSE" | grep -q '"errorMessage":"Client '"$CLIENT_ID"' already exists"'; then echo "Client $CLIENT_ID already exists, skipping creation" else if [ -z "$CLIENT_RESPONSE" ]; then @@ -149,6 +149,9 @@ if [ -f "$SA_DIR/token" ] && [ -n "$KUBERNETES_SERVICE_HOST" ]; then K8S_NAMESPACE=$(cat "$SA_DIR/namespace") K8S_CACERT="$SA_DIR/ca.crt" K8S_API="https://$KUBERNETES_SERVICE_HOST:${KUBERNETES_SERVICE_PORT_HTTPS:-443}" + # The in-cluster API server must be reached directly. curl ignores CIDR entries in + # no_proxy (e.g. 10.233.0.0/16), so on proxied hosts the request would otherwise be + # sent to the external proxy and fail; --noproxy takes the exact host and bypasses it. SECRET_B64=$(printf '%s' "$CLIENT_SECRET" | base64 | tr -d '\n') SECRET_PAYLOAD=$(jq -nc \ @@ -157,18 +160,18 @@ if [ -f "$SA_DIR/token" ] && [ -n "$KUBERNETES_SERVICE_HOST" ]; then --arg data "$SECRET_B64" \ '{apiVersion:"v1",kind:"Secret",metadata:{name:$name,namespace:$ns},type:"Opaque",data:{"client-secret":$data}}') - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --cacert "$K8S_CACERT" \ + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --noproxy "$KUBERNETES_SERVICE_HOST" --cacert "$K8S_CACERT" \ -X GET "$K8S_API/api/v1/namespaces/$K8S_NAMESPACE/secrets/$CLIENT_SECRET_K8S_SECRET" \ -H "Authorization: Bearer $K8S_TOKEN") if [ "$HTTP_CODE" = "200" ]; then - RESP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --cacert "$K8S_CACERT" \ + RESP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --noproxy "$KUBERNETES_SERVICE_HOST" --cacert "$K8S_CACERT" \ -X PUT "$K8S_API/api/v1/namespaces/$K8S_NAMESPACE/secrets/$CLIENT_SECRET_K8S_SECRET" \ -H "Authorization: Bearer $K8S_TOKEN" \ -H "Content-Type: application/json" \ -d "$SECRET_PAYLOAD") else - RESP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --cacert "$K8S_CACERT" \ + RESP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --noproxy "$KUBERNETES_SERVICE_HOST" --cacert "$K8S_CACERT" \ -X POST "$K8S_API/api/v1/namespaces/$K8S_NAMESPACE/secrets" \ -H "Authorization: Bearer $K8S_TOKEN" \ -H "Content-Type: application/json" \ diff --git a/core/scripts/vllm-quickstart/vllm-model-runner.sh b/core/scripts/vllm-quickstart/vllm-model-runner.sh index 881cf02d..e9ebb80d 100755 --- a/core/scripts/vllm-quickstart/vllm-model-runner.sh +++ b/core/scripts/vllm-quickstart/vllm-model-runner.sh @@ -140,7 +140,7 @@ install_docker() { sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin >/dev/null 2>&1 # Add current user to docker group to run docker without sudo - sudo usermod -aG docker $USER + sudo usermod -aG docker "$USER" # Start and enable Docker service sudo systemctl start docker @@ -188,6 +188,7 @@ clone_vllm_repository() { # Check if it's a git repository and has the examples directory if sudo test -d "$vllm_path/.git" && sudo test -d "$vllm_path/examples"; then log "INFO" "Updating existing vLLM repository..." + # shellcheck disable=SC2024 # LOG_FILE is user-owned in /tmp; piping through tee would mask git's exit status if sudo git -C "$vllm_path" pull origin main >> "$LOG_FILE" 2>&1; then log "SUCCESS" "vLLM repository updated successfully" return 0 @@ -225,6 +226,7 @@ clone_vllm_repo_fresh() { sudo mkdir -p "$(dirname "$vllm_path")" 2>/dev/null || true # Clone the repository + # shellcheck disable=SC2024 # LOG_FILE is user-owned in /tmp; piping through tee would mask git's exit status if sudo git clone --depth 1 https://github.com/vllm-project/vllm.git "$vllm_path" >> "$LOG_FILE" 2>&1; then log "SUCCESS" "vLLM repository cloned successfully" @@ -248,7 +250,7 @@ install_dependencies() { if ! sudo -n true 2>/dev/null; then log "WARN" "This script requires sudo privileges to install dependencies" log "INFO" "Please run: sudo -v" - read -p "Press Enter after running sudo -v to continue..." + read -r -p "Press Enter after running sudo -v to continue..." fi # Check and install curl first (needed for Docker installation) @@ -286,7 +288,7 @@ install_dependencies() { else if [[ "$need_newgrp" == "false" ]]; then log "INFO" "Adding user to docker group..." - sudo usermod -aG docker $USER + sudo usermod -aG docker "$USER" need_rerun=true fi fi @@ -341,7 +343,7 @@ validate_environment() { # Check Docker daemon check_docker_access - if ! ${USE_SUDO}docker info >/dev/null 2>&1; then + if ! "${DOCKER_SUDO[@]}" docker info >/dev/null 2>&1; then cleanup_and_exit 1 "Docker daemon is not running or not accessible." fi @@ -351,13 +353,16 @@ validate_environment() { # Global variables for model data declare -a MODEL_KEYS declare -g USE_SUDO="" +declare -ga DOCKER_SUDO=() # Helper function to determine if we need sudo for Docker check_docker_access() { if groups | grep -q docker; then USE_SUDO="" + DOCKER_SUDO=() else USE_SUDO="sudo " + DOCKER_SUDO=(sudo) log "WARN" "User not in docker group, using sudo for Docker commands" fi } @@ -367,10 +372,13 @@ load_configuration() { log "INFO" "Loading configuration from $CONFIG_FILE" # Extract model list - local temp_keys - if ! temp_keys=($(jq -r '.models | keys[]' "$CONFIG_FILE" 2>/dev/null)); then + local temp_keys=() keys_output + if ! keys_output=$(jq -r '.models | keys[]' "$CONFIG_FILE" 2>/dev/null); then cleanup_and_exit 1 "Failed to parse model keys from configuration" fi + if [[ -n "$keys_output" ]]; then + mapfile -t temp_keys <<< "$keys_output" + fi if [[ ${#temp_keys[@]} -eq 0 ]]; then cleanup_and_exit 1 "No models found in configuration" @@ -434,7 +442,7 @@ compute_parallel_config() { # Display available models and get user selection select_model() { - printf "${YELLOW}Available Models:${NC}\n" >&2 + printf '%bAvailable Models:%b\n' "$YELLOW" "$NC" >&2 echo >&2 for i in "${!MODEL_KEYS[@]}"; do @@ -445,8 +453,8 @@ select_model() { done echo >&2 - printf "${YELLOW}Enter the number of the model you want to start:${NC}\n" >&2 - read -p "> " choice + printf '%bEnter the number of the model you want to start:%b\n' "$YELLOW" "$NC" >&2 + read -r -p "> " choice # Validate user input if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#MODEL_KEYS[@]} )); then @@ -511,27 +519,27 @@ stop_existing_container() { # Check for both running and stopped containers with the same name local existing_container - existing_container=$(${USE_SUDO}docker ps -aq --filter "name=$CONTAINER_NAME" 2>/dev/null) + existing_container=$("${DOCKER_SUDO[@]}" docker ps -aq --filter "name=$CONTAINER_NAME" 2>/dev/null) if [[ -n "$existing_container" ]]; then log "INFO" "Stopping existing container: $existing_container" # Stop the container if it's running - if ${USE_SUDO}docker ps -q --filter "name=$CONTAINER_NAME" | grep -q "$existing_container"; then - if ! ${USE_SUDO}docker stop "$existing_container" >> "$LOG_FILE" 2>&1; then + if "${DOCKER_SUDO[@]}" docker ps -q --filter "name=$CONTAINER_NAME" | grep -q "$existing_container"; then + if ! "${DOCKER_SUDO[@]}" docker stop "$existing_container" >> "$LOG_FILE" 2>&1; then log "WARN" "Failed to stop container gracefully, forcing removal" - ${USE_SUDO}docker kill "$existing_container" >> "$LOG_FILE" 2>&1 + "${DOCKER_SUDO[@]}" docker kill "$existing_container" >> "$LOG_FILE" 2>&1 fi fi # Try to remove the container - it might already be gone if started with --rm - if ${USE_SUDO}docker inspect "$existing_container" >/dev/null 2>&1; then + if "${DOCKER_SUDO[@]}" docker inspect "$existing_container" >/dev/null 2>&1; then # Container still exists, try to remove it local retry_count=0 local max_retries=5 while [[ $retry_count -lt $max_retries ]]; do local rm_output - rm_output=$(${USE_SUDO}docker rm "$existing_container" 2>&1) + rm_output=$("${DOCKER_SUDO[@]}" docker rm "$existing_container" 2>&1) local rm_exit_code=$? if [[ $rm_exit_code -eq 0 ]]; then @@ -570,7 +578,7 @@ stop_existing_container() { # Check if Docker image exists locally check_docker_image_exists() { local docker_image="$1" - ${USE_SUDO}docker image inspect "$docker_image" >/dev/null 2>&1 + "${DOCKER_SUDO[@]}" docker image inspect "$docker_image" >/dev/null 2>&1 } # Pull Docker image if needed @@ -588,7 +596,7 @@ pull_docker_image() { log "INFO" "Pulling Docker image (this may take several minutes on first run)..." # Show progress while pulling - if ! ${USE_SUDO}docker pull "$docker_image" >> "$LOG_FILE" 2>&1; then + if ! "${DOCKER_SUDO[@]}" docker pull "$docker_image" >> "$LOG_FILE" 2>&1; then log "ERROR" "Failed to pull Docker image: $docker_image" return 1 fi @@ -607,7 +615,7 @@ wait_for_container_running() { while [[ $attempt -le $max_attempts ]]; do # Check if container exists and get its status local container_status - container_status=$(${USE_SUDO}docker inspect --format='{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null) + container_status=$("${DOCKER_SUDO[@]}" docker inspect --format='{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null) case "$container_status" in "running") @@ -619,7 +627,7 @@ wait_for_container_running() { ;; "exited"|"dead") log "ERROR" "Container exited unexpectedly. Check container logs:" - ${USE_SUDO}docker logs --tail=20 "$CONTAINER_NAME" >> "$LOG_FILE" 2>&1 + "${DOCKER_SUDO[@]}" docker logs --tail=20 "$CONTAINER_NAME" >> "$LOG_FILE" 2>&1 return 1 ;; "") @@ -723,7 +731,7 @@ perform_health_check() { while [[ $attempt -le $max_attempts ]]; do # Check container status first local container_status - container_status=$(${USE_SUDO}docker inspect --format='{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null) + container_status=$("${DOCKER_SUDO[@]}" docker inspect --format='{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null) if [[ "$container_status" != "$last_container_status" ]]; then case "$container_status" in @@ -732,7 +740,7 @@ perform_health_check() { ;; "exited"|"dead") log "ERROR" "Container has stopped unexpectedly. Checking logs..." - ${USE_SUDO}docker logs --tail=20 "$CONTAINER_NAME" >> "$LOG_FILE" 2>&1 + "${DOCKER_SUDO[@]}" docker logs --tail=20 "$CONTAINER_NAME" >> "$LOG_FILE" 2>&1 return 1 ;; "") @@ -771,7 +779,7 @@ perform_health_check() { done log "ERROR" "Health check failed after $max_attempts attempts" - printf "${RED}❌ vLLM server failed to start or is not responding${NC}\n" + printf '%b❌ vLLM server failed to start or is not responding%b\n' "$RED" "$NC" printf "${YELLOW}The server may still be initializing. Check logs with: ${USE_SUDO}docker logs %s${NC}\n" "$CONTAINER_NAME" return 1 } diff --git a/docs/getting-started-deploy-MCP.md b/docs/getting-started-deploy-MCP.md index 65723a24..57f20f0a 100644 --- a/docs/getting-started-deploy-MCP.md +++ b/docs/getting-started-deploy-MCP.md @@ -275,12 +275,14 @@ source core/scripts/generate-token.sh **Prerequisites:** -Before running the script, update the following environment variables in `core/scripts/generate-token.sh` according to your cluster configuration: +Before running the script, export the following environment variables according to your +cluster configuration. `KEYCLOAK_PASSWORD` has no default and must always be provided; +the others fall back to the placeholder defaults shown below. ```bash export BASE_URL="api.example.com" # Base URL of Keycloak server (without https://) export KEYCLOAK_ADMIN_USERNAME="your-keycloak-admin-user" # Keycloak admin username -export KEYCLOAK_PASSWORD="changeme" # Keycloak admin password +export KEYCLOAK_PASSWORD="" # Keycloak admin password (required) export KEYCLOAK_CLIENT_ID="my-client-id" # Client ID to be created in Keycloak ``` diff --git a/sample_solutions/AgenticCodeExecution/examples/airline/mcp_airline_server.py b/sample_solutions/AgenticCodeExecution/examples/airline/mcp_airline_server.py index c1721296..7e8feffa 100644 --- a/sample_solutions/AgenticCodeExecution/examples/airline/mcp_airline_server.py +++ b/sample_solutions/AgenticCodeExecution/examples/airline/mcp_airline_server.py @@ -9,6 +9,7 @@ import json import os import sys +import urllib.parse import urllib.request from copy import deepcopy from pathlib import Path @@ -16,7 +17,7 @@ from fastmcp import FastMCP -# Add parent directory to sys.path for shared modules (error_hints) +# Add parent directory to sys.path for shared modules (error_hints, safe_math) sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from airline_data_model import ( @@ -35,6 +36,7 @@ User, ) from error_hints import analyze_execution_error +from safe_math import calculate_expression DEFAULT_DB_PATH = str(Path(__file__).resolve().parent / "data" / "db.json") @@ -45,6 +47,9 @@ ) +DOWNLOAD_TIMEOUT_SEC = 60 + + def ensure_db(db_path: str) -> None: """Check that the airline database exists; auto-download from tau2-bench if missing.""" p = Path(db_path) @@ -54,7 +59,11 @@ def ensure_db(db_path: str) -> None: print(f" Downloading from tau2-bench …") p.parent.mkdir(parents=True, exist_ok=True) try: - urllib.request.urlretrieve(TAU2_BENCH_URL, str(p)) + if urllib.parse.urlsplit(TAU2_BENCH_URL).scheme != "https": + raise ValueError("Only https:// downloads are permitted") + # nosec B310: the scheme is restricted to https immediately above. + with urllib.request.urlopen(TAU2_BENCH_URL, timeout=DOWNLOAD_TIMEOUT_SEC) as response: # nosec B310 + p.write_bytes(response.read()) print(f" ✅ Downloaded ({p.stat().st_size / 1_048_576:.1f} MB)") except Exception as exc: print(f" ❌ Download failed: {exc}") @@ -383,9 +392,7 @@ def calculate(expression: str, session_id: str = "") -> str: Raises: ValueError: If the expression is invalid. """ - if not all(char in "0123456789+-*/(). " for char in expression): - raise ValueError("Invalid characters in expression") - return str(round(float(eval(expression, {"__builtins__": None}, {})), 2)) + return calculate_expression(expression, 2) @mcp.tool() diff --git a/sample_solutions/AgenticCodeExecution/examples/banking/mcp_banking_server.py b/sample_solutions/AgenticCodeExecution/examples/banking/mcp_banking_server.py index 4b103fc3..7ec53e02 100644 --- a/sample_solutions/AgenticCodeExecution/examples/banking/mcp_banking_server.py +++ b/sample_solutions/AgenticCodeExecution/examples/banking/mcp_banking_server.py @@ -15,7 +15,7 @@ from fastmcp import FastMCP -# Add parent directory to sys.path for shared modules (error_hints) +# Add parent directory to sys.path for shared modules (error_hints, safe_math) sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from banking_data_model import ( @@ -30,6 +30,7 @@ CustomerName, ) from error_hints import analyze_execution_error +from safe_math import calculate_expression DEFAULT_DB_PATH = str(Path(__file__).resolve().parent / "data" / "db.json") @@ -337,9 +338,7 @@ def calculate(expression: str, session_id: str = "") -> str: Returns: The calculated result as a string. """ - if not all(char in "0123456789+-*/(). " for char in expression): - raise ValueError("Invalid characters in expression") - return str(round(float(eval(expression, {"__builtins__": None}, {})), 6)) + return calculate_expression(expression, 6) @mcp.tool() diff --git a/sample_solutions/AgenticCodeExecution/examples/retail/mcp_retail_server.py b/sample_solutions/AgenticCodeExecution/examples/retail/mcp_retail_server.py index 508a5e70..5f06db35 100644 --- a/sample_solutions/AgenticCodeExecution/examples/retail/mcp_retail_server.py +++ b/sample_solutions/AgenticCodeExecution/examples/retail/mcp_retail_server.py @@ -9,13 +9,14 @@ import json import os import sys +import urllib.parse import urllib.request from pathlib import Path from typing import Any, Dict, List, Optional from fastmcp import FastMCP -# Add parent directory to sys.path for shared modules (error_hints) +# Add parent directory to sys.path for shared modules (error_hints, safe_math) sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from retail_data_model import ( @@ -34,6 +35,7 @@ UserAddress, ) from error_hints import analyze_execution_error +from safe_math import calculate_expression # Default DB path (sibling data/ directory) @@ -45,6 +47,9 @@ ) +DOWNLOAD_TIMEOUT_SEC = 60 + + def ensure_db(db_path: str) -> None: """Check that the retail database exists; auto-download from tau2-bench if missing.""" p = Path(db_path) @@ -54,7 +59,11 @@ def ensure_db(db_path: str) -> None: print(f" Downloading from tau2-bench …") p.parent.mkdir(parents=True, exist_ok=True) try: - urllib.request.urlretrieve(TAU2_BENCH_URL, str(p)) + if urllib.parse.urlsplit(TAU2_BENCH_URL).scheme != "https": + raise ValueError("Only https:// downloads are permitted") + # nosec B310: the scheme is restricted to https immediately above. + with urllib.request.urlopen(TAU2_BENCH_URL, timeout=DOWNLOAD_TIMEOUT_SEC) as response: # nosec B310 + p.write_bytes(response.read()) print(f" ✅ Downloaded ({p.stat().st_size / 1_048_576:.1f} MB)") except Exception as exc: print(f" ❌ Download failed: {exc}") @@ -473,9 +482,7 @@ def calculate(expression: str, session_id: str = "") -> str: Returns: The calculated result as a string. """ - if not all(char in "0123456789+-*/(). " for char in expression): - raise ValueError("Invalid characters in expression") - return str(round(float(eval(expression, {"__builtins__": None}, {})), 2)) + return calculate_expression(expression, 2) @mcp.tool() diff --git a/sample_solutions/AgenticCodeExecution/examples/safe_math.py b/sample_solutions/AgenticCodeExecution/examples/safe_math.py new file mode 100644 index 00000000..ea2f3c49 --- /dev/null +++ b/sample_solutions/AgenticCodeExecution/examples/safe_math.py @@ -0,0 +1,59 @@ +"""Safe arithmetic evaluation shared by the domain MCP servers. + +The ``calculate`` tool exposed by each domain server accepts an expression string +supplied by the model. Evaluating that string with ``eval()`` would allow arbitrary +code execution, so expressions are parsed into an AST and only plain numeric +arithmetic nodes are interpreted. +""" + +import ast +import operator +from typing import Callable, Dict, Type, Union + +ALLOWED_CHARACTERS = "0123456789+-*/(). " + +_BINARY_OPERATORS: Dict[Type[ast.operator], Callable[[float, float], float]] = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, +} + + +def _evaluate_node(node: ast.AST) -> float: + """Recursively evaluate an arithmetic-only AST node.""" + if isinstance(node, ast.Expression): + return _evaluate_node(node.body) + if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)) and not isinstance(node.value, bool): + return float(node.value) + if isinstance(node, ast.UnaryOp) and isinstance(node.op, (ast.UAdd, ast.USub)): + value = _evaluate_node(node.operand) + return value if isinstance(node.op, ast.UAdd) else -value + if isinstance(node, ast.BinOp) and type(node.op) in _BINARY_OPERATORS: + return _BINARY_OPERATORS[type(node.op)](_evaluate_node(node.left), _evaluate_node(node.right)) + raise ValueError("Unsupported expression") + + +def calculate_expression(expression: str, ndigits: int) -> str: + """Evaluate a numeric expression and return the result rounded to ``ndigits``. + + Args: + expression: Expression built from numbers, ``+ - * /``, parentheses and spaces. + ndigits: Number of decimal places to round the result to. + + Returns: + The rounded result as a string. + + Raises: + ValueError: If the expression contains disallowed characters or constructs. + ZeroDivisionError: If the expression divides by zero. + """ + if not all(char in ALLOWED_CHARACTERS for char in expression): + raise ValueError("Invalid characters in expression") + try: + # Leading/trailing whitespace is stripped so that ast.parse does not report it + # as an indentation error. + parsed: Union[ast.Expression, ast.AST] = ast.parse(expression.strip(), mode="eval") + except SyntaxError as exc: + raise ValueError("Invalid expression") from exc + return str(round(_evaluate_node(parsed), ndigits)) diff --git a/sample_solutions/AgenticCodeExecution/examples/stocks/mcp_stocks_server.py b/sample_solutions/AgenticCodeExecution/examples/stocks/mcp_stocks_server.py index 410b93f5..86c48302 100644 --- a/sample_solutions/AgenticCodeExecution/examples/stocks/mcp_stocks_server.py +++ b/sample_solutions/AgenticCodeExecution/examples/stocks/mcp_stocks_server.py @@ -16,10 +16,11 @@ from fastmcp import FastMCP -# Add parent directory to sys.path for shared modules (error_hints) +# Add parent directory to sys.path for shared modules (error_hints, safe_math) sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from error_hints import analyze_execution_error +from safe_math import calculate_expression from stocks_data_model import StocksDB @@ -471,9 +472,7 @@ def calculate(expression: str, session_id: str = "") -> str: Returns: The calculated result as a string. """ - if not all(char in "0123456789+-*/(). " for char in expression): - raise ValueError("Invalid characters in expression") - return str(round(float(eval(expression, {"__builtins__": None}, {})), 6)) + return calculate_expression(expression, 6) @mcp.tool() diff --git a/sample_solutions/AgenticCodeExecution/examples/triage/mcp_triage_server.py b/sample_solutions/AgenticCodeExecution/examples/triage/mcp_triage_server.py index 974a93b3..7a1b78c2 100644 --- a/sample_solutions/AgenticCodeExecution/examples/triage/mcp_triage_server.py +++ b/sample_solutions/AgenticCodeExecution/examples/triage/mcp_triage_server.py @@ -20,10 +20,11 @@ from fastmcp import FastMCP -# Add parent directory to sys.path for shared modules (error_hints) +# Add parent directory to sys.path for shared modules (error_hints, safe_math) sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from error_hints import analyze_execution_error +from safe_math import calculate_expression mcp = FastMCP( @@ -39,6 +40,8 @@ ) +_PERMITTED_URL_SCHEMES = ("http", "https") + _STATUS_APIS = { "github": "https://www.githubstatus.com/api/v2/status.json", "openai": "https://status.openai.com/api/v2/status.json", @@ -51,15 +54,27 @@ def _now_iso() -> str: return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") +def _validate_url(url: str) -> str: + """Reject URL schemes other than http/https, such as file: or custom handlers. + + Raises: + ValueError: If the scheme is not permitted. + """ + if urllib.parse.urlsplit(url).scheme.lower() not in _PERMITTED_URL_SCHEMES: + raise ValueError("Only http:// and https:// URLs are supported") + return url + + def _http_get_json(url: str, timeout_sec: int = 8) -> Dict[str, Any]: request = urllib.request.Request( - url, + _validate_url(url), headers={ "User-Agent": "mcp-triage-server/1.0", "Accept": "application/json", }, ) - with urllib.request.urlopen(request, timeout=timeout_sec) as response: + # nosec B310: _validate_url restricts the scheme to http/https. + with urllib.request.urlopen(request, timeout=timeout_sec) as response: # nosec B310 body = response.read().decode("utf-8", errors="replace") return json.loads(body) @@ -113,7 +128,9 @@ def check_http_endpoint(url: str, timeout_sec: int = 8, session_id: str = "") -> } try: - with urllib.request.urlopen(request, timeout=timeout_sec) as response: + _validate_url(url) + # nosec B310: _validate_url restricts the scheme to http/https. + with urllib.request.urlopen(request, timeout=timeout_sec) as response: # nosec B310 elapsed_ms = round((time.perf_counter() - start) * 1000, 2) body = response.read(400).decode("utf-8", errors="replace") result.update( @@ -488,9 +505,7 @@ def draft_customer_update( def calculate(expression: str, session_id: str = "") -> str: """Calculate the result of a mathematical expression.""" _ = session_id - if not all(char in "0123456789+-*/(). " for char in expression): - raise ValueError("Invalid characters in expression") - return str(round(float(eval(expression, {"__builtins__": None}, {})), 6)) + return calculate_expression(expression, 6) @mcp.tool() diff --git a/sample_solutions/HybridSearch/scripts/verify_setup.sh b/sample_solutions/HybridSearch/scripts/verify_setup.sh index d6e76967..883dafb6 100755 --- a/sample_solutions/HybridSearch/scripts/verify_setup.sh +++ b/sample_solutions/HybridSearch/scripts/verify_setup.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 # Hybrid Search RAG - Setup Verification Script # This script verifies that the project structure is complete diff --git a/third_party/Dell/redhat9.6/iac/deploy-enterprise-inference.sh b/third_party/Dell/redhat9.6/iac/deploy-enterprise-inference.sh index c234b959..63028f2b 100644 --- a/third_party/Dell/redhat9.6/iac/deploy-enterprise-inference.sh +++ b/third_party/Dell/redhat9.6/iac/deploy-enterprise-inference.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 # --- RHEL Kubernetes Preflight Steps --- echo "[INFO] Running RHEL Kubernetes preflight configuration..." @@ -88,7 +90,7 @@ DEPLOYMENT_MODE="keycloak" DEPLOY_OBSERVABILITY="off" KEYCLOAK_CLIENT_ID="my-client-id" KEYCLOAK_ADMIN_USER="your-keycloak-admin-user" -KEYCLOAK_ADMIN_PASSWORD="changeme" +KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD:-changeme}" FIRMWARE_VERSION="1.22.1" STATE_FILE="/tmp/ei-deploy.state" BRANCH="release-1.4.0" @@ -241,7 +243,7 @@ check_hf_token_access() { update_inference_config() { if [[ -f "$CONFIG_FILE" ]]; then - local hf_token_escaped models_escaped gpu_type_escaped + local hf_token_escaped gpu_type_escaped local keycloak_client_id_escaped keycloak_admin_user_escaped keycloak_admin_password_escaped local deploy_keycloak_apisix_escaped deploy_genai_gateway_escaped deploy_observability_escaped @@ -452,6 +454,7 @@ log_info "Deployment user validated: $USERNAME" if [[ -f "$STATE_FILE" ]] || [[ "$RESUME" == true ]]; then if [[ -f "$STATE_FILE" ]]; then log_info "State file found. Resuming from checkpoint..." + # shellcheck source=/dev/null # state file written by this script at runtime source "$STATE_FILE" RESUME=true set_deployment_variables @@ -739,6 +742,7 @@ main() { log_info "CPU-only mode detected — disabling NRI and CPU balloons" # Normalize file: always end with newline + # shellcheck disable=SC1003 # '$a\' is the sed idiom for appending a trailing newline sed -i -e '$a\' "$CONFIG_FILE" # Update if keys exist @@ -785,9 +789,11 @@ main() { if [[ "$KERNEL" == 6.8.* ]]; then log_info "Kernel version 6.8 detected. Adding IOMMU configuration..." if ! grep -q "iommu=pt intel_iommu=on" /etc/default/grub; then - echo "" >> /etc/default/grub - echo "# Gaudi3 requires this option for kernel version 6.8" >> /etc/default/grub - echo 'GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt intel_iommu=on"' >> /etc/default/grub + { + echo "" + echo "# Gaudi3 requires this option for kernel version 6.8" + echo 'GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt intel_iommu=on"' + } >> /etc/default/grub log_warn "IOMMU configuration added. System restart required after deployment." else log_info "IOMMU configuration already present" diff --git a/third_party/Dell/ubuntu-22.04/iac/deploy-enterprise-inference.sh b/third_party/Dell/ubuntu-22.04/iac/deploy-enterprise-inference.sh index 8a366174..69a691bc 100644 --- a/third_party/Dell/ubuntu-22.04/iac/deploy-enterprise-inference.sh +++ b/third_party/Dell/ubuntu-22.04/iac/deploy-enterprise-inference.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 # # Combined Enterprise Inference Stack Deployment Script # This script combines genai-system-setup.sh, genai-owner-setup.sh, and post-os-setup.sh @@ -40,7 +42,7 @@ DEPLOYMENT_MODE="keycloak" DEPLOY_OBSERVABILITY="off" KEYCLOAK_CLIENT_ID="my-client-id" KEYCLOAK_ADMIN_USER="your-keycloak-admin-user" -KEYCLOAK_ADMIN_PASSWORD="changeme" +KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD:-changeme}" FIRMWARE_VERSION="1.22.1" STATE_FILE="/tmp/ei-deploy.state" BRANCH="release-1.4.0" @@ -168,7 +170,7 @@ check_hf_token_access() { update_inference_config() { if [[ -f "$CONFIG_FILE" ]]; then - local hf_token_escaped models_escaped gpu_type_escaped + local hf_token_escaped gpu_type_escaped local keycloak_client_id_escaped keycloak_admin_user_escaped keycloak_admin_password_escaped local deploy_keycloak_apisix_escaped deploy_genai_gateway_escaped deploy_observability_escaped @@ -379,6 +381,7 @@ log_info "Deployment user validated: $USERNAME" if [[ -f "$STATE_FILE" ]] || [[ "$RESUME" == true ]]; then if [[ -f "$STATE_FILE" ]]; then log_info "State file found. Resuming from checkpoint..." + # shellcheck source=/dev/null # state file written by this script at runtime source "$STATE_FILE" RESUME=true set_deployment_variables @@ -577,6 +580,7 @@ main() { log_info "CPU-only mode detected — disabling NRI and CPU balloons" # Normalize file: always end with newline + # shellcheck disable=SC1003 # '$a\' is the sed idiom for appending a trailing newline sed -i -e '$a\' "$CONFIG_FILE" # Update if keys exist @@ -623,9 +627,11 @@ main() { if [[ "$KERNEL" == 6.8.* ]]; then log_info "Kernel version 6.8 detected. Adding IOMMU configuration..." if ! grep -q "iommu=pt intel_iommu=on" /etc/default/grub; then - echo "" >> /etc/default/grub - echo "# Gaudi3 requires this option for kernel version 6.8" >> /etc/default/grub - echo 'GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt intel_iommu=on"' >> /etc/default/grub + { + echo "" + echo "# Gaudi3 requires this option for kernel version 6.8" + echo 'GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt intel_iommu=on"' + } >> /etc/default/grub log_warn "IOMMU configuration added. System restart required after deployment." else log_info "IOMMU configuration already present" diff --git a/third_party/Dell/ubuntu-22.04/iac/iso/custom-iso.sh b/third_party/Dell/ubuntu-22.04/iac/iso/custom-iso.sh index 4b148f19..596c5ed0 100644 --- a/third_party/Dell/ubuntu-22.04/iac/iso/custom-iso.sh +++ b/third_party/Dell/ubuntu-22.04/iac/iso/custom-iso.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 set -euo pipefail ISO_URL="https://releases.ubuntu.com/jammy/ubuntu-22.04.5-live-server-amd64.iso" @@ -9,7 +11,7 @@ VOLID="Ubuntu-Server-22.04.5-AI" HOSTNAME="" USERNAME="" -PASSWORD_HASH='$6$Sl0xydNgA3rBk1Uo$Pj7oVVI7smkdBh20V8EyLivWpKDHFueUhvrfwxundGp/DQrAuTHjIxnrCZIMVJ1zcTIJ7VgIWKu0mUZmiRsqv0' +PASSWORD_HASH="" SSH_PUBLIC_KEY="" TIMEZONE="America/Chicago" LOCALE="en_US.UTF-8" diff --git a/third_party/Dell/ubuntu-22.04/iac/mount-iso.sh b/third_party/Dell/ubuntu-22.04/iac/mount-iso.sh index 574ff267..f5ecf29d 100644 --- a/third_party/Dell/ubuntu-22.04/iac/mount-iso.sh +++ b/third_party/Dell/ubuntu-22.04/iac/mount-iso.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 # Script to mount/unmount Ubuntu ISO via iDRAC Redfish API # Usage: # ./mount-iso.sh - Mount the ISO diff --git a/third_party/Dell/ubuntu-22.04/iac/verify-installation.sh b/third_party/Dell/ubuntu-22.04/iac/verify-installation.sh index d7551194..f6481f95 100644 --- a/third_party/Dell/ubuntu-22.04/iac/verify-installation.sh +++ b/third_party/Dell/ubuntu-22.04/iac/verify-installation.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 # Script to verify Ubuntu installation completion # # Environment variables: @@ -29,7 +31,8 @@ if [ -z "$IDRAC_PASS" ]; then exit 1 fi -EXPECTED_HOSTNAME="ubuntu-server" # From terraform.tfvars +# shellcheck disable=SC2034 # reference value from terraform.tfvars, reported in the summary below +EXPECTED_HOSTNAME="ubuntu-server" echo "==========================================" echo "Ubuntu Installation Verification" diff --git a/third_party/IBM/patterns/quickstart/run_script.sh b/third_party/IBM/patterns/quickstart/run_script.sh index cce5dd92..48756bc6 100644 --- a/third_party/IBM/patterns/quickstart/run_script.sh +++ b/third_party/IBM/patterns/quickstart/run_script.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 expand_path() { local path="$1" @@ -27,6 +29,7 @@ else shift # Remove 'multi-node' from arguments reserved_ip=$1 # First control plane node IP cluster_url=$2 + # shellcheck disable=SC2034 # retained to document the positional argument order models=$3 cert_path=$(expand_path "$4") key_path=$(expand_path "$5") @@ -70,7 +73,7 @@ fi # Model deploy code if [[ "$1" == "model-deploy" ]]; then echo "[$(date)] Phase 2: Deploying models with PVC support" - cd /home/ubuntu/Enterprise-Inference/core + cd /home/ubuntu/Enterprise-Inference/core || exit 1 echo -e '3\n2\n1\nyes\ny\n' | bash inference-stack-deploy.sh --models "$2" kubectl delete pods -l app.kubernetes.io/component=device-plugin,app.kubernetes.io/name=habana-ai -n habana-ai-operator --ignore-not-found=true @@ -108,7 +111,9 @@ sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock || true sudo dpkg --configure -a || true # Configure needrestart to automatically restart services without prompting +# shellcheck disable=SC2016 # $nrconf is needrestart config syntax, not a shell variable echo '$nrconf{restart} = "a";' | sudo tee /etc/needrestart/conf.d/50local.conf +# shellcheck disable=SC2016 # $nrconf is needrestart config syntax, not a shell variable echo '$nrconf{kernelhints} = 0;' | sudo tee -a /etc/needrestart/conf.d/50local.conf echo "[$(date)] Updating package lists and installing dependencies..." @@ -153,10 +158,10 @@ else chmod 600 "$key_path" fi -cd ~ +cd ~ || exit 1 rm -rf /home/ubuntu/Enterprise-Inference git clone -b release-1.3.1 --single-branch https://github.com/opea-project/Enterprise-Inference.git /home/ubuntu/Enterprise-Inference -cd /home/ubuntu/Enterprise-Inference +cd /home/ubuntu/Enterprise-Inference || exit 1 # Copy appropriate hosts.yaml based on deployment mode if [[ "$deployment_mode" == "single-node" ]]; then @@ -169,7 +174,7 @@ cp -f /home/ubuntu/inference-config.cfg core/inventory/inference-config.cfg echo "[$(date)] Vault secrets generated successfully" chmod +x core/inference-stack-deploy.sh -cd core +cd core || exit 1 # Deploys infrastructure only (no models) echo "[$(date)] Phase 1: Deploying entire infrastructure stack without models" diff --git a/third_party/IBM/patterns/standard/run_script.sh b/third_party/IBM/patterns/standard/run_script.sh index cc8d87d6..2bd004db 100644 --- a/third_party/IBM/patterns/standard/run_script.sh +++ b/third_party/IBM/patterns/standard/run_script.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Copyright (C) 2025-2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 expand_path() { local path="$1" @@ -27,6 +29,7 @@ else shift # Remove 'multi-node' from arguments reserved_ip=$1 # First control plane node IP cluster_url=$2 + # shellcheck disable=SC2034 # retained to document the positional argument order models=$3 cert_path=$(expand_path "$4") key_path=$(expand_path "$5") @@ -70,7 +73,7 @@ fi # Model deploy code if [[ "$1" == "model-deploy" ]]; then echo "[$(date)] Phase 2: Deploying models with PVC support" - cd /home/ubuntu/Enterprise-Inference/core + cd /home/ubuntu/Enterprise-Inference/core || exit 1 echo -e '3\n2\n1\nyes\ny\n' | bash inference-stack-deploy.sh --models "$2" kubectl delete pods -l app.kubernetes.io/component=device-plugin,app.kubernetes.io/name=habana-ai -n habana-ai-operator --ignore-not-found=true @@ -108,7 +111,9 @@ sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock || true sudo dpkg --configure -a || true # Configure needrestart to automatically restart services without prompting +# shellcheck disable=SC2016 # $nrconf is needrestart config syntax, not a shell variable echo '$nrconf{restart} = "a";' | sudo tee /etc/needrestart/conf.d/50local.conf +# shellcheck disable=SC2016 # $nrconf is needrestart config syntax, not a shell variable echo '$nrconf{kernelhints} = 0;' | sudo tee -a /etc/needrestart/conf.d/50local.conf echo "[$(date)] Updating package lists and installing dependencies..." @@ -153,10 +158,10 @@ else chmod 600 "$key_path" fi -cd ~ +cd ~ || exit 1 rm -rf /home/ubuntu/Enterprise-Inference git clone -b release-1.3.1 --single-branch https://github.com/opea-project/Enterprise-Inference.git /home/ubuntu/Enterprise-Inference -cd /home/ubuntu/Enterprise-Inference +cd /home/ubuntu/Enterprise-Inference || exit 1 # Copy appropriate hosts.yaml based on deployment mode if [[ "$deployment_mode" == "single-node" ]]; then @@ -168,7 +173,7 @@ fi cp -f /home/ubuntu/inference-config.cfg core/inventory/inference-config.cfg echo "[$(date)] Vault secrets generated successfully" chmod +x core/inference-stack-deploy.sh -cd core +cd core || exit 1 # Deploys infrastructure only (no models) echo "[$(date)] Phase 1: Deploying entire infrastructure stack without models"