Add new infra architecture

This commit is contained in:
Urtzi Alfaro
2026-01-19 11:55:17 +01:00
parent 21d35ea92b
commit 35f164f0cd
311 changed files with 13241 additions and 3700 deletions

View File

@@ -0,0 +1,154 @@
# Tekton Detect Changed Services Task for Bakery-IA CI/CD
# This task identifies which services have changed in the repository
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: detect-changed-services
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: detect
spec:
workspaces:
- name: source
description: Source code workspace
params:
- name: base-ref
type: string
description: Base reference for comparison (default HEAD~1)
default: "HEAD~1"
results:
- name: changed-services
description: Comma-separated list of changed services
- name: changed-files-count
description: Number of files changed
steps:
- name: detect
image: alpine/git:2.43.0
script: |
#!/bin/sh
set -e
SOURCE_PATH="$(workspaces.source.path)"
BASE_REF="$(params.base-ref)"
cd "$SOURCE_PATH"
echo "============================================"
echo "Detect Changed Services"
echo "============================================"
echo "Base ref: $BASE_REF"
echo "============================================"
# Get list of changed files compared to base reference
echo ""
echo "Detecting changed files..."
# Try to get diff, fall back to listing all files if this is the first commit
CHANGED_FILES=$(git diff --name-only "$BASE_REF" HEAD 2>/dev/null || git ls-tree -r HEAD --name-only)
FILE_COUNT=$(echo "$CHANGED_FILES" | grep -c "." || echo "0")
echo "Found $FILE_COUNT changed files"
echo "$FILE_COUNT" > $(results.changed-files-count.path)
if [ "$FILE_COUNT" = "0" ]; then
echo "No files changed"
echo "none" > $(results.changed-services.path)
exit 0
fi
echo ""
echo "Changed files:"
echo "$CHANGED_FILES" | head -20
if [ "$FILE_COUNT" -gt 20 ]; then
echo "... and $((FILE_COUNT - 20)) more files"
fi
# Map files to services using simple shell (no bash arrays)
echo ""
echo "Mapping files to services..."
CHANGED_SERVICES=""
# Process each file
echo "$CHANGED_FILES" | while read -r file; do
if [ -z "$file" ]; then
continue
fi
# Check services directory
if echo "$file" | grep -q "^services/"; then
SERVICE=$(echo "$file" | cut -d'/' -f2)
if [ -n "$SERVICE" ] && ! echo "$CHANGED_SERVICES" | grep -q "$SERVICE"; then
if [ -z "$CHANGED_SERVICES" ]; then
CHANGED_SERVICES="$SERVICE"
else
CHANGED_SERVICES="$CHANGED_SERVICES,$SERVICE"
fi
echo "$CHANGED_SERVICES" > /tmp/services.txt
fi
fi
# Check frontend
if echo "$file" | grep -q "^frontend/"; then
if ! echo "$CHANGED_SERVICES" | grep -q "frontend"; then
if [ -z "$CHANGED_SERVICES" ]; then
CHANGED_SERVICES="frontend"
else
CHANGED_SERVICES="$CHANGED_SERVICES,frontend"
fi
echo "$CHANGED_SERVICES" > /tmp/services.txt
fi
fi
# Check gateway
if echo "$file" | grep -q "^gateway/"; then
if ! echo "$CHANGED_SERVICES" | grep -q "gateway"; then
if [ -z "$CHANGED_SERVICES" ]; then
CHANGED_SERVICES="gateway"
else
CHANGED_SERVICES="$CHANGED_SERVICES,gateway"
fi
echo "$CHANGED_SERVICES" > /tmp/services.txt
fi
fi
# Check infrastructure
if echo "$file" | grep -q "^infrastructure/"; then
if ! echo "$CHANGED_SERVICES" | grep -q "infrastructure"; then
if [ -z "$CHANGED_SERVICES" ]; then
CHANGED_SERVICES="infrastructure"
else
CHANGED_SERVICES="$CHANGED_SERVICES,infrastructure"
fi
echo "$CHANGED_SERVICES" > /tmp/services.txt
fi
fi
done
# Read the accumulated services
if [ -f /tmp/services.txt ]; then
CHANGED_SERVICES=$(cat /tmp/services.txt)
fi
echo ""
echo "============================================"
# Output result
if [ -z "$CHANGED_SERVICES" ]; then
echo "No service changes detected"
echo "none" > $(results.changed-services.path)
else
echo "Detected changes in services: $CHANGED_SERVICES"
echo "$CHANGED_SERVICES" > $(results.changed-services.path)
fi
echo "============================================"
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi

