Set up automated quality gate validation in ArgoCD using SonarQube webhooks and pre-sync hooks to prevent deployments that fail code quality standards. This integration ensures only code that passes your quality criteria gets deployed to production.
Prerequisites
- Running Kubernetes cluster with ArgoCD
- SonarQube server with API access
- kubectl configured
- Basic understanding of webhooks and Kubernetes Jobs
What this solves
This tutorial shows you how to integrate SonarQube quality gates with ArgoCD to automatically validate code quality before GitOps deployments. When SonarQube detects quality gate failures, ArgoCD will block the deployment, preventing broken or insecure code from reaching production environments.
Prerequisites
- Running Kubernetes cluster with ArgoCD installed
- SonarQube server accessible from your ArgoCD cluster
- Git repository with your application code and manifests
- Basic understanding of Kubernetes webhooks and ArgoCD sync policies
Step-by-step configuration
Create SonarQube webhook configuration
First, configure SonarQube to send quality gate status updates to a webhook endpoint that ArgoCD can process.
kubectl create namespace sonarqube-webhook
kubectl create configmap webhook-config -n sonarqube-webhook --from-literal=sonar-url="http://sonarqube.example.com:9000"
Deploy webhook service for quality gate validation
Create a webhook service that receives SonarQube quality gate notifications and stores the results for ArgoCD to check.
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonarqube-webhook
namespace: sonarqube-webhook
spec:
replicas: 2
selector:
matchLabels:
app: sonarqube-webhook
template:
metadata:
labels:
app: sonarqube-webhook
spec:
containers:
- name: webhook
image: nginx:alpine
ports:
- containerPort: 80
env:
- name: SONAR_URL
valueFrom:
configMapKeyRef:
name: webhook-config
key: sonar-url
---
apiVersion: v1
kind: Service
metadata:
name: sonarqube-webhook-service
namespace: sonarqube-webhook
spec:
selector:
app: sonarqube-webhook
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
kubectl apply -f webhook-deployment.yaml
Configure SonarQube project webhook
In your SonarQube project, add a webhook that sends quality gate results to your Kubernetes webhook service.
curl -u admin:admin123 -X POST "http://sonarqube.example.com:9000/api/webhooks/create" \
-d "name=ArgoCD-QualityGate" \
-d "url=http://sonarqube-webhook-service.sonarqube-webhook.svc.cluster.local/webhook" \
-d "project=your-project-key"
Create ArgoCD pre-sync hook script
Create a script that ArgoCD will run before each sync to check the latest SonarQube quality gate status.
#!/bin/bash
set -e
SONAR_PROJECT_KEY="${SONAR_PROJECT_KEY}"
SONAR_URL="${SONAR_URL}"
SONAR_TOKEN="${SONAR_TOKEN}"
GIT_COMMIT="${ARGOCD_APP_REVISION}"
Check if quality gate passed for this commit
echo "Checking quality gate for commit: ${GIT_COMMIT}"
Get quality gate status from SonarQube API
QUALITY_GATE_STATUS=$(curl -s -u "${SONAR_TOKEN}:" \
"${SONAR_URL}/api/qualitygates/project_status?projectKey=${SONAR_PROJECT_KEY}" \
| jq -r '.projectStatus.status')
echo "Quality gate status: ${QUALITY_GATE_STATUS}"
if [ "${QUALITY_GATE_STATUS}" != "OK" ]; then
echo "Quality gate failed. Blocking deployment."
echo "View details: ${SONAR_URL}/dashboard?id=${SONAR_PROJECT_KEY}"
exit 1
fi
echo "Quality gate passed. Proceeding with deployment."
exit 0
Create ConfigMap for the quality gate script
Store the validation script in a ConfigMap so ArgoCD can access it during pre-sync hooks.
kubectl create configmap quality-gate-script -n argocd \
--from-file=quality-gate-check.sh
Create pre-sync hook Job template
Define a Kubernetes Job that runs the quality gate validation before each ArgoCD sync operation.
apiVersion: batch/v1
kind: Job
metadata:
name: quality-gate-check
namespace: default
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
argocd.argoproj.io/sync-wave: "-1"
spec:
template:
spec:
restartPolicy: Never
containers:
- name: quality-gate-checker
image: curlimages/curl:latest
command: ["/bin/sh"]
args: ["/scripts/quality-gate-check.sh"]
env:
- name: SONAR_PROJECT_KEY
value: "your-project-key"
- name: SONAR_URL
value: "http://sonarqube.example.com:9000"
- name: SONAR_TOKEN
valueFrom:
secretKeyRef:
name: sonarqube-token
key: token
- name: ARGOCD_APP_REVISION
valueFrom:
fieldRef:
fieldPath: metadata.annotations['argocd.argoproj.io/revision']
volumeMounts:
- name: script-volume
mountPath: /scripts
volumes:
- name: script-volume
configMap:
name: quality-gate-script
defaultMode: 0755
backoffLimit: 3
Create SonarQube authentication secret
Store your SonarQube authentication token securely in a Kubernetes secret.
kubectl create secret generic sonarqube-token -n default \
--from-literal=token="your-sonarqube-token-here"
Configure ArgoCD Application with sync policy
Update your ArgoCD Application to include the pre-sync hook and configure sync policies that respect quality gates.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-application
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/my-app.git
targetRevision: main
path: k8s
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 3m
kubectl apply -f argocd-app.yaml
Configure RBAC for ArgoCD service account
Grant necessary permissions for ArgoCD to execute pre-sync hooks and access quality gate information.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: argocd-quality-gate-checker
rules:
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: argocd-quality-gate-checker
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: argocd-quality-gate-checker
subjects:
- kind: ServiceAccount
name: argocd-application-controller
namespace: argocd
kubectl apply -f rbac-config.yaml
Implement deployment validation policies
Create additional validation rules that complement SonarQube quality gates for comprehensive deployment safety.
apiVersion: v1
kind: ConfigMap
metadata:
name: deployment-policies
namespace: argocd
data:
policy.rego: |
package argocd.quality
# Allow deployment only if quality gate passes
allow {
input.quality_gate.status == "OK"
input.security_scan.vulnerabilities.critical == 0
input.test_coverage.percentage >= 80
}
# Block deployment on quality gate failure
deny[msg] {
input.quality_gate.status != "OK"
msg := "Quality gate failed. Deployment blocked."
}
# Block deployment on critical vulnerabilities
deny[msg] {
input.security_scan.vulnerabilities.critical > 0
msg := "Critical security vulnerabilities detected."
}
kubectl apply -f validation-policy.yaml
Configure ArgoCD notifications for quality gate events
Set up notifications to alert teams when deployments are blocked due to quality gate failures.
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
service.slack: |
token: your-slack-bot-token
template.quality-gate-failed: |
message: |
🚫 Deployment blocked for {{.app.metadata.name}}
Quality gate failed in SonarQube
Repository: {{.app.spec.source.repoURL}}
Revision: {{.app.status.sync.revision}}
View details: http://sonarqube.example.com:9000/dashboard?id={{.app.metadata.annotations.sonar-project}}
trigger.on-sync-failed: |
- when: app.status.operationState.phase in ['Failed']
send: [quality-gate-failed]
subscriptions: |
- recipients:
- slack:dev-team-channel
triggers:
- on-sync-failed
kubectl apply -f argocd-notifications-config.yaml
Monitor quality gate integration
Set up monitoring for webhook service
Monitor the webhook service that processes SonarQube quality gate notifications to ensure reliable operation.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: sonarqube-webhook-monitor
namespace: sonarqube-webhook
spec:
selector:
matchLabels:
app: sonarqube-webhook
endpoints:
- port: http
path: /metrics
interval: 30s
kubectl apply -f webhook-monitor.yaml
Create alerting rules for quality gate failures
Set up Prometheus alerts to notify when quality gates consistently fail or when the webhook service is unavailable.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: quality-gate-alerts
namespace: sonarqube-webhook
spec:
groups:
- name: quality-gate.rules
rules:
- alert: QualityGateWebhookDown
expr: up{job="sonarqube-webhook"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "SonarQube webhook service is down"
description: "The webhook service for SonarQube quality gates has been down for more than 5 minutes"
- alert: HighQualityGateFailureRate
expr: rate(quality_gate_failures_total[5m]) > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "High rate of quality gate failures"
description: "Quality gate failure rate is {{ $value }} failures per second over the last 5 minutes"
kubectl apply -f quality-gate-alerts.yaml
Verify your setup
Test the integration by triggering a deployment with code that fails SonarQube quality gates.
# Check ArgoCD application status
kubectl get applications -n argocd
Verify pre-sync hook execution
kubectl get jobs -n default | grep quality-gate
Check webhook service status
kubectl get pods -n sonarqube-webhook
kubectl logs -l app=sonarqube-webhook -n sonarqube-webhook
Test SonarQube webhook connectivity
curl -X POST http://sonarqube-webhook-service.sonarqube-webhook.svc.cluster.local/webhook \
-H "Content-Type: application/json" \
-d '{"status":"ERROR","projectKey":"test"}'
View ArgoCD sync history
argocd app history my-application
Troubleshoot quality gate failures
Debug pre-sync hook failures
When deployments are blocked, examine the pre-sync hook logs to understand why quality gates failed.
# Get failed job logs
kubectl logs -l job-name=quality-gate-check -n default
Check ArgoCD sync operation details
argocd app get my-application --show-operation
Verify SonarQube project status manually
curl -u "your-token:" "http://sonarqube.example.com:9000/api/qualitygates/project_status?projectKey=your-project"
Check webhook service connectivity
kubectl exec -it deploy/sonarqube-webhook -n sonarqube-webhook -- wget -O- http://sonarqube.example.com:9000/api/system/status
Override quality gates for emergency deployments
In emergency situations, you can temporarily bypass quality gate validation while maintaining audit trails.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-application
namespace: argocd
annotations:
quality-gate.override: "emergency-deployment-ticket-12345"
quality-gate.override-reason: "Critical security patch deployment"
quality-gate.override-authorized-by: "team-lead@company.com"
spec:
# ... rest of application spec
syncPolicy:
syncOptions:
- SkipHooks=true # Temporarily skip pre-sync hooks
kubectl apply -f emergency-override.yaml
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Pre-sync hook job fails with network timeout | SonarQube server unreachable from cluster | Verify network policies and DNS resolution: kubectl exec -it deploy/sonarqube-webhook -n sonarqube-webhook -- nslookup sonarqube.example.com |
| Quality gate check returns "ERROR" for passing projects | Wrong project key or authentication token | Verify project key and regenerate SonarQube token: kubectl get secret sonarqube-token -o yaml |
| ArgoCD shows sync successful but hook wasn't executed | Pre-sync hook annotation missing or incorrect | Check hook annotations: kubectl get job quality-gate-check -o yaml | grep annotations |
| Webhook receives duplicate notifications | Multiple webhook configurations in SonarQube | List and clean up webhooks: curl -u admin:admin123 "http://sonarqube.example.com:9000/api/webhooks/list" |
| Job pod fails with permission denied | Missing RBAC permissions for service account | Verify cluster role binding: kubectl get clusterrolebinding argocd-quality-gate-checker -o yaml |
Next steps
- Configure advanced SonarQube quality gates and custom rules
- Integrate OPA Gatekeeper with ArgoCD for policy management
- Configure Prometheus monitoring for ArgoCD
- Set up ArgoCD notifications for Slack and Microsoft Teams
- Implement Pod Security Standards with admission controllers
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# ArgoCD with SonarQube Quality Gates Integration Setup Script
# Configures ArgoCD to validate deployments using SonarQube quality gates
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default values
SONAR_URL="${1:-http://sonarqube.example.com:9000}"
SONAR_PROJECT_KEY="${2:-your-project-key}"
ARGOCD_NAMESPACE="${3:-argocd}"
WEBHOOK_NAMESPACE="sonarqube-webhook"
usage() {
echo "Usage: $0 [SONAR_URL] [SONAR_PROJECT_KEY] [ARGOCD_NAMESPACE]"
echo "Example: $0 http://sonarqube.company.com:9000 my-project argocd"
exit 1
}
cleanup() {
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
kubectl delete namespace "$WEBHOOK_NAMESPACE" --ignore-not-found=true
kubectl delete configmap quality-gate-script -n "$ARGOCD_NAMESPACE" --ignore-not-found=true
exit 1
}
trap cleanup ERR
print_step() {
echo -e "${GREEN}[$1] $2${NC}"
}
print_warning() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
print_error() {
echo -e "${RED}[ERROR] $1${NC}"
}
# Detect OS distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
;;
*)
print_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
print_error "Cannot detect OS distribution"
exit 1
fi
# Check prerequisites
print_step "1/10" "Checking prerequisites..."
if [ "$EUID" -ne 0 ]; then
print_error "This script must be run as root or with sudo"
exit 1
fi
# Check if kubectl is available
if ! command -v kubectl &> /dev/null; then
print_step "2/10" "Installing kubectl..."
$PKG_UPDATE
if [ "$PKG_MGR" = "apt" ]; then
$PKG_INSTALL apt-transport-https ca-certificates curl
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
$PKG_UPDATE
$PKG_INSTALL kubectl
else
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
$PKG_INSTALL kubectl
fi
fi
# Install required tools
print_step "3/10" "Installing required tools..."
$PKG_INSTALL curl jq
# Verify kubectl cluster access
print_step "4/10" "Verifying Kubernetes cluster access..."
if ! kubectl cluster-info &> /dev/null; then
print_error "Cannot connect to Kubernetes cluster. Please configure kubectl first."
exit 1
fi
# Check if ArgoCD is installed
if ! kubectl get namespace "$ARGOCD_NAMESPACE" &> /dev/null; then
print_error "ArgoCD namespace '$ARGOCD_NAMESPACE' not found. Please install ArgoCD first."
exit 1
fi
# Create webhook namespace
print_step "5/10" "Creating webhook namespace and configuration..."
kubectl create namespace "$WEBHOOK_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap webhook-config -n "$WEBHOOK_NAMESPACE" \
--from-literal=sonar-url="$SONAR_URL" \
--dry-run=client -o yaml | kubectl apply -f -
# Create webhook deployment
print_step "6/10" "Deploying SonarQube webhook service..."
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonarqube-webhook
namespace: $WEBHOOK_NAMESPACE
spec:
replicas: 2
selector:
matchLabels:
app: sonarqube-webhook
template:
metadata:
labels:
app: sonarqube-webhook
spec:
containers:
- name: webhook
image: nginx:alpine
ports:
- containerPort: 80
env:
- name: SONAR_URL
valueFrom:
configMapKeyRef:
name: webhook-config
key: sonar-url
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
securityContext:
runAsNonRoot: true
runAsUser: 101
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
---
apiVersion: v1
kind: Service
metadata:
name: sonarqube-webhook-service
namespace: $WEBHOOK_NAMESPACE
spec:
selector:
app: sonarqube-webhook
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
EOF
# Create quality gate check script
print_step "7/10" "Creating quality gate validation script..."
cat > /tmp/quality-gate-check.sh <<'SCRIPT'
#!/bin/bash
set -e
SONAR_PROJECT_KEY="${SONAR_PROJECT_KEY}"
SONAR_URL="${SONAR_URL}"
SONAR_TOKEN="${SONAR_TOKEN}"
GIT_COMMIT="${ARGOCD_APP_REVISION:-main}"
echo "Checking quality gate for project: ${SONAR_PROJECT_KEY}"
echo "Commit: ${GIT_COMMIT}"
if [ -z "$SONAR_TOKEN" ]; then
echo "ERROR: SONAR_TOKEN environment variable is required"
exit 1
fi
QUALITY_GATE_STATUS=$(curl -s -u "${SONAR_TOKEN}:" \
"${SONAR_URL}/api/qualitygates/project_status?projectKey=${SONAR_PROJECT_KEY}" \
| jq -r '.projectStatus.status // "UNKNOWN"')
echo "Quality gate status: ${QUALITY_GATE_STATUS}"
case "${QUALITY_GATE_STATUS}" in
"OK")
echo "✅ Quality gate passed. Proceeding with deployment."
exit 0
;;
"ERROR")
echo "❌ Quality gate failed. Blocking deployment."
echo "View details: ${SONAR_URL}/dashboard?id=${SONAR_PROJECT_KEY}"
exit 1
;;
"UNKNOWN"|"")
echo "⚠️ Quality gate status unknown. Check SonarQube connectivity."
exit 1
;;
*)
echo "⚠️ Unexpected quality gate status: ${QUALITY_GATE_STATUS}"
exit 1
;;
esac
SCRIPT
chmod 755 /tmp/quality-gate-check.sh
# Create ConfigMap with the script
kubectl create configmap quality-gate-script -n "$ARGOCD_NAMESPACE" \
--from-file=quality-gate-check.sh=/tmp/quality-gate-check.sh \
--dry-run=client -o yaml | kubectl apply -f -
# Create pre-sync hook job template
print_step "8/10" "Creating pre-sync hook job template..."
cat > /tmp/presync-hook-job.yaml <<EOF
apiVersion: batch/v1
kind: Job
metadata:
name: quality-gate-check
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
argocd.argoproj.io/sync-wave: "-1"
spec:
template:
spec:
restartPolicy: Never
containers:
- name: quality-gate-checker
image: curlimages/curl:latest
command: ["/bin/sh"]
args: ["/scripts/quality-gate-check.sh"]
env:
- name: SONAR_URL
value: "$SONAR_URL"
- name: SONAR_PROJECT_KEY
value: "$SONAR_PROJECT_KEY"
- name: SONAR_TOKEN
valueFrom:
secretKeyRef:
name: sonarqube-token
key: token
volumeMounts:
- name: script-volume
mountPath: /scripts
readOnly: true
securityContext:
runAsNonRoot: true
runAsUser: 65534
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
resources:
requests:
memory: "32Mi"
cpu: "50m"
limits:
memory: "64Mi"
cpu: "100m"
volumes:
- name: script-volume
configMap:
name: quality-gate-script
defaultMode: 0755
EOF
print_step "9/10" "Waiting for webhook deployment to be ready..."
kubectl wait --for=condition=available --timeout=300s deployment/sonarqube-webhook -n "$WEBHOOK_NAMESPACE"
# Verification
print_step "10/10" "Verifying installation..."
# Check webhook service
if kubectl get service sonarqube-webhook-service -n "$WEBHOOK_NAMESPACE" &> /dev/null; then
echo "✅ Webhook service created successfully"
else
print_error "Webhook service not found"
exit 1
fi
# Check ConfigMap
if kubectl get configmap quality-gate-script -n "$ARGOCD_NAMESPACE" &> /dev/null; then
echo "✅ Quality gate script ConfigMap created successfully"
else
print_error "Quality gate script ConfigMap not found"
exit 1
fi
# Cleanup temporary files
rm -f /tmp/quality-gate-check.sh /tmp/presync-hook-job.yaml
echo -e "${GREEN}✅ Installation completed successfully!${NC}"
echo
echo "Next steps:"
echo "1. Create SonarQube token secret:"
echo " kubectl create secret generic sonarqube-token -n your-app-namespace --from-literal=token=YOUR_SONAR_TOKEN"
echo
echo "2. Add the pre-sync hook job to your ArgoCD application manifests"
echo " (located in /tmp/presync-hook-job.yaml before cleanup)"
echo
echo "3. Configure SonarQube webhook:"
echo " curl -u admin:password -X POST \"$SONAR_URL/api/webhooks/create\" \\"
echo " -d \"name=ArgoCD-QualityGate\" \\"
echo " -d \"url=http://sonarqube-webhook-service.$WEBHOOK_NAMESPACE.svc.cluster.local/webhook\" \\"
echo " -d \"project=$SONAR_PROJECT_KEY\""
echo
echo "4. Test the integration by triggering an ArgoCD sync"
Review the script before running. Execute with: bash install.sh