Files
scrum-manager/Jenkinsfile
tusuii 5ed8d0bbdc
Some checks failed
scrum-manager/pipeline/head There was a failure building this commit
fix: remove PVC patch that broke kubectl apply on bound claims
The mysql-data-pvc was already dynamically provisioned by the cluster's
'local-path' StorageClass. The overlay patch tried to change storageClassName
to 'manual' and volumeName on an already-bound PVC, which Kubernetes forbids:
  "spec is immutable after creation except resources.requests"

Fixes:
- Remove mysql-pvc-patch from kustomization.yaml (PVC left as-is)
- Remove mysql-pv.yaml resource (not needed with dynamic provisioner)
- Add comment explaining when manual PV/PVC is needed vs not

Jenkinsfile: add --timeout and FQDN to smoke test curl; add comments
explaining MySQL Recreate strategy startup timing expectations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 23:02:54 +05:30

180 lines
6.3 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
pipeline {
agent any
environment {
HARBOR_URL = '192.168.108.200:80'
HARBOR_PROJECT = 'library'
IMAGE_TAG = "${env.BUILD_NUMBER}"
K8S_CRED_ID = 'k8s-config'
FRONTEND_IMAGE = '192.168.108.200:80/library/scrum-frontend'
BACKEND_IMAGE = '192.168.108.200:80/library/scrum-backend'
// Workspace root IS the project root — no subdirectory needed
K8S_OVERLAY = 'k8s/overlays/on-premise'
}
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
}
stages {
stage('Checkout') {
steps {
checkout scm
echo "Workspace: ${env.WORKSPACE}"
sh 'ls -la' // quick sanity check — confirm Dockerfile is here
}
}
stage('Test') {
parallel {
stage('Backend Tests') {
steps {
dir('server') { // server/ relative to workspace root
sh 'npm ci && npm test -- --reporter=verbose 2>&1 || true'
}
}
}
stage('Frontend Tests') {
steps {
// frontend lives at workspace root
sh 'npm ci && npm test -- --reporter=verbose 2>&1 || true'
}
}
}
}
stage('Build Images') {
parallel {
stage('Build Frontend') {
steps {
// Dockerfile is at workspace root
sh """
docker build \
-f Dockerfile \
-t ${FRONTEND_IMAGE}:${IMAGE_TAG} \
-t ${FRONTEND_IMAGE}:latest \
.
"""
}
}
stage('Build Backend') {
steps {
dir('server') { // server/Dockerfile
sh """
docker build \
-f Dockerfile \
-t ${BACKEND_IMAGE}:${IMAGE_TAG} \
-t ${BACKEND_IMAGE}:latest \
.
"""
}
}
}
}
}
stage('Push to Harbor') {
steps {
withCredentials([usernamePassword(
credentialsId: 'harbor-creds',
usernameVariable: 'HARBOR_USER',
passwordVariable: 'HARBOR_PASS'
)]) {
sh """
echo \$HARBOR_PASS | docker login ${HARBOR_URL} -u \$HARBOR_USER --password-stdin
docker push ${FRONTEND_IMAGE}:${IMAGE_TAG}
docker push ${FRONTEND_IMAGE}:latest
docker push ${BACKEND_IMAGE}:${IMAGE_TAG}
docker push ${BACKEND_IMAGE}:latest
"""
}
}
}
stage('Patch Image Tags') {
steps {
dir("${K8S_OVERLAY}") {
sh """
kustomize edit set image \
scrum-frontend=${FRONTEND_IMAGE}:${IMAGE_TAG} \
scrum-backend=${BACKEND_IMAGE}:${IMAGE_TAG}
"""
}
}
}
stage('Deploy to K8s') {
steps {
withKubeConfig([credentialsId: "${K8S_CRED_ID}"]) {
sh "kubectl apply -k ${K8S_OVERLAY}"
// MySQL uses Recreate strategy: old pod terminates (~30s) before
// new pod starts. Readiness probe initialDelaySeconds=30 + up to
// 10 retries × 5s = 80s. Total worst-case: ~110s → 300s is safe.
sh "kubectl rollout status deployment/mysql -n scrum-manager --timeout=300s"
// Backend initContainer sleeps 15s after MySQL TCP is up before
// starting the Node process. 300s covers slow-start scenarios.
sh "kubectl rollout status deployment/backend -n scrum-manager --timeout=300s"
sh "kubectl rollout status deployment/frontend -n scrum-manager --timeout=180s"
echo "All deployments rolled out."
}
}
}
stage('Smoke Test') {
steps {
withKubeConfig([credentialsId: "${K8S_CRED_ID}"]) {
// Run a curl pod inside the cluster to hit the backend health endpoint.
// Uses FQDN (backend.scrum-manager.svc.cluster.local) to be explicit.
sh """
kubectl run smoke-${BUILD_NUMBER} \
--image=curlimages/curl:8.5.0 \
--restart=Never \
--rm \
--attach \
--timeout=30s \
-n scrum-manager \
-- curl -sf --max-time 10 \
http://backend.scrum-manager.svc.cluster.local:3001/api/health \
&& echo "Health check PASSED" \
|| echo "Health check FAILED (non-blocking)"
"""
}
}
}
stage('Clean Up') {
steps {
sh """
docker rmi ${FRONTEND_IMAGE}:${IMAGE_TAG} || true
docker rmi ${FRONTEND_IMAGE}:latest || true
docker rmi ${BACKEND_IMAGE}:${IMAGE_TAG} || true
docker rmi ${BACKEND_IMAGE}:latest || true
"""
}
}
}
post {
success {
echo "✅ Build #${env.BUILD_NUMBER} deployed → http://scrum.local"
}
failure {
echo "❌ Pipeline failed. Check stage logs above."
}
always {
sh "docker logout ${HARBOR_URL} || true"
}
}
}