View File

@@ -0,0 +1,95 @@
# Tekton Git Clone Task for Bakery-IA CI/CD
# This task clones the source code repository
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: git-clone
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: source
spec:
workspaces:
- name: output
description: Workspace to clone the repository into
params:
- name: url
type: string
description: Repository URL to clone
- name: revision
type: string
description: Git revision to checkout
default: "main"
- name: depth
type: string
description: Git clone depth (0 for full history)
default: "1"
results:
- name: commit-sha
description: The commit SHA that was checked out
- name: commit-message
description: The commit message
steps:
- name: clone
image: alpine/git:2.43.0
script: |
#!/bin/sh
set -e
URL="$(params.url)"
REVISION="$(params.revision)"
DEPTH="$(params.depth)"
OUTPUT_PATH="$(workspaces.output.path)"
echo "============================================"
echo "Git Clone Task"
echo "============================================"
echo "URL: $URL"
echo "Revision: $REVISION"
echo "Depth: $DEPTH"
echo "============================================"
# Clone with depth for faster checkout
if [ "$DEPTH" = "0" ]; then
echo "Cloning full repository..."
git clone "$URL" "$OUTPUT_PATH"
else
echo "Cloning with depth $DEPTH..."
git clone --depth "$DEPTH" "$URL" "$OUTPUT_PATH"
fi
cd "$OUTPUT_PATH"
# Fetch the specific revision if needed
if [ "$REVISION" != "main" ] && [ "$REVISION" != "master" ]; then
echo "Fetching revision: $REVISION"
git fetch --depth 1 origin "$REVISION" 2>/dev/null || true
fi
# Checkout the revision
echo "Checking out: $REVISION"
git checkout "$REVISION" 2>/dev/null || git checkout "origin/$REVISION"
# Get commit info
COMMIT_SHA=$(git rev-parse HEAD)
COMMIT_MSG=$(git log -1 --pretty=format:"%s")
echo ""
echo "============================================"
echo "Clone Complete"
echo "============================================"
echo "Commit: $COMMIT_SHA"
echo "Message: $COMMIT_MSG"
echo "============================================"
# Write results
echo -n "$COMMIT_SHA" > $(results.commit-sha.path)
echo -n "$COMMIT_MSG" > $(results.commit-message.path)
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi

View File

