diff --git a/README.md b/README.md index 84467e3..96aa775 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,33 @@ This project uses the helm unittest plugin. ```bash helm unittest . ``` +### Run helm tests + +This project uses helm tests that should be run after deployment. These are integration tests that run actual pods in your cluster after deployment to verify the application is working correctly. + +Helm tests are present in `iag5-helm/charts/iag5/templates/tests/` + +Helm looks through ALL rendered Kubernetes manifests in chart for resources that have this specific annotation: + +```bash +annotations: + "helm.sh/hook": test +``` +Run all test using below command + +```bash +helm test iag5 -n --logs +``` +Run individual test using below command + +```bash +helm test iag5 -n --filter name=iag5-test-connection --logs +``` +Run multiple tests using below command + +```bash +helm test iag5 -n --filter 'name=iag5-test-version,name=iag5-test-processes' --logs +``` #### Values diff --git a/charts/iag5/Chart.yaml b/charts/iag5/Chart.yaml index 4acbda7..6ca3c50 100644 --- a/charts/iag5/Chart.yaml +++ b/charts/iag5/Chart.yaml @@ -10,7 +10,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.3 +version: 1.0.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/iag5/templates/tests/test-connection.yaml b/charts/iag5/templates/tests/test-connection.yaml new file mode 100644 index 0000000..436a8d4 --- /dev/null +++ b/charts/iag5/templates/tests/test-connection.yaml @@ -0,0 +1,29 @@ +# Test to check the connection to 50051 to ensure IAG5 service is running + +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "iag5.fullname" . }}-test-connection" + labels: + {{- include "iag5.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": before-hook-creation +spec: + containers: + - name: connection-test + image: busybox:latest + command: ['sh', '-c'] +# Testing connection to iag5-service.am.svc.cluster.local:50051 + args: + - | + echo "Testing connection to {{ .Values.service.name }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.port }}" + nc -zv -w 10 {{ .Values.service.name }}.{{ .Release.Namespace }}.svc.cluster.local {{ .Values.port }} + if [ $? -eq 0 ]; then + echo "SUCCESS: Port {{ .Values.port }} is open and accepting connections" + exit 0 + else + echo "FAILED: Cannot connect to port {{ .Values.port }}" + exit 1 + fi + restartPolicy: Never diff --git a/charts/iag5/templates/tests/test-processes.yaml b/charts/iag5/templates/tests/test-processes.yaml new file mode 100644 index 0000000..0c11557 --- /dev/null +++ b/charts/iag5/templates/tests/test-processes.yaml @@ -0,0 +1,160 @@ +# Test to check the number of processes running +# Test will check the number of processes running in sever & running with +# minimum number of processes mentioned in values.yaml + +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "iag5.fullname" . }}-test-processes" + labels: + {{- include "iag5.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": before-hook-creation +spec: + serviceAccountName: {{ include "iag5.fullname" . }}-test + containers: + - name: process-checker + image: bitnami/kubectl:latest + command: ['sh', '-c'] + args: + - | + set -e + + # Get minimum process counts from values (with defaults) + MIN_PROCESSES_SERVER={{ .Values.tests.processCount.server.min | default 1 }} + MIN_PROCESSES_RUNNER={{ .Values.tests.processCount.runner.min | default 1 }} + + echo "=========================================" + echo "IAG5 Process Count Verification" + echo "Namespace: {{ .Release.Namespace }}" + echo "Minimum expected 'iagctl server' processes: $MIN_PROCESSES_SERVER" + echo "Minimum expected 'iagctl runner' processes: $MIN_PROCESSES_RUNNER" + echo "=========================================" + echo "" + + FAILED=0 + TOTAL_PODS=0 + PASSED_PODS=0 + + # Wait for pods to be ready + echo "Waiting for IAG5 pods to be ready..." + kubectl wait --for=condition=ready pod \ + -l app.kubernetes.io/name={{ include "iag5.name" . }} \ + -n {{ .Release.Namespace }} \ + --timeout=60s || true + echo "" + + # Check server pods + echo "=========================================" + echo "Checking SERVER pods..." + echo "=========================================" + SERVER_PODS=$(kubectl get pods \ + -l app.kubernetes.io/name={{ include "iag5.name" . }},app.kubernetes.io/component=server \ + -n {{ .Release.Namespace }} \ + -o jsonpath='{.items[*].metadata.name}') + + if [ -z "$SERVER_PODS" ]; then + echo "WARNING: No server pods found" + else + for POD in $SERVER_PODS; do + TOTAL_PODS=$((TOTAL_PODS + 1)) + echo "" + echo "Pod: $POD" + + # Get pod status + POD_STATUS=$(kubectl get pod $POD -n {{ .Release.Namespace }} -o jsonpath='{.status.phase}') + echo " Status: $POD_STATUS" + + if [ "$POD_STATUS" != "Running" ]; then + echo " Result: SKIPPED (Pod not running)" + continue + fi + + # Count 'iagctl server' processes + PROCESS_COUNT=$(kubectl exec -n {{ .Release.Namespace }} $POD -- sh -c "ps aux | grep 'iagctl server' | grep -v grep | wc -l" 2>/dev/null || echo "0") + echo " 'iagctl server' process count: $PROCESS_COUNT" + + if [ "$PROCESS_COUNT" -ge "$MIN_PROCESSES_SERVER" ]; then + echo " Result: ✓ PASS ($PROCESS_COUNT >= $MIN_PROCESSES_SERVER)" + PASSED_PODS=$((PASSED_PODS + 1)) + else + echo " Result: ✗ FAIL ($PROCESS_COUNT < $MIN_PROCESSES_SERVER)" + FAILED=1 + + # Only show processes on failure for debugging + echo " All processes in pod (for debugging):" + kubectl exec -n {{ .Release.Namespace }} $POD -- ps aux 2>/dev/null | sed 's/^/ /' || echo " Could not retrieve process list" + fi + done + fi + + # Check runner pods + echo "" + echo "=========================================" + echo "Checking RUNNER pods..." + echo "=========================================" + RUNNER_PODS=$(kubectl get pods \ + -l app.kubernetes.io/name={{ include "iag5.name" . }},app.kubernetes.io/component=runner \ + -n {{ .Release.Namespace }} \ + -o jsonpath='{.items[*].metadata.name}') + + if [ -z "$RUNNER_PODS" ]; then + echo "INFO: No runner pods found (runnerSettings.replicaCount may be 0)" + else + for POD in $RUNNER_PODS; do + TOTAL_PODS=$((TOTAL_PODS + 1)) + echo "" + echo "Pod: $POD" + + # Get pod status + POD_STATUS=$(kubectl get pod $POD -n {{ .Release.Namespace }} -o jsonpath='{.status.phase}') + echo " Status: $POD_STATUS" + + if [ "$POD_STATUS" != "Running" ]; then + echo " Result: SKIPPED (Pod not running)" + continue + fi + + # Count 'iagctl runner' processes + PROCESS_COUNT=$(kubectl exec -n {{ .Release.Namespace }} $POD -- sh -c "ps aux | grep 'iagctl runner' | grep -v grep | wc -l" 2>/dev/null || echo "0") + echo " 'iagctl runner' process count: $PROCESS_COUNT" + + if [ "$PROCESS_COUNT" -ge "$MIN_PROCESSES_RUNNER" ]; then + echo " Result: ✓ PASS ($PROCESS_COUNT >= $MIN_PROCESSES_RUNNER)" + PASSED_PODS=$((PASSED_PODS + 1)) + else + echo " Result: ✗ FAIL ($PROCESS_COUNT < $MIN_PROCESSES_RUNNER)" + FAILED=1 + + # Only show processes on failure for debugging + echo " All processes in pod (for debugging):" + kubectl exec -n {{ .Release.Namespace }} $POD -- ps aux 2>/dev/null | sed 's/^/ /' || echo " Could not retrieve process list" + fi + done + fi + + # Print summary + echo "" + echo "=========================================" + echo "SUMMARY" + echo "=========================================" + echo "Total pods checked: $TOTAL_PODS" + echo "Passed: $PASSED_PODS" + echo "Failed: $((TOTAL_PODS - PASSED_PODS))" + echo "" + + if [ $FAILED -eq 0 ] && [ $TOTAL_PODS -gt 0 ]; then + echo "Result: ✓ SUCCESS" + echo "All pods have adequate 'iagctl' process counts" + exit 0 + elif [ $TOTAL_PODS -eq 0 ]; then + echo "Result: ✗ FAILED" + echo "No IAG5 pods found to test" + exit 1 + else + echo "Result: ✗ FAILED" + echo "One or more pods do not have adequate 'iagctl' process counts" + exit 1 + fi + restartPolicy: Never \ No newline at end of file diff --git a/charts/iag5/templates/tests/test-version.yaml b/charts/iag5/templates/tests/test-version.yaml new file mode 100644 index 0000000..10a8607 --- /dev/null +++ b/charts/iag5/templates/tests/test-version.yaml @@ -0,0 +1,200 @@ +# Test to check the version of IAG5 +# test compares the IAG version present in values.yaml file +# with IAG running in server and runner + +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "iag5.fullname" . }}-test-version" + labels: + {{- include "iag5.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": before-hook-creation +spec: + serviceAccountName: {{ include "iag5.fullname" . }}-test + containers: + - name: version-checker + image: bitnami/kubectl:latest + command: ['sh', '-c'] + args: + - | + set -e + + echo "=========================================" + echo "IAG5 Version Verification" + echo "Namespace: {{ .Release.Namespace }}" + echo "Expected version: {{ .Values.image.tag }}" + echo "=========================================" + echo "" + + FAILED=0 + TOTAL_PODS=0 + PASSED_PODS=0 + EXPECTED_VERSION="{{ .Values.image.tag }}" + + # Extract version number (e.g., "5.1.1-amd64" -> "5.1.1") + EXPECTED_VERSION_NUMBER=$(echo "$EXPECTED_VERSION" | sed 's/-.*$//') + echo "Expected version number: $EXPECTED_VERSION_NUMBER" + echo "" + + # Wait for pods to be ready + echo "Waiting for IAG5 pods to be ready..." + kubectl wait --for=condition=ready pod \ + -l app.kubernetes.io/name={{ include "iag5.name" . }} \ + -n {{ .Release.Namespace }} \ + --timeout=60s || true + echo "" + + # Check server pods + echo "=========================================" + echo "Checking SERVER pods..." + echo "=========================================" + SERVER_PODS=$(kubectl get pods \ + -l app.kubernetes.io/name={{ include "iag5.name" . }},app.kubernetes.io/component=server \ + -n {{ .Release.Namespace }} \ + -o jsonpath='{.items[*].metadata.name}') + + if [ -z "$SERVER_PODS" ]; then + echo "WARNING: No server pods found" + else + for POD in $SERVER_PODS; do + TOTAL_PODS=$((TOTAL_PODS + 1)) + echo "" + echo "Pod: $POD" + + # Get pod status + POD_STATUS=$(kubectl get pod $POD -n {{ .Release.Namespace }} -o jsonpath='{.status.phase}') + echo " Status: $POD_STATUS" + + if [ "$POD_STATUS" != "Running" ]; then + echo " Result: SKIPPED (Pod not running)" + continue + fi + + # Get version from iagctl + echo " Executing: iagctl version" + VERSION_OUTPUT=$(kubectl exec -n {{ .Release.Namespace }} $POD -- iagctl version 2>&1 || echo "ERROR") + + if echo "$VERSION_OUTPUT" | grep -q "ERROR\|error\|command not found"; then + echo " Error getting version:" + echo "$VERSION_OUTPUT" | sed 's/^/ /' + echo " Result: ✗ FAIL (Could not execute iagctl version)" + FAILED=1 + continue + fi + + echo " Version output:" + echo "$VERSION_OUTPUT" | sed 's/^/ /' + + # Extract version number from output (looking for pattern like "version: 5.1.1" or "5.1.1") + ACTUAL_VERSION=$(echo "$VERSION_OUTPUT" | grep -i "version" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + + if [ -z "$ACTUAL_VERSION" ]; then + echo " Result: ✗ FAIL (Could not parse version from output)" + FAILED=1 + continue + fi + + echo " Parsed version: $ACTUAL_VERSION" + + # Compare versions + if [ "$ACTUAL_VERSION" = "$EXPECTED_VERSION_NUMBER" ]; then + echo " Result: ✓ PASS ($ACTUAL_VERSION = $EXPECTED_VERSION_NUMBER)" + PASSED_PODS=$((PASSED_PODS + 1)) + else + echo " Result: ✗ FAIL ($ACTUAL_VERSION ≠ $EXPECTED_VERSION_NUMBER)" + FAILED=1 + fi + done + fi + + # Check runner pods + echo "" + echo "=========================================" + echo "Checking RUNNER pods..." + echo "=========================================" + RUNNER_PODS=$(kubectl get pods \ + -l app.kubernetes.io/name={{ include "iag5.name" . }},app.kubernetes.io/component=runner \ + -n {{ .Release.Namespace }} \ + -o jsonpath='{.items[*].metadata.name}') + + if [ -z "$RUNNER_PODS" ]; then + echo "INFO: No runner pods found (runnerSettings.replicaCount may be 0)" + else + for POD in $RUNNER_PODS; do + TOTAL_PODS=$((TOTAL_PODS + 1)) + echo "" + echo "Pod: $POD" + + # Get pod status + POD_STATUS=$(kubectl get pod $POD -n {{ .Release.Namespace }} -o jsonpath='{.status.phase}') + echo " Status: $POD_STATUS" + + if [ "$POD_STATUS" != "Running" ]; then + echo " Result: SKIPPED (Pod not running)" + continue + fi + + # Get version from iagctl + echo " Executing: iagctl version" + VERSION_OUTPUT=$(kubectl exec -n {{ .Release.Namespace }} $POD -- iagctl version 2>&1 || echo "ERROR") + + if echo "$VERSION_OUTPUT" | grep -q "ERROR\|error\|command not found"; then + echo " Error getting version:" + echo "$VERSION_OUTPUT" | sed 's/^/ /' + echo " Result: ✗ FAIL (Could not execute iagctl version)" + FAILED=1 + continue + fi + + echo " Version output:" + echo "$VERSION_OUTPUT" | sed 's/^/ /' + + # Extract version number from output + ACTUAL_VERSION=$(echo "$VERSION_OUTPUT" | grep -i "version" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + + if [ -z "$ACTUAL_VERSION" ]; then + echo " Result: ✗ FAIL (Could not parse version from output)" + FAILED=1 + continue + fi + + echo " Parsed version: $ACTUAL_VERSION" + + # Compare versions + if [ "$ACTUAL_VERSION" = "$EXPECTED_VERSION_NUMBER" ]; then + echo " Result: ✓ PASS ($ACTUAL_VERSION = $EXPECTED_VERSION_NUMBER)" + PASSED_PODS=$((PASSED_PODS + 1)) + else + echo " Result: ✗ FAIL ($ACTUAL_VERSION ≠ $EXPECTED_VERSION_NUMBER)" + FAILED=1 + fi + done + fi + + # Print summary + echo "" + echo "=========================================" + echo "SUMMARY" + echo "=========================================" + echo "Expected version: $EXPECTED_VERSION_NUMBER" + echo "Total pods checked: $TOTAL_PODS" + echo "Passed: $PASSED_PODS" + echo "Failed: $((TOTAL_PODS - PASSED_PODS))" + echo "" + + if [ $FAILED -eq 0 ] && [ $TOTAL_PODS -gt 0 ]; then + echo "Result: ✓ SUCCESS" + echo "All pods are running the expected version" + exit 0 + elif [ $TOTAL_PODS -eq 0 ]; then + echo "Result: ✗ FAILED" + echo "No IAG5 pods found to test" + exit 1 + else + echo "Result: ✗ FAILED" + echo "One or more pods are not running the expected version" + exit 1 + fi + restartPolicy: Never diff --git a/charts/iag5/values.yaml b/charts/iag5/values.yaml index 6292a7c..15f2386 100644 --- a/charts/iag5/values.yaml +++ b/charts/iag5/values.yaml @@ -258,6 +258,14 @@ applicationSettings: # separated list: hostname1:port hostname2:port. etcdHosts: "etcd.default.svc.cluster.local:2379" +tests: + enabled: true + processCount: + server: + min: 1 # Minimum processes for server pods + runner: + min: 1 # Minimum processes for runner pods + # DynamoDB settings # -- The DynamoDB table name when storeBackend is set to "dynamodb" dynamodbTableName: ""