Add new infra architecture
This commit is contained in:
293
infrastructure/cicd/README.md
Normal file
293
infrastructure/cicd/README.md
Normal file
@@ -0,0 +1,293 @@
|
||||
# Bakery-IA CI/CD Implementation
|
||||
|
||||
This directory contains the configuration for the production-grade CI/CD system for Bakery-IA using Gitea, Tekton, and Flux CD.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Developer] -->|Push Code| B[Gitea]
|
||||
B -->|Webhook| C[Tekton Pipelines]
|
||||
C -->|Build/Test| D[Gitea Registry]
|
||||
D -->|New Image| E[Flux CD]
|
||||
E -->|kubectl apply| F[MicroK8s Cluster]
|
||||
F -->|Metrics| G[SigNoz]
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
infrastructure/ci-cd/
|
||||
├── gitea/ # Gitea configuration (Git server + registry)
|
||||
│ └── values.yaml # Helm values for Gitea (ingress now in main config)
|
||||
├── tekton/ # Tekton CI/CD pipeline configuration
|
||||
│ ├── tasks/ # Individual pipeline tasks
|
||||
│ │ ├── git-clone.yaml
|
||||
│ │ ├── detect-changes.yaml
|
||||
│ │ ├── kaniko-build.yaml
|
||||
│ │ └── update-gitops.yaml
|
||||
│ ├── pipelines/ # Pipeline definitions
|
||||
│ │ └── ci-pipeline.yaml
|
||||
│ └── triggers/ # Webhook trigger configuration
|
||||
│ ├── trigger-template.yaml
|
||||
│ ├── trigger-binding.yaml
|
||||
│ ├── event-listener.yaml
|
||||
│ └── gitlab-interceptor.yaml
|
||||
├── flux/ # Flux CD GitOps configuration
|
||||
│ ├── git-repository.yaml # Git repository source
|
||||
│ └── kustomization.yaml # Deployment kustomization
|
||||
├── monitoring/ # Monitoring configuration
|
||||
│ └── otel-collector.yaml # OpenTelemetry collector
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Deployment Instructions
|
||||
|
||||
### Phase 1: Infrastructure Setup
|
||||
|
||||
1. **Deploy Gitea**:
|
||||
```bash
|
||||
# Add Helm repo
|
||||
microk8s helm repo add gitea https://dl.gitea.io/charts
|
||||
|
||||
# Create namespace
|
||||
microk8s kubectl create namespace gitea
|
||||
|
||||
# Install Gitea
|
||||
microk8s helm install gitea gitea/gitea \
|
||||
-n gitea \
|
||||
-f infrastructure/ci-cd/gitea/values.yaml
|
||||
|
||||
# Note: Gitea ingress is now included in the main ingress configuration
|
||||
# No separate ingress needs to be applied
|
||||
```
|
||||
|
||||
2. **Deploy Tekton**:
|
||||
```bash
|
||||
# Create namespace
|
||||
microk8s kubectl create namespace tekton-pipelines
|
||||
|
||||
# Install Tekton Pipelines
|
||||
microk8s kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
|
||||
|
||||
# Install Tekton Triggers
|
||||
microk8s kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml
|
||||
|
||||
# Apply Tekton configurations
|
||||
microk8s kubectl apply -f infrastructure/ci-cd/tekton/tasks/
|
||||
microk8s kubectl apply -f infrastructure/ci-cd/tekton/pipelines/
|
||||
microk8s kubectl apply -f infrastructure/ci-cd/tekton/triggers/
|
||||
```
|
||||
|
||||
3. **Deploy Flux CD** (already enabled in MicroK8s):
|
||||
```bash
|
||||
# Verify Flux installation
|
||||
microk8s kubectl get pods -n flux-system
|
||||
|
||||
# Apply Flux configurations using kustomize
|
||||
microk8s kubectl apply -k infrastructure/ci-cd/flux/
|
||||
```
|
||||
|
||||
### Phase 2: Configuration
|
||||
|
||||
1. **Set up Gitea webhook**:
|
||||
- Go to your Gitea repository settings
|
||||
- Add webhook with URL: `http://tekton-triggers.tekton-pipelines.svc.cluster.local:8080`
|
||||
- Use the secret from `gitea-webhook-secret`
|
||||
|
||||
2. **Configure registry credentials**:
|
||||
```bash
|
||||
# Create registry credentials secret
|
||||
microk8s kubectl create secret docker-registry gitea-registry-credentials \
|
||||
-n tekton-pipelines \
|
||||
--docker-server=gitea.bakery-ia.local:5000 \
|
||||
--docker-username=your-username \
|
||||
--docker-password=your-password
|
||||
```
|
||||
|
||||
3. **Configure Git credentials for Flux**:
|
||||
```bash
|
||||
# Create Git credentials secret
|
||||
microk8s kubectl create secret generic gitea-credentials \
|
||||
-n flux-system \
|
||||
--from-literal=username=your-username \
|
||||
--from-literal=password=your-password
|
||||
```
|
||||
|
||||
### Phase 3: Monitoring
|
||||
|
||||
```bash
|
||||
# Apply OpenTelemetry configuration
|
||||
microk8s kubectl apply -f infrastructure/ci-cd/monitoring/otel-collector.yaml
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Triggering a Pipeline
|
||||
|
||||
1. **Manual trigger**:
|
||||
```bash
|
||||
# Create a PipelineRun manually
|
||||
microk8s kubectl create -f - <<EOF
|
||||
apiVersion: tekton.dev/v1beta1
|
||||
kind: PipelineRun
|
||||
metadata:
|
||||
name: manual-ci-run
|
||||
namespace: tekton-pipelines
|
||||
spec:
|
||||
pipelineRef:
|
||||
name: bakery-ia-ci
|
||||
workspaces:
|
||||
- name: shared-workspace
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
- name: docker-credentials
|
||||
secret:
|
||||
secretName: gitea-registry-credentials
|
||||
params:
|
||||
- name: git-url
|
||||
value: "http://gitea.bakery-ia.local/bakery/bakery-ia.git"
|
||||
- name: git-revision
|
||||
value: "main"
|
||||
EOF
|
||||
```
|
||||
|
||||
2. **Automatic trigger**: Push code to the repository and the webhook will trigger the pipeline automatically.
|
||||
|
||||
### Monitoring Pipeline Runs
|
||||
|
||||
```bash
|
||||
# List all PipelineRuns
|
||||
microk8s kubectl get pipelineruns -n tekton-pipelines
|
||||
|
||||
# View logs for a specific PipelineRun
|
||||
microk8s kubectl logs -n tekton-pipelines <pipelinerun-pod> -c <step-name>
|
||||
|
||||
# View Tekton dashboard
|
||||
microk8s kubectl port-forward -n tekton-pipelines svc/tekton-dashboard 9097:9097
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Pipeline not triggering**:
|
||||
- Check Gitea webhook logs
|
||||
- Verify EventListener pods are running
|
||||
- Check TriggerBinding configuration
|
||||
|
||||
2. **Build failures**:
|
||||
- Check Kaniko logs for build errors
|
||||
- Verify Dockerfile paths are correct
|
||||
- Ensure registry credentials are valid
|
||||
|
||||
3. **Flux not applying changes**:
|
||||
- Check GitRepository status
|
||||
- Verify Kustomization reconciliation
|
||||
- Check Flux logs for errors
|
||||
|
||||
### Debugging Commands
|
||||
|
||||
```bash
|
||||
# Check Tekton controller logs
|
||||
microk8s kubectl logs -n tekton-pipelines -l app=tekton-pipelines-controller
|
||||
|
||||
# Check Flux reconciliation
|
||||
microk8s kubectl get kustomizations -n flux-system -o yaml
|
||||
|
||||
# Check Gitea webhook delivery
|
||||
microk8s kubectl logs -n tekton-pipelines -l app=tekton-triggers-controller
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Secrets Management**:
|
||||
- Use Kubernetes secrets for sensitive data
|
||||
- Rotate credentials regularly
|
||||
- Use RBAC for namespace isolation
|
||||
|
||||
2. **Network Security**:
|
||||
- Configure network policies
|
||||
- Use internal DNS names
|
||||
- Restrict ingress access
|
||||
|
||||
3. **Registry Security**:
|
||||
- Enable image scanning
|
||||
- Use image signing
|
||||
- Implement cleanup policies
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Upgrading Components
|
||||
|
||||
```bash
|
||||
# Upgrade Tekton
|
||||
microk8s kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
|
||||
|
||||
# Upgrade Flux
|
||||
microk8s helm upgrade fluxcd fluxcd/flux2 -n flux-system
|
||||
|
||||
# Upgrade Gitea
|
||||
microk8s helm upgrade gitea gitea/gitea -n gitea -f infrastructure/ci-cd/gitea/values.yaml
|
||||
```
|
||||
|
||||
### Backup Procedures
|
||||
|
||||
```bash
|
||||
# Backup Gitea
|
||||
microk8s kubectl exec -n gitea gitea-0 -- gitea dump -c /data/gitea/conf/app.ini
|
||||
|
||||
# Backup Flux configurations
|
||||
microk8s kubectl get all -n flux-system -o yaml > flux-backup.yaml
|
||||
|
||||
# Backup Tekton configurations
|
||||
microk8s kubectl get all -n tekton-pipelines -o yaml > tekton-backup.yaml
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
1. **Resource Management**:
|
||||
- Set appropriate resource limits
|
||||
- Limit concurrent builds
|
||||
- Use node selectors for build pods
|
||||
|
||||
2. **Caching**:
|
||||
- Configure Kaniko cache
|
||||
- Use persistent volumes for dependencies
|
||||
- Cache Docker layers
|
||||
|
||||
3. **Parallelization**:
|
||||
- Build independent services in parallel
|
||||
- Use matrix builds for different architectures
|
||||
- Optimize task dependencies
|
||||
|
||||
## Integration with Existing System
|
||||
|
||||
The CI/CD system integrates with:
|
||||
- **SigNoz**: For monitoring and observability
|
||||
- **MicroK8s**: For cluster management
|
||||
- **Existing Kubernetes manifests**: In `infrastructure/kubernetes/`
|
||||
- **Current services**: All 19 microservices in `services/`
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. **Phase 1**: Set up infrastructure (Gitea, Tekton, Flux)
|
||||
2. **Phase 2**: Configure pipelines and triggers
|
||||
3. **Phase 3**: Test with non-critical services
|
||||
4. **Phase 4**: Gradual rollout to all services
|
||||
5. **Phase 5**: Decommission old deployment methods
|
||||
|
||||
## Support
|
||||
|
||||
For issues with the CI/CD system:
|
||||
- Check logs and monitoring first
|
||||
- Review the troubleshooting section
|
||||
- Consult the original implementation plan
|
||||
- Refer to component documentation:
|
||||
- [Tekton Documentation](https://tekton.dev/docs/)
|
||||
- [Flux CD Documentation](https://fluxcd.io/docs/)
|
||||
- [Gitea Documentation](https://docs.gitea.io/)
|
||||
76
infrastructure/cicd/flux/flux-kustomization.yaml
Normal file
76
infrastructure/cicd/flux/flux-kustomization.yaml
Normal file
@@ -0,0 +1,76 @@
|
||||
# Flux Kustomization for Bakery-IA Production Deployment
|
||||
# This resource tells Flux how to deploy the application
|
||||
#
|
||||
# Prerequisites:
|
||||
# 1. Flux CD must be installed: flux install
|
||||
# 2. GitRepository 'bakery-ia' must be created and ready
|
||||
# 3. Secret 'gitea-credentials' must exist in flux-system namespace
|
||||
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: bakery-ia-prod
|
||||
namespace: flux-system
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia
|
||||
app.kubernetes.io/component: flux
|
||||
spec:
|
||||
# Wait for GitRepository to be ready before reconciling
|
||||
dependsOn: []
|
||||
interval: 5m
|
||||
path: ./infrastructure/environments/prod
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: bakery-ia
|
||||
targetNamespace: bakery-ia
|
||||
timeout: 10m
|
||||
retryInterval: 1m
|
||||
wait: true
|
||||
# Health checks for critical services
|
||||
healthChecks:
|
||||
# Core Infrastructure
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: gateway
|
||||
namespace: bakery-ia
|
||||
# Authentication & Authorization
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: auth-service
|
||||
namespace: bakery-ia
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: tenant-service
|
||||
namespace: bakery-ia
|
||||
# Core Business Services
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: inventory-service
|
||||
namespace: bakery-ia
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: orders-service
|
||||
namespace: bakery-ia
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: pos-service
|
||||
namespace: bakery-ia
|
||||
# Data Services
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: forecasting-service
|
||||
namespace: bakery-ia
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: notification-service
|
||||
namespace: bakery-ia
|
||||
# Post-build variable substitution
|
||||
postBuild:
|
||||
substituteFrom:
|
||||
- kind: ConfigMap
|
||||
name: bakery-ia-config
|
||||
optional: true
|
||||
- kind: Secret
|
||||
name: bakery-ia-secrets
|
||||
optional: true
|
||||
16
infrastructure/cicd/flux/git-repository.yaml
Normal file
16
infrastructure/cicd/flux/git-repository.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Flux GitRepository for Bakery-IA
|
||||
# This resource tells Flux where to find the Git repository
|
||||
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: bakery-ia
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
url: http://gitea.bakery-ia.local/bakery/bakery-ia.git
|
||||
ref:
|
||||
branch: main
|
||||
secretRef:
|
||||
name: gitea-credentials
|
||||
timeout: 60s
|
||||
25
infrastructure/cicd/flux/kustomization.yaml
Normal file
25
infrastructure/cicd/flux/kustomization.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
# Kustomize build configuration for Flux resources
|
||||
# This file is used to build and apply the Flux resources
|
||||
#
|
||||
# IMPORTANT: Apply resources in this order:
|
||||
# 1. Install Flux CD first: flux install
|
||||
# 2. Apply this kustomization: kubectl apply -k infrastructure/cicd/flux/
|
||||
#
|
||||
# The GitRepository must be ready before the Flux Kustomization can reconcile.
|
||||
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
# Resources to apply in order (namespace and secrets first, then sources, then kustomizations)
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- git-repository.yaml
|
||||
- flux-kustomization.yaml
|
||||
|
||||
# Common labels for all resources
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: flux
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
|
||||
# Note: Do NOT set namespace here as resources already have explicit namespaces
|
||||
15
infrastructure/cicd/flux/namespace.yaml
Normal file
15
infrastructure/cicd/flux/namespace.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
# Flux System Namespace
|
||||
# This namespace is required for Flux CD components
|
||||
# It should be created before any Flux resources are applied
|
||||
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: flux-system
|
||||
labels:
|
||||
app.kubernetes.io/name: flux
|
||||
app.kubernetes.io/component: system
|
||||
kubernetes.io/metadata.name: flux-system
|
||||
pod-security.kubernetes.io/enforce: restricted
|
||||
pod-security.kubernetes.io/audit: restricted
|
||||
pod-security.kubernetes.io/warn: restricted
|
||||
44
infrastructure/cicd/gitea/ingress.yaml.disabled
Normal file
44
infrastructure/cicd/gitea/ingress.yaml.disabled
Normal file
@@ -0,0 +1,44 @@
|
||||
# Gitea Ingress Configuration
|
||||
# Routes external traffic to Gitea service for web UI and Git HTTP access
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Gitea must be deployed in the 'gitea' namespace
|
||||
# - Ingress controller must be installed (nginx, traefik, etc.)
|
||||
# - For HTTPS: cert-manager with a ClusterIssuer named 'letsencrypt-prod' or 'local-ca-issuer'
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: gitea-ingress
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/component: ingress
|
||||
app.kubernetes.io/part-of: bakery-ia-cicd
|
||||
annotations:
|
||||
# For nginx ingress controller
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||
# For traefik ingress controller
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
|
||||
# For TLS with cert-manager (uncomment for HTTPS)
|
||||
# cert-manager.io/cluster-issuer: "local-ca-issuer"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
# Uncomment for HTTPS
|
||||
# tls:
|
||||
# - hosts:
|
||||
# - gitea.bakery-ia.local
|
||||
# secretName: gitea-tls
|
||||
rules:
|
||||
- host: gitea.bakery-ia.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: gitea-http
|
||||
port:
|
||||
number: 3000
|
||||
48
infrastructure/cicd/gitea/setup-admin-secret.sh
Executable file
48
infrastructure/cicd/gitea/setup-admin-secret.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
# Setup Gitea Admin Secret
|
||||
#
|
||||
# This script creates the Kubernetes secret required for Gitea admin credentials.
|
||||
# Run this BEFORE installing Gitea with Helm.
|
||||
#
|
||||
# Usage:
|
||||
# ./setup-admin-secret.sh [password]
|
||||
#
|
||||
# If password is not provided, a random one will be generated.
|
||||
|
||||
set -e
|
||||
|
||||
KUBECTL="kubectl"
|
||||
NAMESPACE="gitea"
|
||||
|
||||
# Check if running in microk8s
|
||||
if command -v microk8s &> /dev/null; then
|
||||
KUBECTL="microk8s kubectl"
|
||||
fi
|
||||
|
||||
# Get or generate password
|
||||
if [ -n "$1" ]; then
|
||||
ADMIN_PASSWORD="$1"
|
||||
else
|
||||
ADMIN_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | head -c 20)
|
||||
echo "Generated admin password: $ADMIN_PASSWORD"
|
||||
fi
|
||||
|
||||
# Create namespace if it doesn't exist
|
||||
$KUBECTL create namespace "$NAMESPACE" --dry-run=client -o yaml | $KUBECTL apply -f -
|
||||
|
||||
# Create the secret
|
||||
$KUBECTL create secret generic gitea-admin-secret \
|
||||
--namespace "$NAMESPACE" \
|
||||
--from-literal=username=bakery-admin \
|
||||
--from-literal=password="$ADMIN_PASSWORD" \
|
||||
--dry-run=client -o yaml | $KUBECTL apply -f -
|
||||
|
||||
echo ""
|
||||
echo "Gitea admin secret created successfully!"
|
||||
echo ""
|
||||
echo "Admin credentials:"
|
||||
echo " Username: bakery-admin"
|
||||
echo " Password: $ADMIN_PASSWORD"
|
||||
echo ""
|
||||
echo "Now install Gitea with:"
|
||||
echo " helm install gitea gitea/gitea -n gitea -f infrastructure/cicd/gitea/values.yaml"
|
||||
83
infrastructure/cicd/gitea/values.yaml
Normal file
83
infrastructure/cicd/gitea/values.yaml
Normal file
@@ -0,0 +1,83 @@
|
||||
# Gitea Helm values configuration for Bakery-IA CI/CD
|
||||
# This configuration sets up Gitea with registry support and appropriate storage
|
||||
#
|
||||
# Installation:
|
||||
# helm repo add gitea https://dl.gitea.io/charts
|
||||
# kubectl create namespace gitea
|
||||
# helm install gitea gitea/gitea -n gitea -f infrastructure/cicd/gitea/values.yaml
|
||||
#
|
||||
# NOTE: The namespace is determined by the -n flag during helm install, not in this file.
|
||||
|
||||
service:
|
||||
http:
|
||||
type: ClusterIP
|
||||
port: 3000
|
||||
ssh:
|
||||
type: ClusterIP
|
||||
port: 2222
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 10Gi
|
||||
# Use standard storage class (works with Kind's default provisioner)
|
||||
# For microk8s: storageClass: "microk8s-hostpath"
|
||||
# For Kind: leave empty or use "standard"
|
||||
storageClass: ""
|
||||
|
||||
gitea:
|
||||
admin:
|
||||
username: bakery-admin
|
||||
# IMPORTANT: Override this with --set gitea.admin.password=<secure-password>
|
||||
# or use existingSecret
|
||||
password: ""
|
||||
email: admin@bakery-ia.local
|
||||
existingSecret: gitea-admin-secret
|
||||
config:
|
||||
server:
|
||||
DOMAIN: gitea.bakery-ia.local
|
||||
SSH_DOMAIN: gitea.bakery-ia.local
|
||||
# Use HTTP internally; TLS termination happens at ingress
|
||||
ROOT_URL: http://gitea.bakery-ia.local
|
||||
HTTP_PORT: 3000
|
||||
# For external HTTPS access via ingress, set:
|
||||
# ROOT_URL: https://gitea.bakery-ia.local
|
||||
repository:
|
||||
ENABLE_PUSH_CREATE_USER: true
|
||||
ENABLE_PUSH_CREATE_ORG: true
|
||||
packages:
|
||||
ENABLED: true
|
||||
webhook:
|
||||
ALLOWED_HOST_LIST: "*"
|
||||
# Allow internal cluster URLs for Tekton EventListener
|
||||
SKIP_TLS_VERIFY: true
|
||||
service:
|
||||
DISABLE_REGISTRATION: false
|
||||
REQUIRE_SIGNIN_VIEW: false
|
||||
|
||||
# Use embedded SQLite for simpler local development
|
||||
# For production, enable postgresql
|
||||
postgresql:
|
||||
enabled: false
|
||||
|
||||
# Use embedded in-memory cache for local dev
|
||||
redis-cluster:
|
||||
enabled: false
|
||||
|
||||
# Resource configuration for local development
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
|
||||
# Init containers timeout
|
||||
initContainers:
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
10
infrastructure/cicd/kustomization.yaml
Normal file
10
infrastructure/cicd/kustomization.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- tekton/
|
||||
- ../../namespaces/flux-system.yaml
|
||||
|
||||
# Gitea is managed via Helm, so we don't include it directly here
|
||||
# The Gitea Helm chart is deployed separately and referenced in the ingress
|
||||
# Flux configuration is a Flux Kustomization resource, not a kustomize config
|
||||
222
infrastructure/cicd/tekton/cleanup/cleanup.yaml
Normal file
222
infrastructure/cicd/tekton/cleanup/cleanup.yaml
Normal file
@@ -0,0 +1,222 @@
|
||||
# Workspace and PipelineRun Cleanup for Bakery-IA CI/CD
|
||||
# This CronJob cleans up old PipelineRuns and PVCs to prevent storage exhaustion
|
||||
|
||||
---
|
||||
# ServiceAccount for cleanup job
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: tekton-cleanup-sa
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: cleanup
|
||||
|
||||
---
|
||||
# ClusterRole for cleanup operations
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: tekton-cleanup-role
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: cleanup
|
||||
rules:
|
||||
- apiGroups: ["tekton.dev"]
|
||||
resources: ["pipelineruns", "taskruns"]
|
||||
verbs: ["get", "list", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["get", "list", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "list", "delete"]
|
||||
|
||||
---
|
||||
# ClusterRoleBinding for cleanup
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: tekton-cleanup-binding
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: cleanup
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: tekton-cleanup-sa
|
||||
namespace: tekton-pipelines
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: tekton-cleanup-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
# CronJob to clean up old PipelineRuns
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: tekton-pipelinerun-cleanup
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: cleanup
|
||||
spec:
|
||||
# Run every 6 hours
|
||||
schedule: "0 */6 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 3600
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: cleanup
|
||||
spec:
|
||||
serviceAccountName: tekton-cleanup-sa
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: cleanup
|
||||
image: bitnami/kubectl:latest
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "============================================"
|
||||
echo "Tekton Cleanup Job"
|
||||
echo "Timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
echo "============================================"
|
||||
|
||||
# Configuration
|
||||
NAMESPACE="tekton-pipelines"
|
||||
MAX_AGE_HOURS=24
|
||||
KEEP_RECENT=10
|
||||
|
||||
echo ""
|
||||
echo "Configuration:"
|
||||
echo " Namespace: $NAMESPACE"
|
||||
echo " Max Age: ${MAX_AGE_HOURS} hours"
|
||||
echo " Keep Recent: $KEEP_RECENT"
|
||||
echo ""
|
||||
|
||||
# Get current timestamp
|
||||
CURRENT_TIME=$(date +%s)
|
||||
|
||||
# Clean up completed PipelineRuns older than MAX_AGE_HOURS
|
||||
echo "Cleaning up old PipelineRuns..."
|
||||
|
||||
# Get all completed PipelineRuns
|
||||
COMPLETED_RUNS=$(kubectl get pipelineruns -n "$NAMESPACE" \
|
||||
--no-headers \
|
||||
-o custom-columns=NAME:.metadata.name,STATUS:.status.conditions[0].reason,AGE:.metadata.creationTimestamp \
|
||||
2>/dev/null | grep -E "Succeeded|Failed" || true)
|
||||
|
||||
DELETED_COUNT=0
|
||||
|
||||
echo "$COMPLETED_RUNS" | while read -r line; do
|
||||
if [ -z "$line" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
RUN_NAME=$(echo "$line" | awk '{print $1}')
|
||||
RUN_TIME=$(echo "$line" | awk '{print $3}')
|
||||
|
||||
if [ -z "$RUN_NAME" ] || [ -z "$RUN_TIME" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Convert timestamp to seconds
|
||||
RUN_TIMESTAMP=$(date -d "$RUN_TIME" +%s 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$RUN_TIMESTAMP" = "0" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Calculate age in hours
|
||||
AGE_SECONDS=$((CURRENT_TIME - RUN_TIMESTAMP))
|
||||
AGE_HOURS=$((AGE_SECONDS / 3600))
|
||||
|
||||
if [ "$AGE_HOURS" -gt "$MAX_AGE_HOURS" ]; then
|
||||
echo "Deleting PipelineRun: $RUN_NAME (age: ${AGE_HOURS}h)"
|
||||
kubectl delete pipelinerun "$RUN_NAME" -n "$NAMESPACE" --ignore-not-found=true
|
||||
DELETED_COUNT=$((DELETED_COUNT + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Deleted $DELETED_COUNT old PipelineRuns"
|
||||
|
||||
# Clean up orphaned PVCs (PVCs without associated PipelineRuns)
|
||||
echo ""
|
||||
echo "Cleaning up orphaned PVCs..."
|
||||
|
||||
ORPHANED_PVCS=$(kubectl get pvc -n "$NAMESPACE" \
|
||||
-l tekton.dev/pipelineRun \
|
||||
--no-headers \
|
||||
-o custom-columns=NAME:.metadata.name,PIPELINERUN:.metadata.labels.tekton\\.dev/pipelineRun \
|
||||
2>/dev/null || true)
|
||||
|
||||
echo "$ORPHANED_PVCS" | while read -r line; do
|
||||
if [ -z "$line" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
PVC_NAME=$(echo "$line" | awk '{print $1}')
|
||||
PR_NAME=$(echo "$line" | awk '{print $2}')
|
||||
|
||||
if [ -z "$PVC_NAME" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if associated PipelineRun exists
|
||||
if ! kubectl get pipelinerun "$PR_NAME" -n "$NAMESPACE" > /dev/null 2>&1; then
|
||||
echo "Deleting orphaned PVC: $PVC_NAME (PipelineRun $PR_NAME not found)"
|
||||
kubectl delete pvc "$PVC_NAME" -n "$NAMESPACE" --ignore-not-found=true
|
||||
fi
|
||||
done
|
||||
|
||||
# Clean up completed/failed pods older than 1 hour
|
||||
echo ""
|
||||
echo "Cleaning up old completed pods..."
|
||||
|
||||
kubectl delete pods -n "$NAMESPACE" \
|
||||
--field-selector=status.phase=Succeeded \
|
||||
--ignore-not-found=true 2>/dev/null || true
|
||||
|
||||
kubectl delete pods -n "$NAMESPACE" \
|
||||
--field-selector=status.phase=Failed \
|
||||
--ignore-not-found=true 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo "Cleanup complete"
|
||||
echo "============================================"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
|
||||
---
|
||||
# ConfigMap for cleanup configuration
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cleanup-config
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: cleanup
|
||||
data:
|
||||
# Maximum age of completed PipelineRuns to keep (in hours)
|
||||
MAX_AGE_HOURS: "24"
|
||||
# Number of recent PipelineRuns to keep regardless of age
|
||||
KEEP_RECENT: "10"
|
||||
# Cleanup schedule (cron format)
|
||||
CLEANUP_SCHEDULE: "0 */6 * * *"
|
||||
5
infrastructure/cicd/tekton/cleanup/kustomization.yaml
Normal file
5
infrastructure/cicd/tekton/cleanup/kustomization.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- cleanup.yaml
|
||||
5
infrastructure/cicd/tekton/configs/kustomization.yaml
Normal file
5
infrastructure/cicd/tekton/configs/kustomization.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- pipeline-config.yaml
|
||||
41
infrastructure/cicd/tekton/configs/pipeline-config.yaml
Normal file
41
infrastructure/cicd/tekton/configs/pipeline-config.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
# CI/CD Pipeline Configuration for Bakery-IA
|
||||
# This ConfigMap contains configurable values for the CI/CD pipeline
|
||||
#
|
||||
# IMPORTANT: When changing REGISTRY_URL, also update:
|
||||
# - infrastructure/cicd/tekton/triggers/trigger-template.yaml (registry-url default)
|
||||
# - infrastructure/cicd/tekton/secrets/secrets.yaml (registry credentials)
|
||||
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: pipeline-config
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: config
|
||||
data:
|
||||
# Container Registry Configuration
|
||||
# Change this to your actual registry URL
|
||||
# Also update trigger-template.yaml and secrets when changing this!
|
||||
REGISTRY_URL: "gitea.bakery-ia.local:5000"
|
||||
|
||||
# Git Configuration
|
||||
GIT_BRANCH: "main"
|
||||
GIT_USER_NAME: "bakery-ia-ci"
|
||||
GIT_USER_EMAIL: "ci@bakery-ia.local"
|
||||
|
||||
# Build Configuration
|
||||
BUILD_CACHE_TTL: "24h"
|
||||
BUILD_VERBOSITY: "info"
|
||||
|
||||
# Test Configuration
|
||||
SKIP_TESTS: "false"
|
||||
SKIP_LINT: "false"
|
||||
|
||||
# Deployment Configuration
|
||||
DEPLOY_NAMESPACE: "bakery-ia"
|
||||
FLUX_NAMESPACE: "flux-system"
|
||||
|
||||
# Workspace Configuration
|
||||
WORKSPACE_SIZE: "5Gi"
|
||||
WORKSPACE_STORAGE_CLASS: "standard"
|
||||
11
infrastructure/cicd/tekton/kustomization.yaml
Normal file
11
infrastructure/cicd/tekton/kustomization.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- rbac/
|
||||
- secrets/
|
||||
- configs/
|
||||
- tasks/
|
||||
- triggers/
|
||||
- pipelines/
|
||||
- cleanup/
|
||||
149
infrastructure/cicd/tekton/pipelines/ci-pipeline.yaml
Normal file
149
infrastructure/cicd/tekton/pipelines/ci-pipeline.yaml
Normal file
@@ -0,0 +1,149 @@
|
||||
# Main CI Pipeline for Bakery-IA
|
||||
# This pipeline orchestrates the build, test, and deploy process
|
||||
# Includes: fetch -> detect changes -> test -> build -> update gitops
|
||||
|
||||
apiVersion: tekton.dev/v1beta1
|
||||
kind: Pipeline
|
||||
metadata:
|
||||
name: bakery-ia-ci
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: pipeline
|
||||
spec:
|
||||
workspaces:
|
||||
- name: shared-workspace
|
||||
description: Shared workspace for source code
|
||||
- name: docker-credentials
|
||||
description: Docker registry credentials
|
||||
- name: git-credentials
|
||||
description: Git credentials for pushing GitOps updates
|
||||
optional: true
|
||||
params:
|
||||
- name: git-url
|
||||
type: string
|
||||
description: Repository URL
|
||||
- name: git-revision
|
||||
type: string
|
||||
description: Git revision/commit hash
|
||||
- name: registry
|
||||
type: string
|
||||
description: Container registry URL
|
||||
- name: git-branch
|
||||
type: string
|
||||
description: Target branch for GitOps updates
|
||||
default: "main"
|
||||
- name: skip-tests
|
||||
type: string
|
||||
description: Skip tests if "true"
|
||||
default: "false"
|
||||
- name: dry-run
|
||||
type: string
|
||||
description: Dry run mode - don't push changes
|
||||
default: "false"
|
||||
|
||||
tasks:
|
||||
# Stage 1: Fetch source code
|
||||
- name: fetch-source
|
||||
taskRef:
|
||||
name: git-clone
|
||||
workspaces:
|
||||
- name: output
|
||||
workspace: shared-workspace
|
||||
params:
|
||||
- name: url
|
||||
value: $(params.git-url)
|
||||
- name: revision
|
||||
value: $(params.git-revision)
|
||||
|
||||
# Stage 2: Detect which services changed
|
||||
- name: detect-changes
|
||||
runAfter: [fetch-source]
|
||||
taskRef:
|
||||
name: detect-changed-services
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: shared-workspace
|
||||
|
||||
# Stage 3: Run tests on changed services
|
||||
- name: run-tests
|
||||
runAfter: [detect-changes]
|
||||
taskRef:
|
||||
name: run-tests
|
||||
when:
|
||||
- input: "$(tasks.detect-changes.results.changed-services)"
|
||||
operator: notin
|
||||
values: ["none", "infrastructure"]
|
||||
- input: "$(params.skip-tests)"
|
||||
operator: notin
|
||||
values: ["true"]
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: shared-workspace
|
||||
params:
|
||||
- name: services
|
||||
value: $(tasks.detect-changes.results.changed-services)
|
||||
- name: skip-tests
|
||||
value: $(params.skip-tests)
|
||||
|
||||
# Stage 4: Build and push container images
|
||||
- name: build-and-push
|
||||
runAfter: [run-tests]
|
||||
taskRef:
|
||||
name: kaniko-build
|
||||
when:
|
||||
- input: "$(tasks.detect-changes.results.changed-services)"
|
||||
operator: notin
|
||||
values: ["none", "infrastructure"]
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: shared-workspace
|
||||
- name: docker-credentials
|
||||
workspace: docker-credentials
|
||||
params:
|
||||
- name: services
|
||||
value: $(tasks.detect-changes.results.changed-services)
|
||||
- name: registry
|
||||
value: $(params.registry)
|
||||
- name: git-revision
|
||||
value: $(params.git-revision)
|
||||
|
||||
# Stage 5: Update GitOps manifests
|
||||
- name: update-gitops-manifests
|
||||
runAfter: [build-and-push]
|
||||
taskRef:
|
||||
name: update-gitops
|
||||
when:
|
||||
- input: "$(tasks.detect-changes.results.changed-services)"
|
||||
operator: notin
|
||||
values: ["none", "infrastructure"]
|
||||
- input: "$(tasks.build-and-push.results.build-status)"
|
||||
operator: in
|
||||
values: ["success", "partial"]
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: shared-workspace
|
||||
- name: git-credentials
|
||||
workspace: git-credentials
|
||||
params:
|
||||
- name: services
|
||||
value: $(tasks.detect-changes.results.changed-services)
|
||||
- name: registry
|
||||
value: $(params.registry)
|
||||
- name: git-revision
|
||||
value: $(params.git-revision)
|
||||
- name: git-branch
|
||||
value: $(params.git-branch)
|
||||
- name: dry-run
|
||||
value: $(params.dry-run)
|
||||
|
||||
# Final tasks that run regardless of pipeline success/failure
|
||||
finally:
|
||||
- name: pipeline-summary
|
||||
taskRef:
|
||||
name: pipeline-summary
|
||||
params:
|
||||
- name: changed-services
|
||||
value: $(tasks.detect-changes.results.changed-services)
|
||||
- name: git-revision
|
||||
value: $(params.git-revision)
|
||||
6
infrastructure/cicd/tekton/pipelines/kustomization.yaml
Normal file
6
infrastructure/cicd/tekton/pipelines/kustomization.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ci-pipeline.yaml
|
||||
- prod-deploy-pipeline.yaml
|
||||
118
infrastructure/cicd/tekton/pipelines/prod-deploy-pipeline.yaml
Normal file
118
infrastructure/cicd/tekton/pipelines/prod-deploy-pipeline.yaml
Normal file
@@ -0,0 +1,118 @@
|
||||
# Production Deployment Pipeline for Bakery-IA
|
||||
# This pipeline handles production deployments with manual approval gate
|
||||
# It should be triggered after the CI pipeline succeeds
|
||||
|
||||
apiVersion: tekton.dev/v1beta1
|
||||
kind: Pipeline
|
||||
metadata:
|
||||
name: bakery-ia-prod-deploy
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: pipeline
|
||||
app.kubernetes.io/environment: production
|
||||
spec:
|
||||
workspaces:
|
||||
- name: shared-workspace
|
||||
description: Shared workspace for source code
|
||||
- name: git-credentials
|
||||
description: Git credentials for pushing GitOps updates
|
||||
optional: true
|
||||
params:
|
||||
- name: git-url
|
||||
type: string
|
||||
description: Repository URL
|
||||
- name: git-revision
|
||||
type: string
|
||||
description: Git revision/commit hash to deploy
|
||||
- name: services
|
||||
type: string
|
||||
description: Comma-separated list of services to deploy
|
||||
- name: registry
|
||||
type: string
|
||||
description: Container registry URL
|
||||
- name: approver
|
||||
type: string
|
||||
description: Name of the person who approved this deployment
|
||||
default: "automated"
|
||||
- name: approval-ticket
|
||||
type: string
|
||||
description: Ticket/issue number for deployment approval
|
||||
default: "N/A"
|
||||
|
||||
tasks:
|
||||
# Stage 1: Fetch source code
|
||||
- name: fetch-source
|
||||
taskRef:
|
||||
name: git-clone
|
||||
workspaces:
|
||||
- name: output
|
||||
workspace: shared-workspace
|
||||
params:
|
||||
- name: url
|
||||
value: $(params.git-url)
|
||||
- name: revision
|
||||
value: $(params.git-revision)
|
||||
|
||||
# Stage 2: Verify images exist in registry
|
||||
- name: verify-images
|
||||
runAfter: [fetch-source]
|
||||
taskRef:
|
||||
name: verify-images
|
||||
params:
|
||||
- name: services
|
||||
value: $(params.services)
|
||||
- name: registry
|
||||
value: $(params.registry)
|
||||
- name: git-revision
|
||||
value: $(params.git-revision)
|
||||
|
||||
# Stage 3: Pre-deployment validation
|
||||
- name: pre-deploy-validation
|
||||
runAfter: [verify-images]
|
||||
taskRef:
|
||||
name: pre-deploy-validation
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: shared-workspace
|
||||
params:
|
||||
- name: services
|
||||
value: $(params.services)
|
||||
- name: environment
|
||||
value: "production"
|
||||
|
||||
# Stage 4: Update production manifests
|
||||
- name: update-prod-manifests
|
||||
runAfter: [pre-deploy-validation]
|
||||
taskRef:
|
||||
name: update-gitops
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: shared-workspace
|
||||
- name: git-credentials
|
||||
workspace: git-credentials
|
||||
params:
|
||||
- name: services
|
||||
value: $(params.services)
|
||||
- name: registry
|
||||
value: $(params.registry)
|
||||
- name: git-revision
|
||||
value: $(params.git-revision)
|
||||
- name: git-branch
|
||||
value: "main"
|
||||
- name: dry-run
|
||||
value: "false"
|
||||
|
||||
finally:
|
||||
- name: deployment-summary
|
||||
taskRef:
|
||||
name: prod-deployment-summary
|
||||
params:
|
||||
- name: services
|
||||
value: $(params.services)
|
||||
- name: git-revision
|
||||
value: $(params.git-revision)
|
||||
- name: approver
|
||||
value: $(params.approver)
|
||||
- name: approval-ticket
|
||||
value: $(params.approval-ticket)
|
||||
6
infrastructure/cicd/tekton/rbac/kustomization.yaml
Normal file
6
infrastructure/cicd/tekton/rbac/kustomization.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- rbac.yaml
|
||||
- resource-quota.yaml
|
||||
159
infrastructure/cicd/tekton/rbac/rbac.yaml
Normal file
159
infrastructure/cicd/tekton/rbac/rbac.yaml
Normal file
@@ -0,0 +1,159 @@
|
||||
# Tekton RBAC Configuration for Bakery-IA CI/CD
|
||||
# This file defines ServiceAccounts, Roles, and RoleBindings for Tekton
|
||||
|
||||
---
|
||||
# ServiceAccount for Tekton Triggers EventListener
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: tekton-triggers-sa
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
|
||||
---
|
||||
# ServiceAccount for Pipeline execution
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: tekton-pipeline-sa
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: pipeline
|
||||
|
||||
---
|
||||
# ClusterRole for Tekton Triggers to create PipelineRuns
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: tekton-triggers-role
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
rules:
|
||||
# Ability to create PipelineRuns from triggers
|
||||
- apiGroups: ["tekton.dev"]
|
||||
resources: ["pipelineruns", "taskruns"]
|
||||
verbs: ["create", "get", "list", "watch"]
|
||||
# Ability to read pipelines and tasks
|
||||
- apiGroups: ["tekton.dev"]
|
||||
resources: ["pipelines", "tasks", "clustertasks"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# Ability to manage PVCs for workspaces
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["create", "get", "list", "watch", "delete"]
|
||||
# Ability to read secrets for credentials
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# Ability to read configmaps
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# Ability to manage events for logging
|
||||
- apiGroups: [""]
|
||||
resources: ["events"]
|
||||
verbs: ["create", "patch"]
|
||||
|
||||
---
|
||||
# ClusterRoleBinding for Tekton Triggers
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: tekton-triggers-binding
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: tekton-triggers-sa
|
||||
namespace: tekton-pipelines
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: tekton-triggers-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
# ClusterRole for Pipeline execution (needed for git operations and deployments)
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: tekton-pipeline-role
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: pipeline
|
||||
rules:
|
||||
# Ability to read/update deployments for GitOps
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["get", "list", "watch", "patch", "update"]
|
||||
# Ability to read secrets for credentials
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# Ability to read configmaps
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# Ability to manage pods for build operations
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "pods/log"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
---
|
||||
# ClusterRoleBinding for Pipeline execution
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: tekton-pipeline-binding
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: pipeline
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: tekton-pipeline-sa
|
||||
namespace: tekton-pipelines
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: tekton-pipeline-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
# Role for EventListener to access triggers resources
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: tekton-triggers-eventlistener-role
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
rules:
|
||||
- apiGroups: ["triggers.tekton.dev"]
|
||||
resources: ["eventlisteners", "triggerbindings", "triggertemplates", "triggers", "interceptors"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps", "secrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
---
|
||||
# RoleBinding for EventListener
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: tekton-triggers-eventlistener-binding
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: tekton-triggers-sa
|
||||
namespace: tekton-pipelines
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: tekton-triggers-eventlistener-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
64
infrastructure/cicd/tekton/rbac/resource-quota.yaml
Normal file
64
infrastructure/cicd/tekton/rbac/resource-quota.yaml
Normal file
@@ -0,0 +1,64 @@
|
||||
# ResourceQuota for Tekton Pipelines Namespace
|
||||
# Prevents resource exhaustion from runaway pipeline runs
|
||||
#
|
||||
# This quota limits:
|
||||
# - Total CPU and memory that can be requested/used
|
||||
# - Number of concurrent pods
|
||||
# - Number of PVCs for workspaces
|
||||
|
||||
apiVersion: v1
|
||||
kind: ResourceQuota
|
||||
metadata:
|
||||
name: tekton-pipelines-quota
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: quota
|
||||
spec:
|
||||
hard:
|
||||
# Limit total CPU
|
||||
requests.cpu: "8"
|
||||
limits.cpu: "16"
|
||||
# Limit total memory
|
||||
requests.memory: "16Gi"
|
||||
limits.memory: "32Gi"
|
||||
# Limit number of pods (controls concurrent pipeline tasks)
|
||||
pods: "20"
|
||||
# Limit PVCs (controls workspace storage)
|
||||
persistentvolumeclaims: "10"
|
||||
# Limit storage
|
||||
requests.storage: "50Gi"
|
||||
|
||||
---
|
||||
# LimitRange to set defaults and limits for individual pods
|
||||
# Ensures every pod has resource requests/limits
|
||||
apiVersion: v1
|
||||
kind: LimitRange
|
||||
metadata:
|
||||
name: tekton-pipelines-limits
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: quota
|
||||
spec:
|
||||
limits:
|
||||
# Default limits for containers
|
||||
- type: Container
|
||||
default:
|
||||
cpu: "1"
|
||||
memory: "1Gi"
|
||||
defaultRequest:
|
||||
cpu: "100m"
|
||||
memory: "256Mi"
|
||||
max:
|
||||
cpu: "4"
|
||||
memory: "8Gi"
|
||||
min:
|
||||
cpu: "50m"
|
||||
memory: "64Mi"
|
||||
# Limits for PVCs
|
||||
- type: PersistentVolumeClaim
|
||||
max:
|
||||
storage: "10Gi"
|
||||
min:
|
||||
storage: "1Gi"
|
||||
4
infrastructure/cicd/tekton/secrets/.gitignore
vendored
Normal file
4
infrastructure/cicd/tekton/secrets/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Ignore generated secrets
|
||||
.webhook-secret
|
||||
*-actual.yaml
|
||||
sealed-secrets.yaml
|
||||
167
infrastructure/cicd/tekton/secrets/generate-secrets.sh
Executable file
167
infrastructure/cicd/tekton/secrets/generate-secrets.sh
Executable file
@@ -0,0 +1,167 @@
|
||||
#!/bin/bash
|
||||
# Generate CI/CD Secrets for Bakery-IA
|
||||
#
|
||||
# This script creates Kubernetes secrets required for the CI/CD pipeline.
|
||||
# Run this script once during initial setup.
|
||||
#
|
||||
# Usage:
|
||||
# ./generate-secrets.sh [options]
|
||||
#
|
||||
# Options:
|
||||
# --registry-url Container registry URL (default: gitea.bakery-ia.local:5000)
|
||||
# --gitea-user Gitea username (will prompt if not provided)
|
||||
# --gitea-password Gitea password (will prompt if not provided)
|
||||
# --dry-run Print commands without executing
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Default values
|
||||
REGISTRY_URL="${REGISTRY_URL:-gitea.bakery-ia.local:5000}"
|
||||
DRY_RUN=false
|
||||
KUBECTL="kubectl"
|
||||
|
||||
# Check if running in microk8s
|
||||
if command -v microk8s &> /dev/null; then
|
||||
KUBECTL="microk8s kubectl"
|
||||
fi
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--registry-url)
|
||||
REGISTRY_URL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--gitea-user)
|
||||
GITEA_USERNAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--gitea-password)
|
||||
GITEA_PASSWORD="$2"
|
||||
shift 2
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unknown option: $1${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "=========================================="
|
||||
echo " Bakery-IA CI/CD Secrets Generator"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Prompt for credentials if not provided
|
||||
if [ -z "$GITEA_USERNAME" ]; then
|
||||
read -p "Enter Gitea username: " GITEA_USERNAME
|
||||
fi
|
||||
|
||||
if [ -z "$GITEA_PASSWORD" ]; then
|
||||
read -s -p "Enter Gitea password: " GITEA_PASSWORD
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Generate webhook secret
|
||||
WEBHOOK_SECRET=$(openssl rand -hex 32)
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Configuration:${NC}"
|
||||
echo " Registry URL: $REGISTRY_URL"
|
||||
echo " Gitea User: $GITEA_USERNAME"
|
||||
echo " Webhook Secret: ${WEBHOOK_SECRET:0:8}..."
|
||||
echo ""
|
||||
|
||||
# Function to create secret
|
||||
create_secret() {
|
||||
local cmd="$1"
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo -e "${YELLOW}[DRY-RUN]${NC} $cmd"
|
||||
else
|
||||
eval "$cmd"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure namespaces exist
|
||||
echo -e "${GREEN}Creating namespaces if they don't exist...${NC}"
|
||||
create_secret "$KUBECTL create namespace tekton-pipelines --dry-run=client -o yaml | $KUBECTL apply -f -"
|
||||
create_secret "$KUBECTL create namespace flux-system --dry-run=client -o yaml | $KUBECTL apply -f -"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Creating secrets...${NC}"
|
||||
|
||||
# 1. Webhook Secret
|
||||
echo " Creating gitea-webhook-secret..."
|
||||
create_secret "$KUBECTL create secret generic gitea-webhook-secret \
|
||||
--namespace tekton-pipelines \
|
||||
--from-literal=secretToken='$WEBHOOK_SECRET' \
|
||||
--dry-run=client -o yaml | $KUBECTL apply -f -"
|
||||
|
||||
# 2. Registry Credentials (docker-registry type)
|
||||
echo " Creating gitea-registry-credentials..."
|
||||
create_secret "$KUBECTL create secret docker-registry gitea-registry-credentials \
|
||||
--namespace tekton-pipelines \
|
||||
--docker-server='$REGISTRY_URL' \
|
||||
--docker-username='$GITEA_USERNAME' \
|
||||
--docker-password='$GITEA_PASSWORD' \
|
||||
--dry-run=client -o yaml | $KUBECTL apply -f -"
|
||||
|
||||
# 3. Git Credentials for Tekton
|
||||
echo " Creating gitea-git-credentials..."
|
||||
create_secret "$KUBECTL create secret generic gitea-git-credentials \
|
||||
--namespace tekton-pipelines \
|
||||
--from-literal=username='$GITEA_USERNAME' \
|
||||
--from-literal=password='$GITEA_PASSWORD' \
|
||||
--dry-run=client -o yaml | $KUBECTL apply -f -"
|
||||
|
||||
# 4. Flux Git Credentials
|
||||
echo " Creating gitea-credentials for Flux..."
|
||||
create_secret "$KUBECTL create secret generic gitea-credentials \
|
||||
--namespace flux-system \
|
||||
--from-literal=username='$GITEA_USERNAME' \
|
||||
--from-literal=password='$GITEA_PASSWORD' \
|
||||
--dry-run=client -o yaml | $KUBECTL apply -f -"
|
||||
|
||||
# Label all secrets
|
||||
echo ""
|
||||
echo -e "${GREEN}Adding labels to secrets...${NC}"
|
||||
for ns in tekton-pipelines flux-system; do
|
||||
for secret in gitea-webhook-secret gitea-registry-credentials gitea-git-credentials gitea-credentials; do
|
||||
if $KUBECTL get secret "$secret" -n "$ns" &> /dev/null; then
|
||||
create_secret "$KUBECTL label secret $secret -n $ns app.kubernetes.io/name=bakery-ia-cicd --overwrite 2>/dev/null || true"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo -e "${GREEN}Secrets created successfully!${NC}"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo -e "${YELLOW}IMPORTANT:${NC} Save this webhook secret for Gitea webhook configuration:"
|
||||
echo ""
|
||||
echo " Webhook Secret: $WEBHOOK_SECRET"
|
||||
echo ""
|
||||
echo "Configure this in Gitea:"
|
||||
echo " 1. Go to Repository Settings > Webhooks"
|
||||
echo " 2. Add webhook with URL: http://el-bakery-ia-listener.tekton-pipelines.svc.cluster.local:8080"
|
||||
echo " 3. Set Secret to the webhook secret above"
|
||||
echo " 4. Select events: Push"
|
||||
echo ""
|
||||
|
||||
# Save webhook secret to a file for reference (gitignored)
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
echo "$WEBHOOK_SECRET" > "$(dirname "$0")/.webhook-secret"
|
||||
chmod 600 "$(dirname "$0")/.webhook-secret"
|
||||
echo "Webhook secret saved to .webhook-secret (gitignored)"
|
||||
fi
|
||||
19
infrastructure/cicd/tekton/secrets/kustomization.yaml
Normal file
19
infrastructure/cicd/tekton/secrets/kustomization.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- secrets.yaml
|
||||
|
||||
# Note: In production, use sealed-secrets or external-secrets-operator
|
||||
# to manage secrets securely. The secrets.yaml file contains placeholder
|
||||
# values that must be replaced before deployment.
|
||||
#
|
||||
# Example using sealed-secrets:
|
||||
# 1. Install sealed-secrets controller
|
||||
# 2. Create SealedSecret resources instead of plain Secrets
|
||||
# 3. Commit the SealedSecret manifests to Git (safe to commit)
|
||||
#
|
||||
# Example using external-secrets-operator:
|
||||
# 1. Install external-secrets-operator
|
||||
# 2. Configure a SecretStore (AWS Secrets Manager, HashiCorp Vault, etc.)
|
||||
# 3. Create ExternalSecret resources that reference the SecretStore
|
||||
79
infrastructure/cicd/tekton/secrets/secrets-template.yaml
Normal file
79
infrastructure/cicd/tekton/secrets/secrets-template.yaml
Normal file
@@ -0,0 +1,79 @@
|
||||
# CI/CD Secrets Template for Tekton Pipelines
|
||||
#
|
||||
# DO NOT commit this file with actual credentials!
|
||||
# Use the generate-secrets.sh script to create secrets safely.
|
||||
#
|
||||
# For production, use one of these approaches:
|
||||
# 1. Sealed Secrets: kubeseal < secrets.yaml > sealed-secrets.yaml
|
||||
# 2. External Secrets Operator: Configure with your secret store
|
||||
# 3. Manual creation: kubectl create secret ... (see generate-secrets.sh)
|
||||
|
||||
---
|
||||
# Secret for Gitea webhook validation
|
||||
# Used by EventListener to validate incoming webhooks
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gitea-webhook-secret
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
type: Opaque
|
||||
stringData:
|
||||
# Generate with: openssl rand -hex 32
|
||||
secretToken: "${WEBHOOK_SECRET_TOKEN}"
|
||||
|
||||
---
|
||||
# Secret for Gitea container registry credentials
|
||||
# Used by Kaniko to push images to Gitea registry
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gitea-registry-credentials
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: build
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
stringData:
|
||||
.dockerconfigjson: |
|
||||
{
|
||||
"auths": {
|
||||
"${REGISTRY_URL}": {
|
||||
"username": "${GITEA_USERNAME}",
|
||||
"password": "${GITEA_PASSWORD}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
# Secret for Git credentials (used by pipeline to push GitOps updates)
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gitea-git-credentials
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: gitops
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: "${GITEA_USERNAME}"
|
||||
password: "${GITEA_PASSWORD}"
|
||||
|
||||
---
|
||||
# Secret for Flux GitRepository access
|
||||
# Used by Flux to pull from Gitea repository
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gitea-credentials
|
||||
namespace: flux-system
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: flux
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: "${GITEA_USERNAME}"
|
||||
password: "${GITEA_PASSWORD}"
|
||||
98
infrastructure/cicd/tekton/secrets/secrets.yaml
Normal file
98
infrastructure/cicd/tekton/secrets/secrets.yaml
Normal file
@@ -0,0 +1,98 @@
|
||||
# CI/CD Secrets for Tekton Pipelines
|
||||
#
|
||||
# WARNING: This file contains EXAMPLE values only!
|
||||
# DO NOT use these values in production.
|
||||
#
|
||||
# To create actual secrets, use ONE of these methods:
|
||||
#
|
||||
# Method 1 (Recommended): Use the generate-secrets.sh script
|
||||
# ./generate-secrets.sh --gitea-user <username> --gitea-password <password>
|
||||
#
|
||||
# Method 2: Create secrets manually with kubectl
|
||||
# kubectl create secret generic gitea-webhook-secret \
|
||||
# --namespace tekton-pipelines \
|
||||
# --from-literal=secretToken="$(openssl rand -hex 32)"
|
||||
#
|
||||
# Method 3: Use Sealed Secrets for GitOps
|
||||
# kubeseal < secrets-template.yaml > sealed-secrets.yaml
|
||||
#
|
||||
# Method 4: Use External Secrets Operator
|
||||
# Configure ESO to pull from your secret store (Vault, AWS SM, etc.)
|
||||
|
||||
---
|
||||
# Example Secret for Gitea webhook validation
|
||||
# Used by EventListener to validate incoming webhooks
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gitea-webhook-secret
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
annotations:
|
||||
note: "EXAMPLE - Replace with actual secret using generate-secrets.sh"
|
||||
type: Opaque
|
||||
stringData:
|
||||
# Generate with: openssl rand -hex 32
|
||||
secretToken: "example-webhook-token-do-not-use-in-production"
|
||||
|
||||
---
|
||||
# Example Secret for Gitea container registry credentials
|
||||
# Used by Kaniko to push images to Gitea registry
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gitea-registry-credentials
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: build
|
||||
annotations:
|
||||
note: "EXAMPLE - Replace with actual secret using generate-secrets.sh"
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
stringData:
|
||||
.dockerconfigjson: |
|
||||
{
|
||||
"auths": {
|
||||
"gitea.bakery-ia.local:5000": {
|
||||
"username": "example-user",
|
||||
"password": "example-password"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
# Example Secret for Git credentials (used by pipeline to push GitOps updates)
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gitea-git-credentials
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: gitops
|
||||
annotations:
|
||||
note: "EXAMPLE - Replace with actual secret using generate-secrets.sh"
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: "example-user"
|
||||
password: "example-password"
|
||||
|
||||
---
|
||||
# Example Secret for Flux GitRepository access
|
||||
# Used by Flux to pull from Gitea repository
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gitea-credentials
|
||||
namespace: flux-system
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: flux
|
||||
annotations:
|
||||
note: "EXAMPLE - Replace with actual secret using generate-secrets.sh"
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: "example-user"
|
||||
password: "example-password"
|
||||
154
infrastructure/cicd/tekton/tasks/detect-changes.yaml
Normal file
154
infrastructure/cicd/tekton/tasks/detect-changes.yaml
Normal 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
|
||||
95
infrastructure/cicd/tekton/tasks/git-clone.yaml
Normal file
95
infrastructure/cicd/tekton/tasks/git-clone.yaml
Normal 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
|
||||
200
infrastructure/cicd/tekton/tasks/kaniko-build.yaml
Normal file
200
infrastructure/cicd/tekton/tasks/kaniko-build.yaml
Normal 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: {}
|
||||
14
infrastructure/cicd/tekton/tasks/kustomization.yaml
Normal file
14
infrastructure/cicd/tekton/tasks/kustomization.yaml
Normal 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
|
||||
62
infrastructure/cicd/tekton/tasks/pipeline-summary.yaml
Normal file
62
infrastructure/cicd/tekton/tasks/pipeline-summary.yaml
Normal 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
|
||||
76
infrastructure/cicd/tekton/tasks/pre-deploy-validation.yaml
Normal file
76
infrastructure/cicd/tekton/tasks/pre-deploy-validation.yaml
Normal 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
|
||||
@@ -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
|
||||
205
infrastructure/cicd/tekton/tasks/run-tests.yaml
Normal file
205
infrastructure/cicd/tekton/tasks/run-tests.yaml
Normal 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
|
||||
302
infrastructure/cicd/tekton/tasks/update-gitops.yaml
Normal file
302
infrastructure/cicd/tekton/tasks/update-gitops.yaml
Normal 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
|
||||
91
infrastructure/cicd/tekton/tasks/verify-images.yaml
Normal file
91
infrastructure/cicd/tekton/tasks/verify-images.yaml
Normal 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
|
||||
35
infrastructure/cicd/tekton/triggers/event-listener.yaml
Normal file
35
infrastructure/cicd/tekton/triggers/event-listener.yaml
Normal file
@@ -0,0 +1,35 @@
|
||||
# Tekton EventListener for Bakery-IA CI/CD
|
||||
# This listener receives webhook events and triggers pipelines
|
||||
|
||||
apiVersion: triggers.tekton.dev/v1beta1
|
||||
kind: EventListener
|
||||
metadata:
|
||||
name: bakery-ia-listener
|
||||
namespace: tekton-pipelines
|
||||
spec:
|
||||
serviceAccountName: tekton-triggers-sa
|
||||
triggers:
|
||||
- name: bakery-ia-gitea-trigger
|
||||
bindings:
|
||||
- ref: bakery-ia-trigger-binding
|
||||
template:
|
||||
ref: bakery-ia-trigger-template
|
||||
# Using CEL interceptor for local development (no TLS/CA bundle required)
|
||||
# The CEL interceptor is built-in and doesn't need external services
|
||||
interceptors:
|
||||
- name: "filter-push-events"
|
||||
ref:
|
||||
name: "cel"
|
||||
params:
|
||||
# Filter for push events from Gitea or GitHub
|
||||
- name: "filter"
|
||||
value: "header.match('X-Gitea-Event', 'push') || header.match('X-GitHub-Event', 'push')"
|
||||
# Add overlays to standardize the payload
|
||||
- name: "overlays"
|
||||
value:
|
||||
- key: "git_url"
|
||||
expression: "body.repository.clone_url"
|
||||
- key: "git_revision"
|
||||
expression: "body.after"
|
||||
- key: "git_branch"
|
||||
expression: "body.ref.split('/')[2]"
|
||||
9
infrastructure/cicd/tekton/triggers/kustomization.yaml
Normal file
9
infrastructure/cicd/tekton/triggers/kustomization.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
# NOTE: gitlab-interceptor.yaml removed - uses built-in Tekton Triggers interceptor
|
||||
# The gitlab ClusterInterceptor is provided by Tekton Triggers installation
|
||||
- event-listener.yaml
|
||||
- trigger-template.yaml
|
||||
- trigger-binding.yaml
|
||||
31
infrastructure/cicd/tekton/triggers/trigger-binding.yaml
Normal file
31
infrastructure/cicd/tekton/triggers/trigger-binding.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
# Tekton TriggerBinding for Bakery-IA CI/CD
|
||||
# This binding extracts parameters from Gitea webhook events
|
||||
#
|
||||
# Note: We use CEL overlay extensions for consistent field access
|
||||
# The EventListener's CEL interceptor creates these extensions:
|
||||
# - extensions.git_url: Repository clone URL
|
||||
# - extensions.git_revision: Commit SHA (from body.after)
|
||||
# - extensions.git_branch: Branch name (extracted from ref)
|
||||
|
||||
apiVersion: triggers.tekton.dev/v1beta1
|
||||
kind: TriggerBinding
|
||||
metadata:
|
||||
name: bakery-ia-trigger-binding
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
spec:
|
||||
params:
|
||||
# Use CEL overlay extensions for consistent access across Git providers
|
||||
- name: git-repo-url
|
||||
value: $(extensions.git_url)
|
||||
- name: git-revision
|
||||
value: $(extensions.git_revision)
|
||||
- name: git-branch
|
||||
value: $(extensions.git_branch)
|
||||
# Direct body access for fields not in overlays
|
||||
- name: git-repo-name
|
||||
value: $(body.repository.name)
|
||||
- name: git-repo-full-name
|
||||
value: $(body.repository.full_name)
|
||||
86
infrastructure/cicd/tekton/triggers/trigger-template.yaml
Normal file
86
infrastructure/cicd/tekton/triggers/trigger-template.yaml
Normal file
@@ -0,0 +1,86 @@
|
||||
# Tekton TriggerTemplate for Bakery-IA CI/CD
|
||||
# This template defines how PipelineRuns are created when triggers fire
|
||||
#
|
||||
# Registry URL Configuration:
|
||||
# The registry URL is configured via the 'registry' parameter.
|
||||
# Default value should match pipeline-config ConfigMap's REGISTRY_URL.
|
||||
# To change the registry, update BOTH:
|
||||
# 1. This template's default value
|
||||
# 2. The pipeline-config ConfigMap
|
||||
|
||||
apiVersion: triggers.tekton.dev/v1beta1
|
||||
kind: TriggerTemplate
|
||||
metadata:
|
||||
name: bakery-ia-trigger-template
|
||||
namespace: tekton-pipelines
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
app.kubernetes.io/component: triggers
|
||||
spec:
|
||||
params:
|
||||
- name: git-repo-url
|
||||
description: The git repository URL
|
||||
- name: git-revision
|
||||
description: The git revision/commit hash
|
||||
- name: git-branch
|
||||
description: The git branch name
|
||||
default: "main"
|
||||
- name: git-repo-name
|
||||
description: The git repository name
|
||||
default: "bakery-ia"
|
||||
- name: git-repo-full-name
|
||||
description: The full repository name (org/repo)
|
||||
default: "bakery/bakery-ia"
|
||||
# Registry URL - keep in sync with pipeline-config ConfigMap
|
||||
- name: registry-url
|
||||
description: Container registry URL
|
||||
default: "gitea.bakery-ia.local:5000"
|
||||
resourcetemplates:
|
||||
- apiVersion: tekton.dev/v1beta1
|
||||
kind: PipelineRun
|
||||
metadata:
|
||||
generateName: bakery-ia-ci-run-
|
||||
labels:
|
||||
app.kubernetes.io/name: bakery-ia-cicd
|
||||
tekton.dev/pipeline: bakery-ia-ci
|
||||
triggers.tekton.dev/trigger: bakery-ia-gitea-trigger
|
||||
annotations:
|
||||
# Track the source commit
|
||||
bakery-ia.io/git-revision: $(tt.params.git-revision)
|
||||
bakery-ia.io/git-branch: $(tt.params.git-branch)
|
||||
spec:
|
||||
pipelineRef:
|
||||
name: bakery-ia-ci
|
||||
serviceAccountName: tekton-pipeline-sa
|
||||
workspaces:
|
||||
- name: shared-workspace
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
- name: docker-credentials
|
||||
secret:
|
||||
secretName: gitea-registry-credentials
|
||||
- name: git-credentials
|
||||
secret:
|
||||
secretName: gitea-git-credentials
|
||||
params:
|
||||
- name: git-url
|
||||
value: $(tt.params.git-repo-url)
|
||||
- name: git-revision
|
||||
value: $(tt.params.git-revision)
|
||||
- name: git-branch
|
||||
value: $(tt.params.git-branch)
|
||||
# Use template parameter for registry URL
|
||||
- name: registry
|
||||
value: $(tt.params.registry-url)
|
||||
- name: skip-tests
|
||||
value: "false"
|
||||
- name: dry-run
|
||||
value: "false"
|
||||
# Timeout for the entire pipeline run
|
||||
timeouts:
|
||||
pipeline: "1h0m0s"
|
||||
tasks: "45m0s"
|
||||
Reference in New Issue
Block a user