@@ -0,0 +1,200 @@
# Tekton Kaniko Build Task for Bakery-IA CI/CD
# This task builds and pushes container images using Kaniko
# Supports building multiple services from a comma-separated list
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: kaniko-build
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: build
spec:
workspaces:
- name: source
description: Source code workspace
- name: docker-credentials
description: Docker registry credentials
params:
- name: services
type: string
description: Comma-separated list of services to build
- name: registry
type: string
description: Container registry URL
- name: git-revision
type: string
description: Git revision for image tag
default: "latest"
results:
- name: built-images
description: List of successfully built images
- name: build-status
description: Overall build status (success/failure)
steps:
# Step 1: Setup docker credentials
- name: setup-docker-config
image: alpine:3.18
script: |
#!/bin/sh
set -e
echo "Setting up Docker credentials..."
mkdir -p /kaniko/.docker
# Check if credentials secret is mounted
if [ -f "$(workspaces.docker-credentials.path)/config.json" ]; then
cp "$(workspaces.docker-credentials.path)/config.json" /kaniko/.docker/config.json
echo "Docker config copied from secret"
elif [ -f "$(workspaces.docker-credentials.path)/.dockerconfigjson" ]; then
cp "$(workspaces.docker-credentials.path)/.dockerconfigjson" /kaniko/.docker/config.json
echo "Docker config copied from .dockerconfigjson"
else
echo "Warning: No docker credentials found, builds may fail for private registries"
echo '{}' > /kaniko/.docker/config.json
fi
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker
resources:
limits:
cpu: 100m
memory: 64Mi
requests:
cpu: 50m
memory: 32Mi
# Step 2: Build each service iteratively
- name: build-services
image: gcr.io/kaniko-project/executor:v1.23.0
script: |
#!/busybox/sh
set -e
SERVICES="$(params.services)"
REGISTRY="$(params.registry)"
REVISION="$(params.git-revision)"
SOURCE_PATH="$(workspaces.source.path)"
BUILT_IMAGES=""
FAILED_SERVICES=""
echo "============================================"
echo "Starting build for services: $SERVICES"
echo "Registry: $REGISTRY"
echo "Tag: $REVISION"
echo "============================================"
# Skip if no services to build
if [ "$SERVICES" = "none" ] || [ -z "$SERVICES" ]; then
echo "No services to build, skipping..."
echo "none" > $(results.built-images.path)
echo "skipped" > $(results.build-status.path)
exit 0
fi
# Convert comma-separated list to space-separated
SERVICES_LIST=$(echo "$SERVICES" | tr ',' ' ')
for SERVICE in $SERVICES_LIST; do
# Trim whitespace
SERVICE=$(echo "$SERVICE" | tr -d ' ')
# Skip infrastructure changes (not buildable)
if [ "$SERVICE" = "infrastructure" ]; then
echo "Skipping infrastructure (not a buildable service)"
continue
fi
echo ""
echo "--------------------------------------------"
echo "Building service: $SERVICE"
echo "--------------------------------------------"
# Determine Dockerfile path based on service type
if [ "$SERVICE" = "frontend" ]; then
DOCKERFILE_PATH="$SOURCE_PATH/frontend/Dockerfile"
CONTEXT_PATH="$SOURCE_PATH/frontend"
elif [ "$SERVICE" = "gateway" ]; then
DOCKERFILE_PATH="$SOURCE_PATH/gateway/Dockerfile"
CONTEXT_PATH="$SOURCE_PATH/gateway"
else
DOCKERFILE_PATH="$SOURCE_PATH/services/$SERVICE/Dockerfile"
CONTEXT_PATH="$SOURCE_PATH"
fi
# Check if Dockerfile exists
if [ ! -f "$DOCKERFILE_PATH" ]; then
echo "Warning: Dockerfile not found at $DOCKERFILE_PATH, skipping $SERVICE"
FAILED_SERVICES="$FAILED_SERVICES $SERVICE"
continue
fi
IMAGE_NAME="$REGISTRY/bakery/$SERVICE:$REVISION"
IMAGE_NAME_LATEST="$REGISTRY/bakery/$SERVICE:latest"
echo "Dockerfile: $DOCKERFILE_PATH"
echo "Context: $CONTEXT_PATH"
echo "Image: $IMAGE_NAME"
# Run Kaniko build
/kaniko/executor \
--dockerfile="$DOCKERFILE_PATH" \
--context="$CONTEXT_PATH" \
--destination="$IMAGE_NAME" \
--destination="$IMAGE_NAME_LATEST" \
--cache=true \
--cache-ttl=24h \
--verbosity=info \
--snapshot-mode=redo \
--use-new-run
BUILD_EXIT_CODE=$?
if [ $BUILD_EXIT_CODE -eq 0 ]; then
echo "Successfully built and pushed: $IMAGE_NAME"
if [ -z "$BUILT_IMAGES" ]; then
BUILT_IMAGES="$IMAGE_NAME"
else
BUILT_IMAGES="$BUILT_IMAGES,$IMAGE_NAME"
fi
else
echo "Failed to build: $SERVICE (exit code: $BUILD_EXIT_CODE)"
FAILED_SERVICES="$FAILED_SERVICES $SERVICE"
fi
done
echo ""
echo "============================================"
echo "Build Summary"
echo "============================================"
echo "Built images: $BUILT_IMAGES"
echo "Failed services: $FAILED_SERVICES"
# Write results
if [ -z "$BUILT_IMAGES" ]; then
echo "none" > $(results.built-images.path)
else
echo "$BUILT_IMAGES" > $(results.built-images.path)
fi
if [ -n "$FAILED_SERVICES" ]; then
echo "partial" > $(results.build-status.path)
echo "Warning: Some services failed to build: $FAILED_SERVICES"
else
echo "success" > $(results.build-status.path)
fi
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker
securityContext:
runAsUser: 0
resources:
limits:
cpu: 2000m
memory: 4Gi
requests:
cpu: 500m
memory: 1Gi
volumes:
- name: docker-config
emptyDir: {}

