diff --git a/README-EKS.md b/README-EKS.md index 5fcec639..d4a563fc 100644 --- a/README-EKS.md +++ b/README-EKS.md @@ -88,10 +88,10 @@ Example node name: `ip-10-0-120-104.us-west-2.compute.internal` or `i-02a3f32795 #### 3b. Execute APerf Collection -Use the provided `eks-aperf.sh` script to run APerf on the selected node: +Use the provided `kubectl-aperf` script to run APerf on the selected node: ```bash -bash ./eks-aperf.sh \ +./kubectl-aperf \ --aperf_image="${APERF_ECRREPO}:latest" \ --node="ip-10-0-120-104.us-west-2.compute.internal" ``` @@ -111,7 +111,7 @@ bash ./eks-aperf.sh \ ```bash # Run APerf for 60 seconds with profiling enabled -bash ./eks-aperf.sh \ +./kubectl-aperf \ --aperf_image="${APERF_ECRREPO}:latest" \ --node="ip-10-0-120-104.us-west-2.compute.internal" \ --aperf_options="-p 60 --profile" \ @@ -122,7 +122,7 @@ bash ./eks-aperf.sh \ ```bash # Run APerf with custom CPU and memory settings -bash ./eks-aperf.sh \ +./kubectl-aperf \ --aperf_image="${APERF_ECRREPO}:latest" \ --node="ip-10-0-120-104.us-west-2.compute.internal" \ --cpu-request="2.0" \ @@ -133,7 +133,7 @@ bash ./eks-aperf.sh \ #### 3c. Collect Results -The `eks-aperf.sh` script will automatically run the following steps: +The `kubectl-aperf` script will automatically run the following steps: 1. **Pod Deployment**: Deploy a privileged pod on the specified node 2. **APerf Record**: Runs APerf record inside the pod with the specified options @@ -145,7 +145,7 @@ The APerf report will be downloaded as a compressed tarball file with a timestam Example of correct output execution of the script: ```bash -$ bash ./eks-aperf.sh --aperf_image="${APERF_ECRREPO}:latest" --namespace=aperf --node ip-10-0-120-104.us-west-2.compute.internal --aperf_options="-p 30 --profile" +$ ./kubectl-aperf --aperf_image="${APERF_ECRREPO}:latest" --namespace=aperf --node ip-10-0-120-104.us-west-2.compute.internal --aperf_options="-p 30 --profile" Tageted node instance type... m6g.8xlarge Check namespace security policy... Namespace 'aperf' has 'privileged' policy - privileged pods allowed. @@ -198,6 +198,24 @@ Done! - The pod is automatically cleaned up after execution +## Installing as a kubectl Plugin + +You can install `kubectl-aperf` as a kubectl plugin to run it as `kubectl aperf` instead of just `./kubectl-aperf`. + +To do so, run the following commands: + +```bash +sudo mv kubectl-aperf /usr/local/bin/ +kubectl plugin list +kubectl aperf --help +``` + +Now you can run it as: + +```bash +kubectl aperf --aperf_image="${APERF_ECRREPO}:latest" --node="ip-10-0-120-104.us-west-2.compute.internal" +``` + ## Known Limitations **Note**: The `--profile-java` option is not currently fully supported with this script. diff --git a/eks-aperf.sh b/eks-aperf.sh deleted file mode 100755 index 92ae297e..00000000 --- a/eks-aperf.sh +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -o pipefail - -# Script to deploy a pod on a specific node, run aperf, and copy files locally -# Usage: ./aperf_k8s.sh --node="node-name" [--namespace="namespace"] [--aperf_options="options"] [--help] -# or: ./aperf_k8s.sh --node "node-name" [--namespace "namespace"] [--aperf_options "options"] [--help] - -# Default values -NAMESPACE="default" -APERF_OPTIONS="" -NODE_NAME="" -APERF_IMAGE="" -SHOW_HELP=false -CPU_REQUEST="1.0" -MEMORY_REQUEST="1Gi" -CPU_LIMIT="4.0" -MEMORY_LIMIT="4Gi" - -# Define color and formatting codes -BOLD="\033[1m" -GREEN="\033[0;32m" -YELLOW="\033[0;33m" -BLUE="\033[0;34m" -RED="\033[0;31m" -NC="\033[0m" # No Color - -# Parse command line arguments -while [ $# -gt 0 ]; do - dest="" - case "${1%=*}" in - --node) dest="NODE_NAME";; - --namespace) dest="NAMESPACE";; - --aperf_options) dest="APERF_OPTIONS";; - --aperf_image) dest="APERF_IMAGE";; - --cpu-request) dest="CPU_REQUEST";; - --memory-request) dest="MEMORY_REQUEST";; - --cpu-limit) dest="CPU_LIMIT";; - --memory-limit) dest="MEMORY_LIMIT";; - --help) - SHOW_HELP=true - shift - continue - ;; - *) - echo "Unknown parameter: $1" - exit 1 - ;; - esac - - if [[ "$1" = *=* ]]; then - eval ${dest}='"${1#*=}"' - else - eval ${dest}='"$2"' - shift - fi - shift -done - -# Show help if requested -if [ "$SHOW_HELP" = true ]; then - echo "Usage: ./aperf_k8s.sh --aperf_image=IMAGE --node=NODE_NAME [--namespace=NAMESPACE] [--aperf_options=OPTIONS] [--help]" - echo " or: ./aperf_k8s.sh --aperf_image IMAGE --node NODE_NAME [--namespace NAMESPACE] [--aperf_options OPTIONS] [--help]" - echo "" - echo "Parameters:" - echo " --aperf_image Required. ECR image location" - echo " --node Required. The name of the Kubernetes node to run aperf on" - echo " --namespace Optional. The Kubernetes namespace (default: '${NAMESPACE}')" - echo " --aperf_options Optional. Options to pass to aperf (default: '${APERF_OPTIONS}')" - echo " --cpu-request Optional. CPU request (default: '${CPU_REQUEST}')" - echo " --memory-request Optional. Memory request (default: '${MEMORY_REQUEST}')" - echo " --cpu-limit Optional. CPU limit (default: '${CPU_LIMIT}')" - echo " --memory-limit Optional. Memory limit (default: '${MEMORY_LIMIT}')" - echo " --help Show this help message" - exit 0 -fi - -# Check if aperf image is provided -if [ -z "$APERF_IMAGE" ]; then - echo "Error: APerf image is required. Use --aperf_image=IMAGE_URL or --aperf_image IMAGE_URL to specify the ECR image location." - echo "Use --help for more information." - exit 1 -fi - -# Check if node name is provided -if [ -z "$NODE_NAME" ]; then - echo "Error: Node name is required. Use --node=NODE_NAME or --node NODE_NAME to specify a node." - echo "Use --help for more information." - exit 1 -fi - - -POD_NAME="aperf-pod-${NODE_NAME//[.]/-}" - -# Create pod YAML as a variable -POD_YAML=$(cat << EOF -apiVersion: v1 -kind: Pod -metadata: - name: ${POD_NAME} - labels: - app: aperf -spec: - nodeSelector: - kubernetes.io/hostname: "${NODE_NAME}" - containers: - - name: aperf-runner - image: ${APERF_IMAGE} - securityContext: - privileged: true - command: ["/bin/sh", "-c"] - args: - - | - set -e - - echo -e "\nStarting Aperf recording execution..." - echo "Run: /usr/bin/aperf record -r aperf_record ${APERF_OPTIONS}" - sudo /usr/bin/aperf record -r aperf_record ${APERF_OPTIONS} - echo "APerf record completed" - - echo -e "\nStarting Aperf report generation..." - echo "Run: /usr/bin/aperf report -r aperf_record -n aperf_report" - sudo /usr/bin/aperf report -r aperf_record -n aperf_report - echo "APerf report generation completed" - - echo -e "\nWaiting for files to be copied..." - sleep 7200 - - resources: - requests: - memory: "${MEMORY_REQUEST}" - cpu: "${CPU_REQUEST}" - limits: - memory: "${MEMORY_LIMIT}" - cpu: "${CPU_LIMIT}" - volumeMounts: - - mountPath: /boot - name: boot-volume - readOnly: true - - name: opt - mountPath: /shared - volumes: - - name: boot-volume - hostPath: - path: /boot - type: Directory - - name: opt - hostPath: - path: /opt - type: DirectoryOrCreate - hostPID: true - hostNetwork: true - restartPolicy: Never -EOF -) - - -# Get node instance type information -echo -e -n "${BOLD}Tageted node instance type... ${NC} " -kubectl get node ${NODE_NAME} -o jsonpath='{.metadata.labels.beta\.kubernetes\.io/instance-type}' - -# Check if namespace has security restriction. If yes, exit. -ENFORCE=$(kubectl get namespace $NAMESPACE -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce}' 2>/dev/null) -echo -e -n "\n${BOLD}Check namespace security policy... ${NC} " -if [ "$ENFORCE" = "baseline" ] || [ "$ENFORCE" = "restricted" ]; then - echo "Namespace '$NAMESPACE' has '$ENFORCE' policy - privileged pods NOT allowed. Exit" - exit 1 -elif [ -z "$ENFORCE" ]; then - echo "Namespace '$NAMESPACE' has no policy restrictions - privileged pods allowed." -else - echo "Namespace '$NAMESPACE' has '$ENFORCE' policy - privileged pods allowed." -fi - -# Show resource usage for pods on this node -echo -e "${BOLD}Resource usage for pods on ${NODE_NAME}:${NC}" -kubectl top pods --all-namespaces > /tmp/allpods.out && \ -head -n 1 /tmp/allpods.out && \ -grep "$(kubectl get pods --all-namespaces --field-selector spec.nodeName=${NODE_NAME} -o jsonpath='{range .items[*]}{.metadata.name}{" "}{end}' | sed 's/[[:space:]]*$//' | sed 's/[[:space:]]/\\|/g')" /tmp/allpods.out --color=never - -# Create APerf pod -echo -e "\n${BOLD}Created pod configuration for node:${NC} ${NODE_NAME}${NC}" - -# Apply the pod directly from variable -echo -e -n "${BOLD}Deploying pod to Kubernetes...${NC} " -echo "$POD_YAML" | kubectl apply -f - -n ${NAMESPACE} - -# Wait for pod to start -echo -e -n "${BOLD}Waiting for pod to start...${NC} " -if ! kubectl wait --for=condition=ready pod/${POD_NAME} -n ${NAMESPACE} --timeout=60s; then - echo -e "${RED}${BOLD}Pod failed to reach ready state. Showing pod logs:${NC}" - kubectl describe pod ${POD_NAME} -n ${NAMESPACE} - echo -e "\n${RED}${BOLD}Pod logs:${NC}" - kubectl logs ${POD_NAME} -n ${NAMESPACE} - echo -e "\n${RED}${BOLD}Cleaning up resources...${NC}" - kubectl delete pod ${POD_NAME} -n ${NAMESPACE} --force --grace-period=0 2>/dev/null || true - exit 1 -fi - -# Start recording time -POD_STARTTIME=$(date +%Y%m%d-%H%M%S) - -# Start logs in background and save PID -echo -e "${BOLD}Starting program logs...${NC} ${YELLOW}" -kubectl logs -f ${POD_NAME} -n ${NAMESPACE} --tail=100 & -LOGS_PID=$! - -# Wait until we see the "Waiting for files to be copied" message in the logs -while ! kubectl logs ${POD_NAME} -n ${NAMESPACE} | grep -q "Waiting for files to be copied"; do - sleep 5s -done - -# Kill the logs tail process -kill $LOGS_PID 2>/dev/null || true - -# Copy files from pod to local directory -LOCAL_FILE="aperf_report_${POD_STARTTIME}.tar.gz" -echo -e "${NC}${BOLD}Aperf completed. Copying files from pod ${POD_NAME}...${NC}" -kubectl cp ${NAMESPACE}/${POD_NAME}:aperf_report.tar.gz ${LOCAL_FILE} - -# Delete the pod after copying files -echo -ne "${BOLD}Deleting pod to clean up resources...${NC} " -kubectl delete pod ${POD_NAME} -n ${NAMESPACE} - -echo -e "${BOLD}${GREEN}Files copied to${NC} ${BLUE}${LOCAL_FILE}${NC}" -echo -e "${BOLD}${GREEN}Done!${NC}" diff --git a/kubectl-aperf b/kubectl-aperf new file mode 100755 index 00000000..319963d6 --- /dev/null +++ b/kubectl-aperf @@ -0,0 +1,365 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +# Script to deploy a pod on a specific node, run aperf, and copy files locally +# Usage: ./aperf_k8s.sh --node="node-name" [--namespace="namespace"] [--aperf_options="options"] [--help] +# or: ./aperf_k8s.sh --node "node-name" [--namespace "namespace"] [--aperf_options "options"] [--help] + +# Default values +NAMESPACE="default" +APERF_OPTIONS="" +NODE_NAME="" +APERF_IMAGE="" +REPORT_NAME="aperf_record" +OPEN_BROWSER=true +SHOW_HELP=false +CPU_REQUEST="" +MEMORY_REQUEST="" +CPU_LIMIT="" +MEMORY_LIMIT="" + +# Define color and formatting codes +BOLD="\033[1m" +GREEN="\033[0;32m" +YELLOW="\033[0;33m" +BLUE="\033[0;34m" +RED="\033[0;31m" +NC="\033[0m" # No Color + +# Parse command line arguments +while [ $# -gt 0 ]; do + dest="" + case "${1%=*}" in + --node) dest="NODE_NAME";; + --namespace) dest="NAMESPACE";; + --aperf_options) dest="APERF_OPTIONS";; + --aperf_image) dest="APERF_IMAGE";; + --report-name) dest="REPORT_NAME";; + --open-browser) dest="OPEN_BROWSER";; + --cpu-request) dest="CPU_REQUEST";; + --memory-request) dest="MEMORY_REQUEST";; + --cpu-limit) dest="CPU_LIMIT";; + --memory-limit) dest="MEMORY_LIMIT";; + --help) + SHOW_HELP=true + shift + continue + ;; + *) + echo "Unknown parameter: $1" + exit 1 + ;; + esac + + if [[ "$1" = *=* ]]; then + eval ${dest}='"${1#*=}"' + else + eval ${dest}='"$2"' + shift + fi + shift +done + +# Show help if requested +if [ "$SHOW_HELP" = true ]; then + echo "Usage: ./aperf_k8s.sh --aperf_image=IMAGE --node=NODE_NAME [--namespace=NAMESPACE] [--aperf_options=OPTIONS] [--help]" + echo " or: ./aperf_k8s.sh --aperf_image IMAGE --node NODE_NAME [--namespace NAMESPACE] [--aperf_options OPTIONS] [--help]" + echo "" + echo "Parameters:" + echo " --aperf_image Required. ECR image location" + echo " --node Required. The name of the Kubernetes node to run aperf on" + echo " --namespace Optional. The Kubernetes namespace (default: '${NAMESPACE}')" + echo " --aperf_options Optional. Options to pass to aperf (default: '${APERF_OPTIONS}')" + echo " --report-name Optional. Name for aperf record/report (default: '${REPORT_NAME}')" + echo " --open-browser Optional. Open report in browser (default: ${OPEN_BROWSER})" + echo " --cpu-request Optional. CPU request (e.g., '1.0', '500m')" + echo " --memory-request Optional. Memory request (e.g., '1Gi', '512Mi')" + echo " --cpu-limit Optional. CPU limit (e.g., '1.0', '500m')" + echo " --memory-limit Optional. Memory limit (e.g., '1Gi', '512Mi')" + echo " --help Show this help message" + exit 0 +fi + +# Check if aperf image is provided +if [ -z "$APERF_IMAGE" ]; then + echo "Error: APerf image is required. Use --aperf_image=IMAGE_URL or --aperf_image IMAGE_URL to specify the ECR image location." + echo "Use --help for more information." + exit 1 +fi + +# Check if node name is provided +if [ -z "$NODE_NAME" ]; then + echo "Error: Node name is required. Use --node=NODE_NAME or --node NODE_NAME to specify a node." + echo "Use --help for more information." + exit 1 +fi + + +POD_NAME="aperf-pod-${NODE_NAME//[.]/-}" + +echo -e "${BOLD}Verifying node exists...${NC}" +if ! kubectl get node ${NODE_NAME} &>/dev/null; then + echo -e "${RED}Error: Node '${NODE_NAME}' not found in the cluster.${NC}" + echo -e "${YELLOW}Available nodes:${NC}" + kubectl get nodes -o custom-columns=NAME:.metadata.name --no-headers + exit 1 +fi +echo -e "${GREEN}Node '${NODE_NAME}' found${NC}" + +# Get node taints and generate tolerations +echo -e "${BOLD}Checking node taints... " +TAINTS=$(kubectl get node ${NODE_NAME} -o jsonpath='{.spec.taints[*]}' 2>/dev/null) + +TOLERATIONS="" +if [ -n "$TAINTS" ]; then + echo -e "${YELLOW}Node has taints, adding tolerations to pod spec${NC}" + + # Parse taints and create tolerations YAML + TOLERATIONS=" tolerations:" + + # Get taints as JSON array and process each one + TAINT_COUNT=$(kubectl get node ${NODE_NAME} -o json | jq -r '.spec.taints | length' 2>/dev/null || echo "0") + + for ((i=0; i<$TAINT_COUNT; i++)); do + KEY=$(kubectl get node ${NODE_NAME} -o json | jq -r ".spec.taints[$i].key" 2>/dev/null) + VALUE=$(kubectl get node ${NODE_NAME} -o json | jq -r ".spec.taints[$i].value" 2>/dev/null) + EFFECT=$(kubectl get node ${NODE_NAME} -o json | jq -r ".spec.taints[$i].effect" 2>/dev/null) + + echo -e " Taint: ${KEY}=${VALUE}:${EFFECT}" + + TOLERATIONS="${TOLERATIONS} + - key: \"${KEY}\"" + + if [ "$VALUE" != "null" ] && [ -n "$VALUE" ]; then + TOLERATIONS="${TOLERATIONS} + value: \"${VALUE}\"" + fi + + TOLERATIONS="${TOLERATIONS} + effect: \"${EFFECT}\" + operator: \"Equal\"" + done +else + echo -e "${GREEN}No taints found on node${NC}" +fi + +RESOURCES="" +if [ -n "$CPU_REQUEST" ] || [ -n "$MEMORY_REQUEST" ] || [ -n "$CPU_LIMIT" ] || [ -n "$MEMORY_LIMIT" ]; then + RESOURCES=" resources:" + + if [ -n "$CPU_REQUEST" ] || [ -n "$MEMORY_REQUEST" ]; then + RESOURCES="${RESOURCES} + requests:" + [ -n "$MEMORY_REQUEST" ] && RESOURCES="${RESOURCES} + memory: \"${MEMORY_REQUEST}\"" + [ -n "$CPU_REQUEST" ] && RESOURCES="${RESOURCES} + cpu: \"${CPU_REQUEST}\"" + fi + + if [ -n "$CPU_LIMIT" ] || [ -n "$MEMORY_LIMIT" ]; then + RESOURCES="${RESOURCES} + limits:" + [ -n "$MEMORY_LIMIT" ] && RESOURCES="${RESOURCES} + memory: \"${MEMORY_LIMIT}\"" + [ -n "$CPU_LIMIT" ] && RESOURCES="${RESOURCES} + cpu: \"${CPU_LIMIT}\"" + fi +fi + +# Create pod YAML as a variable +POD_YAML=$(cat << EOF +apiVersion: v1 +kind: Pod +metadata: + name: ${POD_NAME} + labels: + app: aperf +spec: + nodeSelector: + kubernetes.io/hostname: "${NODE_NAME}" +${TOLERATIONS} + containers: + - name: aperf-runner + image: ${APERF_IMAGE} + securityContext: + privileged: true + command: ["/bin/sh", "-c"] + args: + - | + set -e + + echo -e "\nStarting Aperf recording execution..." + echo "Run: /usr/bin/aperf record -r ${REPORT_NAME} ${APERF_OPTIONS}" + sudo /usr/bin/aperf record -r ${REPORT_NAME} ${APERF_OPTIONS} + echo "APerf record completed" + + echo -e "\nStarting Aperf report generation..." + echo "Run: /usr/bin/aperf report -r ${REPORT_NAME} -n ${REPORT_NAME}_report" + sudo /usr/bin/aperf report -r ${REPORT_NAME} -n ${REPORT_NAME}_report + echo "APerf report generation completed" + + echo -e "\nWaiting for files to be copied..." + sleep 7200 +${RESOURCES} + volumeMounts: + - mountPath: /boot + name: boot-volume + readOnly: true + - name: opt + mountPath: /shared + volumes: + - name: boot-volume + hostPath: + path: /boot + type: Directory + - name: opt + hostPath: + path: /opt + type: DirectoryOrCreate + hostPID: true + hostNetwork: true + restartPolicy: Never +EOF +) + + +# Get node instance type information +echo -e -n "${BOLD}Tageted node instance type... ${NC} " +kubectl get node ${NODE_NAME} -o jsonpath='{.metadata.labels.beta\.kubernetes\.io/instance-type}' + +# Check if namespace has security restriction. If yes, exit. +ENFORCE=$(kubectl get namespace $NAMESPACE -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce}' 2>/dev/null) +echo -e -n "\n${BOLD}Check namespace security policy... ${NC} " +if [ "$ENFORCE" = "baseline" ] || [ "$ENFORCE" = "restricted" ]; then + echo "Namespace '$NAMESPACE' has '$ENFORCE' policy - privileged pods NOT allowed. Exit" + exit 1 +elif [ -z "$ENFORCE" ]; then + echo "Namespace '$NAMESPACE' has no policy restrictions - privileged pods allowed." +else + echo "Namespace '$NAMESPACE' has '$ENFORCE' policy - privileged pods allowed." +fi + +# Show resource usage for pods on this node +echo -e "${BOLD}Resource usage for pods on ${NODE_NAME}: " +if kubectl top pods --all-namespaces > /tmp/allpods.out 2>/dev/null; then + head -n 1 /tmp/allpods.out + grep "$(kubectl get pods --all-namespaces --field-selector spec.nodeName=${NODE_NAME} -o jsonpath='{range .items[*]}{.metadata.name}{" "}{end}' | sed 's/[[:space:]]*$//' | sed 's/[[:space:]]/\\|/g')" /tmp/allpods.out --color=never || echo " No pods found on this node" + rm /tmp/allpods.out 2>/dev/null || true +else + echo "${YELLOW}Note: kubectl top not available (metrics-server may not be installed)${NC}" +fi + +# Create APerf pod +echo -e "\n${BOLD}Created pod configuration for node:${NC} ${NODE_NAME}${NC}" + +# Apply the pod directly from variable +echo -e -n "${BOLD}Deploying pod to Kubernetes...${NC} " +echo "$POD_YAML" | kubectl apply -f - -n ${NAMESPACE} + +# Wait for pod to start +echo -e -n "${BOLD}Waiting for pod to start...${NC} " +if ! kubectl wait --for=condition=ready pod/${POD_NAME} -n ${NAMESPACE} --timeout=60s; then + echo -e "${RED}${BOLD}Pod failed to reach ready state. Showing pod logs:${NC}" + kubectl describe pod ${POD_NAME} -n ${NAMESPACE} + echo -e "\n${RED}${BOLD}Pod logs:${NC}" + kubectl logs ${POD_NAME} -n ${NAMESPACE} + echo -e "\n${RED}${BOLD}Cleaning up resources...${NC}" + kubectl delete pod ${POD_NAME} -n ${NAMESPACE} --force --grace-period=0 2>/dev/null || true + exit 1 +fi + +# Start recording time +POD_STARTTIME=$(date +%Y%m%d-%H%M%S) + +# Start logs in background and save PID +echo -e "${BOLD}Starting program logs...${NC} ${YELLOW}" +kubectl logs -f ${POD_NAME} -n ${NAMESPACE} --tail=100 & +LOGS_PID=$! + +# Wait until we see the "Waiting for files to be copied" message in the logs +while ! kubectl logs ${POD_NAME} -n ${NAMESPACE} | grep -q "Waiting for files to be copied"; do + sleep 5s +done + +# Kill the logs tail process +kill $LOGS_PID 2>/dev/null || true + +# Copy files from pod to local directory +LOCAL_FILE="${REPORT_NAME}_${POD_STARTTIME}.tar.gz" +EXTRACT_DIR="${REPORT_NAME}_${POD_STARTTIME}" +echo -e "${NC}${BOLD}Aperf completed. Copying files from pod ${POD_NAME}...${NC}" +kubectl cp ${NAMESPACE}/${POD_NAME}:${REPORT_NAME}_report.tar.gz ${LOCAL_FILE} + +# Delete the pod after copying files +echo -ne "${BOLD}Deleting pod to clean up resources...${NC} " +kubectl delete pod ${POD_NAME} -n ${NAMESPACE} + +echo -e "${BOLD}${GREEN}Files copied to${NC} ${BLUE}${LOCAL_FILE}${NC}" + +# Extract the tar.gz file +echo -e "${BOLD}Extracting report files...${NC}" +mkdir -p "${EXTRACT_DIR}" + +if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" || "$OSTYPE" == "cygwin" ]]; then + # Windows - use tar if available, otherwise try 7z or PowerShell + if command -v tar &> /dev/null; then + tar -xzf "${LOCAL_FILE}" -C "${EXTRACT_DIR}" + elif command -v 7z &> /dev/null; then + 7z x "${LOCAL_FILE}" -so | 7z x -si -ttar -o"${EXTRACT_DIR}" + elif command -v powershell.exe &> /dev/null; then + powershell.exe -Command "Expand-Archive -Path '${LOCAL_FILE}' -DestinationPath '${EXTRACT_DIR}' -Force" + else + echo -e " ${RED}Error: No extraction tool found. Please install tar, 7-Zip, or use PowerShell${NC}" + exit 1 + fi +else + # macOS/Linux + tar -xzf "${LOCAL_FILE}" -C "${EXTRACT_DIR}" +fi + +echo -e " ${GREEN}Extracted to${NC} ${BLUE}${EXTRACT_DIR}/${NC}" + +# Open index.html in browser if enabled +if [ "$OPEN_BROWSER" = true ]; then + INDEX_FILE="${EXTRACT_DIR}/${REPORT_NAME}_report/index.html" + + if [ -f "$INDEX_FILE" ]; then + echo -e "${BOLD}Opening report in browser...${NC}" + + # Detect OS and open browser accordingly + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + open "$INDEX_FILE" + elif [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Linux + if command -v xdg-open &> /dev/null; then + xdg-open "$INDEX_FILE" + elif command -v sensible-browser &> /dev/null; then + sensible-browser "$INDEX_FILE" + else + echo -e " ${YELLOW}Could not detect browser command. Please open manually:${NC} ${BLUE}${INDEX_FILE}${NC}" + fi + elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" || "$OSTYPE" == "cygwin" ]]; then + # Windows + if command -v start &> /dev/null; then + start "$INDEX_FILE" + elif command -v cmd.exe &> /dev/null; then + cmd.exe /c start "$INDEX_FILE" + elif command -v powershell.exe &> /dev/null; then + powershell.exe -Command "Start-Process '${INDEX_FILE}'" + else + echo -e " ${YELLOW}Could not detect browser command. Please open manually:${NC} ${BLUE}${INDEX_FILE}${NC}" + fi + else + echo -e " ${YELLOW}Unsupported OS. Please open manually:${NC} ${BLUE}${INDEX_FILE}${NC}" + fi + else + echo -e " ${YELLOW}Warning: index.html not found at ${INDEX_FILE}${NC}" + echo -e " ${YELLOW}Extracted contents:${NC}" + ls -la "${EXTRACT_DIR}/" + fi +fi + +echo -e "${BOLD}${GREEN}Done!${NC}"