22
33# MaaS Platform Deployment Validation Script
44# This script validates that the MaaS platform is correctly deployed and functional
5+ #
6+ # Usage: ./validate-deployment.sh [MODEL_NAME]
7+ # MODEL_NAME: Optional. If provided, the script will validate using this specific model
58
69# Note: We don't use 'set -e' because we want to continue validation even if some checks fail
710
11+ # Parse command line arguments
12+ REQUESTED_MODEL=" "
13+ CUSTOM_REQUEST_PAYLOAD=" "
14+ INFERENCE_ENDPOINT=" chat/completions" # Default to chat completions
15+
16+ # Show help if requested
17+ if [ " $1 " = " --help" ] || [ " $1 " = " -h" ]; then
18+ echo " MaaS Platform Deployment Validation Script"
19+ echo " "
20+ echo " Usage: $0 [OPTIONS] [MODEL_NAME]"
21+ echo " "
22+ echo " This script validates that the MaaS platform is correctly deployed and functional."
23+ echo " It performs checks on components, gateway status, policies, and API endpoints."
24+ echo " "
25+ echo " Arguments:"
26+ echo " MODEL_NAME Optional. Name of a specific model to use for validation."
27+ echo " If not provided, the first available model will be used."
28+ echo " "
29+ echo " Options:"
30+ echo " -h, --help Show this help message and exit"
31+ echo " --request-payload JSON Custom JSON request payload for model inference tests."
32+ echo " Use \$ {MODEL_NAME} as a placeholder for the model name."
33+ echo " Default (Chat): '{\" model\" : \"\$ {MODEL_NAME}\" , \" messages\" : [{\" role\" : \" user\" , \" content\" : \" Hello\" }], \" max_tokens\" : 5}'"
34+ echo " --endpoint ENDPOINT API endpoint to use: 'chat/completions' or 'completions'"
35+ echo " Default: 'chat/completions'"
36+ echo " "
37+ echo " Examples:"
38+ echo " # Basic validation"
39+ echo " $0 # Validate using first available model (default chat format)"
40+ echo " $0 llm-simulator # Validate using llm-simulator model"
41+ echo " "
42+ echo " # For base models like granite (use completions endpoint with 'prompt')"
43+ echo " $0 granite-8b-base --endpoint completions --request-payload '{\" model\" : \"\$ {MODEL_NAME}\" , \" messages\" : \" Hello, how are you?\" , \" max_tokens\" : 50}'"
44+ echo " "
45+ echo " # For instruction/chat models (default format works)"
46+ echo " $0 qwen3-instruct"
47+ echo " "
48+ echo " Exit Codes:"
49+ echo " 0 All critical checks passed"
50+ echo " 1 Some checks failed"
51+ echo " "
52+ exit 0
53+ fi
54+
55+ # Parse arguments
56+ while [ $# -gt 0 ]; do
57+ case " $1 " in
58+ --request-payload)
59+ CUSTOM_REQUEST_PAYLOAD=" $2 "
60+ shift 2
61+ ;;
62+ --endpoint)
63+ INFERENCE_ENDPOINT=" $2 "
64+ shift 2
65+ ;;
66+ -* )
67+ echo " Unknown option: $1 "
68+ echo " Use --help for usage information"
69+ exit 1
70+ ;;
71+ * )
72+ if [ -z " $REQUESTED_MODEL " ]; then
73+ REQUESTED_MODEL=" $1 "
74+ else
75+ echo " Error: Multiple model names provided"
76+ echo " Use --help for usage information"
77+ exit 1
78+ fi
79+ shift
80+ ;;
81+ esac
82+ done
83+
84+ # Set default request payload if not provided (OpenAI Chat Completions format)
85+ if [ -z " $CUSTOM_REQUEST_PAYLOAD " ]; then
86+ DEFAULT_REQUEST_PAYLOAD=' {"model": "${MODEL_NAME}", "messages": [{"role": "user", "content": "Hello"}], "max_tokens": 5}'
87+ else
88+ DEFAULT_REQUEST_PAYLOAD=" $CUSTOM_REQUEST_PAYLOAD "
89+ fi
90+
91+ if [ -n " $REQUESTED_MODEL " ]; then
92+ echo " Requested model for validation: $REQUESTED_MODEL "
93+ fi
94+
95+ if [ -n " $CUSTOM_REQUEST_PAYLOAD " ]; then
96+ echo " Using custom request payload: $CUSTOM_REQUEST_PAYLOAD "
97+ fi
98+
99+ if [ " $INFERENCE_ENDPOINT " != " chat/completions" ]; then
100+ echo " Using custom endpoint: /v1/$INFERENCE_ENDPOINT "
101+ fi
102+
8103# Color codes for output
9104RED=' \033[0;31m'
10105GREEN=' \033[0;32m'
72167
73168print_header " 🚀 MaaS Platform Deployment Validation"
74169
170+ if [ -n " $REQUESTED_MODEL " ]; then
171+ print_info " Validation will use model: $REQUESTED_MODEL "
172+ fi
173+
75174# ==========================================
76175# 1. Component Status Checks
77176# ==========================================
302401 -H " Content-Type: application/json" \
303402 -H " Authorization: Bearer ${TOKEN} " \
304403 " ${ENDPOINT} " 2> /dev/null || echo " " )
305- echo " $MODELS_RESPONSE "
306404 HTTP_CODE=$( echo " $MODELS_RESPONSE " | tail -n1)
307405 RESPONSE_BODY=$( echo " $MODELS_RESPONSE " | sed ' $d' )
308406
@@ -316,14 +414,44 @@ else
316414 elif [ " $HTTP_CODE " = " 200" ]; then
317415 MODEL_COUNT=$( echo " $RESPONSE_BODY " | jq -r ' .data | length' 2> /dev/null || echo " 0" )
318416 if [ " $MODEL_COUNT " -gt 0 ]; then
319- MODEL_NAME=$( echo " $RESPONSE_BODY " | jq -r ' .data[0].id' 2> /dev/null || echo " " )
320- MODEL_CHAT=$( echo " $RESPONSE_BODY " | jq -r ' .data[0].url' 2> /dev/null || echo " " )
321- if [ -n " $MODEL_CHAT " ]; then
322- MODEL_CHAT_ENDPOINT=" ${MODEL_CHAT} /v1/chat/completions"
417+ print_success " Models endpoint accessible, found $MODEL_COUNT model(s)"
418+
419+ # Print list of available models
420+ print_info " Available models:"
421+ echo " $RESPONSE_BODY " | jq -r ' .data[] | " • \(.id) - \(.url)"' 2> /dev/null || echo " Could not parse model list"
422+ echo " "
423+
424+ # Check if a specific model was requested
425+ if [ -n " $REQUESTED_MODEL " ]; then
426+ # Look for the requested model in the response
427+ MODEL_INDEX=$( echo " $RESPONSE_BODY " | jq -r " .data | map(.id) | index(\" $REQUESTED_MODEL \" )" 2> /dev/null || echo " null" )
428+
429+ if [ " $MODEL_INDEX " != " null" ] && [ -n " $MODEL_INDEX " ]; then
430+ MODEL_NAME=$( echo " $RESPONSE_BODY " | jq -r " .data[$MODEL_INDEX ].id" 2> /dev/null || echo " " )
431+ MODEL_CHAT=$( echo " $RESPONSE_BODY " | jq -r " .data[$MODEL_INDEX ].url" 2> /dev/null || echo " " )
432+ print_info " Using requested model: $MODEL_NAME for validation"
433+ else
434+ # Requested model not found
435+ print_fail " Requested model '$REQUESTED_MODEL ' not found" " See available models above" " Use one of the available models or deploy the requested model"
436+ MODEL_NAME=" "
437+ MODEL_CHAT=" "
438+ MODEL_CHAT_ENDPOINT=" "
439+ fi
323440 else
324- print_warning " Model chat endpoint not found" " Model chat endpoint not found for $MODEL_NAME " " Check model HTTPRoute configuration: kubectl get httproute -n llm"
441+ # No specific model requested, use the first one (default behavior)
442+ MODEL_NAME=$( echo " $RESPONSE_BODY " | jq -r ' .data[0].id' 2> /dev/null || echo " " )
443+ MODEL_CHAT=$( echo " $RESPONSE_BODY " | jq -r ' .data[0].url' 2> /dev/null || echo " " )
444+ print_info " Using first available model: $MODEL_NAME for validation"
445+ fi
446+
447+ # Set the inference endpoint if we have a valid model
448+ if [ -n " $MODEL_CHAT " ] && [ " $MODEL_CHAT " != " null" ]; then
449+ MODEL_CHAT_ENDPOINT=" ${MODEL_CHAT} /v1/${INFERENCE_ENDPOINT} "
450+ elif [ -n " $MODEL_NAME " ]; then
451+ print_warning " Model endpoint not found" " Model endpoint not found for $MODEL_NAME " " Check model HTTPRoute configuration: kubectl get httproute -n llm"
452+ MODEL_NAME=" "
453+ MODEL_CHAT_ENDPOINT=" "
325454 fi
326- print_success " Models endpoint accessible, found $MODEL_COUNT model(s) using $MODEL_NAME for validation"
327455 else
328456 print_warning " Models endpoint accessible but no models found" " You may need to deploy a model a simulated model can be deployed with the following command:" " kustomize build docs/samples/models/simulator | kubectl apply --server-side=true --force-conflicts -f -"
329457 MODEL_NAME=" "
@@ -355,12 +483,16 @@ else
355483 # Test model inference endpoint (if model exists)
356484 if [ -n " $TOKEN " ] && [ -n " $MODEL_NAME " ] && [ -n " $MODEL_CHAT_ENDPOINT " ]; then
357485 print_check " Model inference endpoint"
358- print_info " Testing: curl -sSk -X POST ${MODEL_CHAT_ENDPOINT} -H 'Authorization: Bearer \$ TOKEN' -H 'Content-Type: application/json' -d '{\" model\" : \" ${MODEL_NAME} \" , \" prompt\" : \" Hello\" , \" max_tokens\" : 5}'"
486+
487+ # Substitute MODEL_NAME placeholder in the request payload
488+ REQUEST_PAYLOAD=" ${DEFAULT_REQUEST_PAYLOAD// \$\{ MODEL_NAME\} / $MODEL_NAME } "
489+
490+ print_info " Testing: curl -sSk -X POST ${MODEL_CHAT_ENDPOINT} -H 'Authorization: Bearer \$ TOKEN' -H 'Content-Type: application/json' -d '${REQUEST_PAYLOAD} '"
359491
360492 INFERENCE_RESPONSE=$( curl -sSk --connect-timeout 10 --max-time 30 -w " \n%{http_code}" \
361493 -H " Authorization: Bearer ${TOKEN} " \
362494 -H " Content-Type: application/json" \
363- -d " { \" model \" : \" ${MODEL_NAME} \" , \" prompt \" : \" Hello \" , \" max_tokens \" : 5 }" \
495+ -d " ${REQUEST_PAYLOAD }" \
364496 " ${MODEL_CHAT_ENDPOINT} " 2> /dev/null || echo " " )
365497
366498 HTTP_CODE=$( echo " $INFERENCE_RESPONSE " | tail -n1)
373505 " Check Gateway and model HTTPRoute: kubectl get httproute -n llm"
374506 elif [ " $HTTP_CODE " = " 200" ]; then
375507 print_success " Model inference endpoint working"
508+ print_info " Response: $( echo $RESPONSE_BODY | head -c 200) "
376509 elif [ " $HTTP_CODE " = " 404" ]; then
377510 print_fail " Model inference endpoint not found (HTTP 404)" \
378511 " Path is incorrect - traffic reaching but wrong path" \
@@ -381,8 +514,12 @@ else
381514 print_fail " Gateway/Service error (HTTP $HTTP_CODE )" \
382515 " Gateway cannot reach model service" \
383516 " Check: 1) Model pods running: kubectl get pods -n llm, 2) Model service exists, 3) HTTPRoute configured: kubectl get httproute -n llm"
517+ elif [ " $HTTP_CODE " = " 401" ]; then
518+ print_fail " Authorization failed (HTTP 401)" " Response: $( echo $RESPONSE_BODY | head -c 200) " " Check AuthPolicy and TokenRateLimitPolicy"
519+ elif [ " $HTTP_CODE " = " 429" ]; then
520+ print_warning " Rate limiting (HTTP 429)" " Response: $( echo $RESPONSE_BODY | head -c 200) " " wait a minute and try again"
384521 else
385- print_fail " Model inference failed (HTTP $HTTP_CODE )" " Response: $( echo $RESPONSE_BODY | head -c 200) " " Check model pod logs and HTTPRoute configuration"
522+ print_fail " Model inference failed (HTTP $HTTP_CODE )" " Response: $( echo $RESPONSE_BODY | head -c 200) " " Check model pod logs and HTTPRoute configuration, this model may also have a different response format "
386523 fi
387524 fi
388525
@@ -391,14 +528,17 @@ else
391528 print_check " Rate limiting"
392529 print_info " Sending 10 rapid requests to test rate limiting..."
393530
531+ # Use the same request payload for rate limiting tests
532+ REQUEST_PAYLOAD=" ${DEFAULT_REQUEST_PAYLOAD// \$\{ MODEL_NAME\} / $MODEL_NAME } "
533+
394534 SUCCESS_COUNT=0
395535 RATE_LIMITED_COUNT=0
396536
397537 for i in {1..10}; do
398538 HTTP_CODE=$( curl -sSk --connect-timeout 5 --max-time 15 -o /dev/null -w " %{http_code}" \
399539 -H " Authorization: Bearer ${TOKEN} " \
400540 -H " Content-Type: application/json" \
401- -d " { \" model \" : \" ${MODEL_NAME} \" , \" prompt \" : \" Test \" , \" max_tokens \" : 1 }" \
541+ -d " ${REQUEST_PAYLOAD }" \
402542 " ${MODEL_CHAT_ENDPOINT} " 2> /dev/null || echo " 000" )
403543
404544 if [ " $HTTP_CODE " = " 200" ]; then
@@ -420,9 +560,12 @@ else
420560 # Test unauthorized access
421561 print_check " Authorization enforcement (401 without token)"
422562 if [ -n " $MODEL_NAME " ] && [ -n " $MODEL_CHAT_ENDPOINT " ]; then
563+ # Use the same request payload for unauthorized test
564+ REQUEST_PAYLOAD=" ${DEFAULT_REQUEST_PAYLOAD// \$\{ MODEL_NAME\} / $MODEL_NAME } "
565+
423566 UNAUTH_CODE=$( curl -sSk --connect-timeout 5 --max-time 15 -o /dev/null -w " %{http_code}" \
424567 -H " Content-Type: application/json" \
425- -d " { \" model \" : \" ${MODEL_NAME} \" , \" prompt \" : \" Test \" , \" max_tokens \" : 1 }" \
568+ -d " ${REQUEST_PAYLOAD }" \
426569 " ${MODEL_CHAT_ENDPOINT} " 2> /dev/null || echo " 000" )
427570
428571 if [ " $UNAUTH_CODE " = " 401" ]; then
@@ -453,6 +596,7 @@ if [ "$FAILED" -eq 0 ]; then
453596 echo " 1. Deploy a model: kustomize build docs/samples/models/simulator | kubectl apply -f -"
454597 echo " 2. Access the API at: ${HOST:- https:// maas.\$ {CLUSTER_DOMAIN} }"
455598 echo " 3. Check documentation: docs/README.md"
599+ echo " 4. Re-run validation with specific model: ./deployment/scripts/validate-deployment.sh MODEL_NAME"
456600 exit 0
457601else
458602 print_fail " Some checks failed. Please review the errors above."
462606 echo " - Check operator logs: kubectl logs -n kuadrant-system -l app.kubernetes.io/name=kuadrant-operator"
463607 echo " - Re-run deployment: ./deployment/scripts/deploy-openshift.sh"
464608 echo " "
609+ echo " Usage: ./deployment/scripts/validate-deployment.sh [MODEL_NAME]"
610+ echo " MODEL_NAME: Optional. Specify a model to validate against"
611+ echo " "
465612 exit 1
466613fi
467614
0 commit comments