View File

@@ -0,0 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- git-clone.yaml
- detect-changes.yaml
- run-tests.yaml
- kaniko-build.yaml
- update-gitops.yaml
- pipeline-summary.yaml
# Production deployment tasks
- verify-images.yaml
- pre-deploy-validation.yaml
- prod-deployment-summary.yaml

View File

@@ -0,0 +1,62 @@
# Tekton Pipeline Summary Task for Bakery-IA CI/CD
# This task runs at the end of the pipeline and provides a summary
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: pipeline-summary
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: summary
spec:
params:
- name: changed-services
type: string
description: List of changed services
default: "none"
- name: git-revision
type: string
description: Git revision that was built
default: "unknown"
steps:
- name: summary
image: alpine:3.18
script: |
#!/bin/sh
SERVICES="$(params.changed-services)"
REVISION="$(params.git-revision)"
echo ""
echo "============================================"
echo " Pipeline Execution Summary"
echo "============================================"
echo ""
echo "Git Revision: $REVISION"
echo "Changed Services: $SERVICES"
echo ""
echo "Timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo ""
echo "============================================"
echo ""
if [ "$SERVICES" = "none" ] || [ -z "$SERVICES" ]; then
echo "No services were changed in this commit."
echo "Pipeline completed without building any images."
else
echo "The following services were processed:"
echo "$SERVICES" | tr ',' '\n' | while read service; do
echo " - $service"
done
fi
echo ""
echo "============================================"
resources:
limits:
cpu: 100m
memory: 64Mi
requests:
cpu: 50m
memory: 32Mi

View File

@@ -0,0 +1,76 @@
# Task for pre-deployment validation
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: pre-deploy-validation
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: validation
spec:
workspaces:
- name: source
description: Source code workspace
params:
- name: services
type: string
description: Comma-separated list of services to validate
- name: environment
type: string
description: Target environment (staging/production)
default: "production"
results:
- name: validation-status
description: Status of validation (passed/failed)
steps:
- name: validate
image: registry.k8s.io/kustomize/kustomize:v5.3.0
script: |
#!/bin/sh
set -e
SOURCE_PATH="$(workspaces.source.path)"
SERVICES="$(params.services)"
ENVIRONMENT="$(params.environment)"
echo "============================================"
echo "Pre-Deployment Validation"
echo "============================================"
echo "Environment: $ENVIRONMENT"
echo "Services: $SERVICES"
echo "============================================"
cd "$SOURCE_PATH"
# Validate kustomization can be built
KUSTOMIZE_DIR="infrastructure/environments/$ENVIRONMENT"
if [ -d "$KUSTOMIZE_DIR" ]; then
echo ""
echo "Validating kustomization..."
if kustomize build "$KUSTOMIZE_DIR" > /dev/null 2>&1; then
echo " ✓ Kustomization is valid"
else
echo " ✗ Kustomization validation failed"
echo "failed" > $(results.validation-status.path)
exit 1
fi
fi
# Additional validation checks can be added here
# - Schema validation
# - Policy checks (OPA/Gatekeeper)
# - Security scanning
echo ""
echo "============================================"
echo "All validations passed"
echo "============================================"
echo "passed" > $(results.validation-status.path)
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi

View File

@@ -0,0 +1,57 @@
# Task for production deployment summary
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: prod-deployment-summary
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: summary
spec:
params:
- name: services
type: string
description: List of deployed services
- name: git-revision
type: string
description: Git revision that was deployed
- name: approver
type: string
description: Name of the approver
- name: approval-ticket
type: string
description: Approval ticket number
steps:
- name: summary
image: alpine:3.18
script: |
#!/bin/sh
echo ""
echo "============================================"
echo " Production Deployment Summary"
echo "============================================"
echo ""
echo "Git Revision: $(params.git-revision)"
echo "Services: $(params.services)"
echo "Approved By: $(params.approver)"
echo "Approval Ticket: $(params.approval-ticket)"
echo "Timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo ""
echo "============================================"
echo ""
echo "Deployment to production initiated."
echo "Flux CD will reconcile the changes."
echo ""
echo "Monitor deployment status with:"
echo " kubectl get kustomization -n flux-system"
echo " kubectl get pods -n bakery-ia"
echo ""
echo "============================================"
resources:
limits:
cpu: 100m
memory: 64Mi
requests:
cpu: 50m
memory: 32Mi

View File

@@ -0,0 +1,205 @@
# Tekton Test Task for Bakery-IA CI/CD
# This task runs unit tests and linting for changed services
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: run-tests
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: test
spec:
workspaces:
- name: source
description: Source code workspace
params:
- name: services
type: string
description: Comma-separated list of services to test
- name: skip-lint
type: string
description: Skip linting if "true"
default: "false"
- name: skip-tests
type: string
description: Skip tests if "true"
default: "false"
results:
- name: test-status
description: Overall test status (passed/failed/skipped)
- name: tested-services
description: List of services that were tested
- name: failed-services
description: List of services that failed tests
steps:
- name: run-tests
image: python:3.11-slim
script: |
#!/bin/bash
set -e
SOURCE_PATH="$(workspaces.source.path)"
SERVICES="$(params.services)"
SKIP_LINT="$(params.skip-lint)"
SKIP_TESTS="$(params.skip-tests)"
TESTED_SERVICES=""
FAILED_SERVICES=""
OVERALL_STATUS="passed"
cd "$SOURCE_PATH"
echo "============================================"
echo "Running Tests"
echo "============================================"
echo "Services: $SERVICES"
echo "Skip Lint: $SKIP_LINT"
echo "Skip Tests: $SKIP_TESTS"
echo "============================================"
# Skip if no services to test
if [ "$SERVICES" = "none" ] || [ -z "$SERVICES" ]; then
echo "No services to test, skipping..."
echo "skipped" > $(results.test-status.path)
echo "none" > $(results.tested-services.path)
echo "none" > $(results.failed-services.path)
exit 0
fi
# Install common test dependencies
echo ""
echo "Installing test dependencies..."
pip install --quiet pytest pytest-cov pytest-asyncio ruff mypy 2>/dev/null || true
# Convert comma-separated list to space-separated
SERVICES_LIST=$(echo "$SERVICES" | tr ',' ' ')
for SERVICE in $SERVICES_LIST; do
# Trim whitespace
SERVICE=$(echo "$SERVICE" | tr -d ' ')
# Skip infrastructure changes
if [ "$SERVICE" = "infrastructure" ]; then
echo "Skipping infrastructure (not testable)"
continue
fi
echo ""
echo "--------------------------------------------"
echo "Testing service: $SERVICE"
echo "--------------------------------------------"
# Determine service path
if [ "$SERVICE" = "frontend" ]; then
SERVICE_PATH="$SOURCE_PATH/frontend"
elif [ "$SERVICE" = "gateway" ]; then
SERVICE_PATH="$SOURCE_PATH/gateway"
else
SERVICE_PATH="$SOURCE_PATH/services/$SERVICE"
fi
# Check if service exists
if [ ! -d "$SERVICE_PATH" ]; then
echo "Warning: Service directory not found: $SERVICE_PATH"
continue
fi
cd "$SERVICE_PATH"
SERVICE_FAILED=false
# Install service-specific dependencies if requirements.txt exists
if [ -f "requirements.txt" ]; then
echo "Installing service dependencies..."
pip install --quiet -r requirements.txt 2>/dev/null || true
fi
# Run linting (ruff)
if [ "$SKIP_LINT" != "true" ]; then
echo ""
echo "Running linter (ruff)..."
if [ -d "app" ]; then
ruff check app/ --output-format=text 2>&1 || {
echo "Linting failed for $SERVICE"
SERVICE_FAILED=true
}
fi
fi
# Run tests
if [ "$SKIP_TESTS" != "true" ]; then
echo ""
echo "Running tests (pytest)..."
if [ -d "tests" ]; then
pytest tests/ -v --tb=short 2>&1 || {
echo "Tests failed for $SERVICE"
SERVICE_FAILED=true
}
elif [ -d "app/tests" ]; then
pytest app/tests/ -v --tb=short 2>&1 || {
echo "Tests failed for $SERVICE"
SERVICE_FAILED=true
}
else
echo "No tests directory found, skipping tests"
fi
fi
# Track results
if [ -z "$TESTED_SERVICES" ]; then
TESTED_SERVICES="$SERVICE"
else
TESTED_SERVICES="$TESTED_SERVICES,$SERVICE"
fi
if [ "$SERVICE_FAILED" = true ]; then
OVERALL_STATUS="failed"
if [ -z "$FAILED_SERVICES" ]; then
FAILED_SERVICES="$SERVICE"
else
FAILED_SERVICES="$FAILED_SERVICES,$SERVICE"
fi
fi
cd "$SOURCE_PATH"
done
echo ""
echo "============================================"
echo "Test Summary"
echo "============================================"
echo "Tested services: $TESTED_SERVICES"
echo "Failed services: $FAILED_SERVICES"
echo "Overall status: $OVERALL_STATUS"
# Write results
echo "$OVERALL_STATUS" > $(results.test-status.path)
if [ -z "$TESTED_SERVICES" ]; then
echo "none" > $(results.tested-services.path)
else
echo "$TESTED_SERVICES" > $(results.tested-services.path)
fi
if [ -z "$FAILED_SERVICES" ]; then
echo "none" > $(results.failed-services.path)
else
echo "$FAILED_SERVICES" > $(results.failed-services.path)
fi
# Exit with error if tests failed
if [ "$OVERALL_STATUS" = "failed" ]; then
echo ""
echo "ERROR: Some tests failed!"
exit 1
fi
echo ""
echo "All tests passed!"
resources:
limits:
cpu: 1000m
memory: 2Gi
requests:
cpu: 500m
memory: 1Gi

View File

@@ -0,0 +1,302 @@
# Tekton Update GitOps Manifests Task for Bakery-IA CI/CD
# This task updates Kubernetes manifests with new image tags using Kustomize
# It uses a safer approach than sed for updating image references
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: update-gitops
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: gitops
spec:
workspaces:
- name: source
description: Source code workspace with Git repository
- name: git-credentials
description: Git credentials for pushing changes
optional: true
params:
- name: services
type: string
description: Comma-separated list of services to update
- name: registry
type: string
description: Container registry URL
- name: git-revision
type: string
description: Git revision for image tag
- name: git-branch
type: string
description: Target branch for GitOps updates
default: "main"
- name: dry-run
type: string
description: If "true", only show what would be changed without committing
default: "false"
results:
- name: updated-services
description: List of services that were updated
- name: commit-sha
description: Git commit SHA of the update (empty if dry-run)
steps:
- name: update-manifests
# Use alpine with curl to install kustomize
image: alpine:3.19
script: |
#!/bin/sh
set -e
# Install kustomize
echo "Installing kustomize..."
wget -q "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" -O - | sh
mv kustomize /usr/local/bin/
echo "Kustomize version: $(kustomize version)"
SOURCE_PATH="$(workspaces.source.path)"
SERVICES="$(params.services)"
REGISTRY="$(params.registry)"
REVISION="$(params.git-revision)"
DRY_RUN="$(params.dry-run)"
UPDATED_SERVICES=""
cd "$SOURCE_PATH"
echo "============================================"
echo "GitOps Manifest Update"
echo "============================================"
echo "Services: $SERVICES"
echo "Registry: $REGISTRY"
echo "Revision: $REVISION"
echo "Dry Run: $DRY_RUN"
echo "============================================"
# Skip if no services to update
if [ "$SERVICES" = "none" ] || [ -z "$SERVICES" ]; then
echo "No services to update, skipping..."
echo "none" > $(results.updated-services.path)
echo "" > $(results.commit-sha.path)
exit 0
fi
# Define the kustomization directory
KUSTOMIZE_DIR="infrastructure/environments/prod"
# Check if kustomization.yaml exists, create if not
if [ ! -f "$KUSTOMIZE_DIR/kustomization.yaml" ]; then
echo "Creating kustomization.yaml in $KUSTOMIZE_DIR"
mkdir -p "$KUSTOMIZE_DIR"
printf '%s\n' \
"apiVersion: kustomize.config.k8s.io/v1beta1" \
"kind: Kustomization" \
"" \
"resources:" \
" - ../base" \
"" \
"images: []" \
> "$KUSTOMIZE_DIR/kustomization.yaml"
fi
# Convert comma-separated list to space-separated
SERVICES_LIST=$(echo "$SERVICES" | tr ',' ' ')
# Build the images section for kustomization
echo ""
echo "Updating image references..."
for SERVICE in $SERVICES_LIST; do
# Trim whitespace
SERVICE=$(echo "$SERVICE" | tr -d ' ')
# Skip infrastructure changes
if [ "$SERVICE" = "infrastructure" ]; then
echo "Skipping infrastructure (not a deployable service)"
continue
fi
echo "Processing: $SERVICE"
# Determine the image name based on service
NEW_IMAGE="$REGISTRY/bakery/$SERVICE:$REVISION"
# Use kustomize to set the image
# This is safer than sed as it understands the YAML structure
cd "$SOURCE_PATH/$KUSTOMIZE_DIR"
# Check if this service has a deployment
SERVICE_DEPLOYMENT=""
if [ "$SERVICE" = "frontend" ]; then
SERVICE_DEPLOYMENT="frontend"
elif [ "$SERVICE" = "gateway" ]; then
SERVICE_DEPLOYMENT="gateway"
else
SERVICE_DEPLOYMENT="$SERVICE-service"
fi
# Update the kustomization with the new image
# Using kustomize edit to safely modify the file
kustomize edit set image "bakery/$SERVICE=$NEW_IMAGE" 2>/dev/null || \
kustomize edit set image "$SERVICE=$NEW_IMAGE" 2>/dev/null || \
echo "Note: Could not set image via kustomize edit, will use alternative method"
# Track updated services
if [ -z "$UPDATED_SERVICES" ]; then
UPDATED_SERVICES="$SERVICE"
else
UPDATED_SERVICES="$UPDATED_SERVICES,$SERVICE"
fi
cd "$SOURCE_PATH"
done
# Alternative: Update images in kustomization.yaml directly if kustomize edit didn't work
# This creates/updates an images section in the kustomization
echo ""
echo "Ensuring image overrides in kustomization.yaml..."
# Create a patch file for image updates
IMAGES_FILE="$KUSTOMIZE_DIR/images.yaml"
printf '%s\n' \
"# Auto-generated by CI/CD pipeline" \
"# Commit: $REVISION" \
"# Updated: $(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
"images:" \
> "$IMAGES_FILE"
for SERVICE in $SERVICES_LIST; do
SERVICE=$(echo "$SERVICE" | tr -d ' ')
if [ "$SERVICE" != "infrastructure" ]; then
printf '%s\n' \
" - name: bakery/$SERVICE" \
" newName: $REGISTRY/bakery/$SERVICE" \
" newTag: \"$REVISION\"" \
>> "$IMAGES_FILE"
fi
done
echo ""
echo "Generated images.yaml:"
cat "$IMAGES_FILE"
# Validate the kustomization
echo ""
echo "Validating kustomization..."
cd "$SOURCE_PATH/$KUSTOMIZE_DIR"
if kustomize build . > /dev/null 2>&1; then
echo "Kustomization is valid"
else
echo "Warning: Kustomization validation failed, but continuing..."
fi
cd "$SOURCE_PATH"
# Write results
echo "$UPDATED_SERVICES" > $(results.updated-services.path)
if [ "$DRY_RUN" = "true" ]; then
echo ""
echo "============================================"
echo "DRY RUN - Changes not committed"
echo "============================================"
echo "Would update services: $UPDATED_SERVICES"
git diff --stat || true
echo "" > $(results.commit-sha.path)
exit 0
fi
echo ""
echo "Committing changes..."
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
- name: commit-and-push
image: alpine/git:2.43.0
script: |
#!/bin/sh
set -e
SOURCE_PATH="$(workspaces.source.path)"
SERVICES="$(params.services)"
REVISION="$(params.git-revision)"
BRANCH="$(params.git-branch)"
DRY_RUN="$(params.dry-run)"
cd "$SOURCE_PATH"
if [ "$DRY_RUN" = "true" ]; then
echo "Dry run mode - skipping commit"
echo "" > $(results.commit-sha.path)
exit 0
fi
if [ "$SERVICES" = "none" ] || [ -z "$SERVICES" ]; then
echo "No services to commit"
echo "" > $(results.commit-sha.path)
exit 0
fi
# Check if there are changes to commit
if git diff --quiet && git diff --cached --quiet; then
echo "No changes to commit"
echo "" > $(results.commit-sha.path)
exit 0
fi
# Configure git
git config --global user.name "bakery-ia-ci"
git config --global user.email "ci@bakery-ia.local"
git config --global --add safe.directory "$SOURCE_PATH"
# Setup git credentials if provided
if [ -d "$(workspaces.git-credentials.path)" ]; then
if [ -f "$(workspaces.git-credentials.path)/username" ] && [ -f "$(workspaces.git-credentials.path)/password" ]; then
GIT_USER=$(cat "$(workspaces.git-credentials.path)/username")
GIT_PASS=$(cat "$(workspaces.git-credentials.path)/password")
# Get the remote URL and inject credentials
REMOTE_URL=$(git remote get-url origin)
# Handle both http and https
if echo "$REMOTE_URL" | grep -q "^http"; then
REMOTE_URL=$(echo "$REMOTE_URL" | sed "s|://|://$GIT_USER:$GIT_PASS@|")
git remote set-url origin "$REMOTE_URL"
fi
fi
fi
# Stage changes
git add -A
# Create commit with detailed message
COMMIT_MSG=$(printf 'ci: Update image tags to %s\n\nServices updated: %s\n\nThis commit was automatically generated by the CI/CD pipeline.\nPipeline run triggered by commit: %s' "$REVISION" "$SERVICES" "$REVISION")
git commit -m "$COMMIT_MSG"
# Get the commit SHA
COMMIT_SHA=$(git rev-parse HEAD)
echo "$COMMIT_SHA" > $(results.commit-sha.path)
echo "Created commit: $COMMIT_SHA"
# Push changes
echo "Pushing to origin/$BRANCH..."
git push origin HEAD:"$BRANCH"
echo ""
echo "============================================"
echo "GitOps Update Complete"
echo "============================================"
echo "Commit: $COMMIT_SHA"
echo "Branch: $BRANCH"
echo "Services: $SERVICES"
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi

View File

@@ -0,0 +1,91 @@
# Task to verify images exist in the registry before deploying
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: verify-images
namespace: tekton-pipelines
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/component: validation
spec:
params:
- name: services
type: string
description: Comma-separated list of services to verify
- name: registry
type: string
description: Container registry URL
- name: git-revision
type: string
description: Git revision/tag to verify
results:
- name: verification-status
description: Status of image verification (success/failed)
- name: missing-images
description: List of images that were not found
steps:
- name: verify
image: gcr.io/go-containerregistry/crane:latest
script: |
#!/bin/sh
set -e
SERVICES="$(params.services)"
REGISTRY="$(params.registry)"
REVISION="$(params.git-revision)"
MISSING=""
echo "============================================"
echo "Verifying Images in Registry"
echo "============================================"
echo "Registry: $REGISTRY"
echo "Revision: $REVISION"
echo "Services: $SERVICES"
echo "============================================"
# Convert comma-separated list to space-separated
SERVICES_LIST=$(echo "$SERVICES" | tr ',' ' ')
for SERVICE in $SERVICES_LIST; do
SERVICE=$(echo "$SERVICE" | tr -d ' ')
if [ "$SERVICE" = "infrastructure" ]; then
continue
fi
IMAGE="$REGISTRY/bakery/$SERVICE:$REVISION"
echo ""
echo "Checking: $IMAGE"
if crane manifest "$IMAGE" > /dev/null 2>&1; then
echo " ✓ Found"
else
echo " ✗ NOT FOUND"
if [ -z "$MISSING" ]; then
MISSING="$SERVICE"
else
MISSING="$MISSING,$SERVICE"
fi
fi
done
echo ""
echo "============================================"
if [ -n "$MISSING" ]; then
echo "ERROR: Missing images: $MISSING"
echo "failed" > $(results.verification-status.path)
echo "$MISSING" > $(results.missing-images.path)
exit 1
fi
echo "All images verified successfully"
echo "success" > $(results.verification-status.path)
echo "none" > $(results.missing-images.path)
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 100m
memory: 64Mi