Add new infra architecture 3

This commit is contained in:
Urtzi Alfaro
2026-01-19 13:57:50 +01:00
parent 8461226a97
commit 9edcc8c231
110 changed files with 2568 additions and 4636 deletions

View File

@@ -33,9 +33,14 @@ infrastructure/ci-cd/
│ ├── trigger-binding.yaml
│ ├── event-listener.yaml
│ └── gitlab-interceptor.yaml
├── flux/ # Flux CD GitOps configuration
│ ├── git-repository.yaml # Git repository source
── kustomization.yaml # Deployment kustomization
├── flux/ # Flux CD GitOps Helm chart configuration
│ ├── Chart.yaml # Helm chart definition
── values.yaml # Default configuration values
│ ├── templates/ # Kubernetes manifest templates
│ │ ├── gitrepository.yaml
│ │ ├── kustomization.yaml
│ │ └── namespace.yaml
│ └── values/ # Additional value files
├── monitoring/ # Monitoring configuration
│ └── otel-collector.yaml # OpenTelemetry collector
└── README.md # This file

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: flux-cd
description: A Helm chart for deploying Flux CD GitOps toolkit for Bakery-IA
type: application
version: 0.1.0
appVersion: "2.2.3"

View File

@@ -1,16 +0,0 @@
# 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

View File

@@ -1,25 +0,0 @@
# 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

View File

@@ -1,15 +0,0 @@
# 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

View File

@@ -0,0 +1,15 @@
{{- if .Values.gitRepository }}
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: {{ .Values.gitRepository.name }}
namespace: {{ .Values.gitRepository.namespace }}
spec:
interval: {{ .Values.gitRepository.interval }}
url: {{ .Values.gitRepository.url }}
ref:
branch: {{ .Values.gitRepository.ref.branch }}
secretRef:
name: {{ .Values.gitRepository.secretRef.name }}
timeout: {{ .Values.gitRepository.timeout }}
{{- end }}

View File

@@ -0,0 +1,43 @@
{{- if .Values.kustomization }}
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: {{ .Values.kustomization.name }}
namespace: {{ .Values.kustomization.namespace }}
labels:
app.kubernetes.io/name: bakery-ia
app.kubernetes.io/component: flux
spec:
# Wait for GitRepository to be ready before reconciling
dependsOn: []
interval: {{ .Values.kustomization.interval }}
path: {{ .Values.kustomization.path }}
prune: {{ .Values.kustomization.prune }}
sourceRef:
kind: {{ .Values.kustomization.sourceRef.kind }}
name: {{ .Values.kustomization.sourceRef.name }}
targetNamespace: {{ .Values.kustomization.targetNamespace }}
timeout: {{ .Values.kustomization.timeout }}
retryInterval: {{ .Values.kustomization.retryInterval }}
wait: {{ .Values.kustomization.wait }}
{{- if .Values.kustomization.healthChecks }}
healthChecks:
{{- range .Values.kustomization.healthChecks }}
- apiVersion: {{ .apiVersion }}
kind: {{ .kind }}
name: {{ .name }}
namespace: {{ .namespace }}
{{- end }}
{{- end }}
{{- if .Values.kustomization.postBuild }}
postBuild:
substituteFrom:
{{- range .Values.kustomization.postBuild.substituteFrom }}
- kind: {{ .kind }}
name: {{ .name }}
{{- if .optional }}
optional: {{ .optional }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{ .Values.gitRepository.namespace }}
labels:
app.kubernetes.io/name: flux
kubernetes.io/metadata.name: {{ .Values.gitRepository.namespace }}

View File

@@ -1,22 +1,21 @@
# 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
# Default values for flux-cd
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
gitRepository:
name: bakery-ia
namespace: flux-system
interval: 1m
url: http://gitea.bakery-ia.local/bakery/bakery-ia.git
ref:
branch: main
secretRef:
name: gitea-credentials
timeout: 60s
kustomization:
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
@@ -27,7 +26,6 @@ spec:
timeout: 10m
retryInterval: 1m
wait: true
# Health checks for critical services
healthChecks:
# Core Infrastructure
- apiVersion: apps/v1
@@ -65,7 +63,6 @@ spec:
kind: Deployment
name: notification-service
namespace: bakery-ia
# Post-build variable substitution
postBuild:
substituteFrom:
- kind: ConfigMap

View File

@@ -16,6 +16,9 @@ service:
type: ClusterIP
port: 2222
ingress:
enabled: false
persistence:
enabled: true
size: 10Gi

View File

@@ -1,10 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- tekton/
- ../../namespaces/flux-system.yaml
# Tekton is now managed via Helm, so we don't include it directly here
# The Tekton Helm chart is deployed separately via Tilt
# 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
# Flux is now managed via Helm chart located in this directory, so we don't include it directly here

View File

@@ -0,0 +1,15 @@
apiVersion: v2
name: tekton-cicd
description: Tekton CI/CD infrastructure for Bakery-IA
type: application
version: 0.1.0
appVersion: "0.57.0"
maintainers:
- name: Bakery-IA Team
email: team@bakery-ia.local
annotations:
category: Infrastructure
app.kubernetes.io/name: tekton-cicd
app.kubernetes.io/instance: tekton-cicd
app.kubernetes.io/version: "0.57.0"
app.kubernetes.io/part-of: bakery-ia

View File

@@ -0,0 +1,63 @@
# Tekton CI/CD Helm Chart
This Helm chart deploys the Tekton CI/CD infrastructure for the Bakery-IA project.
## Prerequisites
- Kubernetes 1.20+
- Tekton Pipelines installed (v0.57.0 or later)
- Helm 3.0+
## Installation
Before installing this chart, Tekton Pipelines must be installed separately:
```bash
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
```
Then install the chart:
```bash
helm repo add tekton-pipelines https://tekton.dev/charts
helm repo update
helm install tekton-cicd infrastructure/helm/tekton --namespace tekton-pipelines --create-namespace
```
## Configuration
The following table lists the configurable parameters of the tekton-cicd chart and their default values.
| Parameter | Description | Default |
|-----------|-------------|---------|
| `global.registry.url` | Container registry URL | `"gitea.bakery-ia.local:5000"` |
| `global.git.branch` | Git branch name | `"main"` |
| `global.git.userName` | Git user name | `"bakery-ia-ci"` |
| `global.git.userEmail` | Git user email | `"ci@bakery-ia.local"` |
| `pipeline.build.cacheTTL` | Build cache TTL | `"24h"` |
| `pipeline.build.verbosity` | Build verbosity level | `"info"` |
| `pipeline.test.skipTests` | Skip tests flag | `"false"` |
| `pipeline.test.skipLint` | Skip lint flag | `"false"` |
| `pipeline.deployment.namespace` | Deployment namespace | `"bakery-ia"` |
| `pipeline.deployment.fluxNamespace` | Flux namespace | `"flux-system"` |
| `pipeline.workspace.size` | Workspace size | `"5Gi"` |
| `pipeline.workspace.storageClass` | Workspace storage class | `"standard"` |
| `secrets.webhook.token` | Webhook validation token | `"example-webhook-token-do-not-use-in-production"` |
| `secrets.registry.username` | Registry username | `"example-user"` |
| `secrets.registry.password` | Registry password | `"example-password"` |
| `secrets.registry.registryUrl` | Registry URL | `"gitea.bakery-ia.local:5000"` |
| `secrets.git.username` | Git username | `"example-user"` |
| `secrets.git.password` | Git password | `"example-password"` |
| `namespace` | Namespace for Tekton resources | `"tekton-pipelines"` |
## Uninstallation
To uninstall/delete the `tekton-cicd` release:
```bash
helm delete tekton-cicd --namespace tekton-pipelines
```
## Values
For a detailed list of configurable values, see the `values.yaml` file.

View File

@@ -0,0 +1,22 @@
Thank you for installing {{ .Chart.Name }}.
This chart deploys the Tekton CI/CD infrastructure for Bakery-IA.
IMPORTANT: Tekton Pipelines must be installed separately before deploying this chart.
To install Tekton Pipelines, run:
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
To verify Tekton is running:
kubectl get pods -n tekton-pipelines
After Tekton is installed, this chart will deploy:
- ConfigMaps with pipeline configuration
- RBAC resources for triggers and pipelines
- Secrets for registry and Git credentials
- Tasks, Pipelines, and Triggers for CI/CD
To check the status of deployed resources:
kubectl get all -n {{ .Values.namespace }}
For more information about Tekton, visit: https://tekton.dev/

View File

@@ -1,36 +1,10 @@
# 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/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
rules:
# Ability to create PipelineRuns from triggers
@@ -57,25 +31,6 @@ rules:
- 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
@@ -83,7 +38,7 @@ kind: ClusterRole
metadata:
name: tekton-pipeline-role
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: pipeline
rules:
# Ability to read/update deployments for GitOps
@@ -102,34 +57,15 @@ rules:
- 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
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
rules:
- apiGroups: ["triggers.tekton.dev"]
@@ -137,23 +73,4 @@ rules:
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
verbs: ["get", "list", "watch"]

View File

@@ -0,0 +1,32 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: pipeline-config
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: config
data:
# Container Registry Configuration
REGISTRY_URL: "{{ .Values.global.registry.url }}"
# Git Configuration
GIT_BRANCH: "{{ .Values.global.git.branch }}"
GIT_USER_NAME: "{{ .Values.global.git.userName }}"
GIT_USER_EMAIL: "{{ .Values.global.git.userEmail }}"
# Build Configuration
BUILD_CACHE_TTL: "{{ .Values.pipeline.build.cacheTTL }}"
BUILD_VERBOSITY: "{{ .Values.pipeline.build.verbosity }}"
# Test Configuration
SKIP_TESTS: "{{ .Values.pipeline.test.skipTests }}"
SKIP_LINT: "{{ .Values.pipeline.test.skipLint }}"
# Deployment Configuration
DEPLOY_NAMESPACE: "{{ .Values.pipeline.deployment.namespace }}"
FLUX_NAMESPACE: "{{ .Values.pipeline.deployment.fluxNamespace }}"
# Workspace Configuration
WORKSPACE_SIZE: "{{ .Values.pipeline.workspace.size }}"
WORKSPACE_STORAGE_CLASS: "{{ .Values.pipeline.workspace.storageClass }}"

View File

@@ -0,0 +1,33 @@
# 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-event-listener
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
spec:
serviceAccountName: {{ .Values.serviceAccounts.triggers.name }}
triggers:
- name: bakery-ia-gitea-trigger
interceptors:
- ref:
name: "cel"
params:
- name: "filter"
value: "has(body.repository) && body.ref.contains('main')"
- ref:
name: "bitbucket"
params:
- name: "secretRef"
value:
secretName: gitea-webhook-secret
secretKey: secretToken
bindings:
- ref: bakery-ia-trigger-binding
template:
ref: bakery-ia-trigger-template
replicas: 1

View File

@@ -0,0 +1,9 @@
{{- if .Values.namespace }}
apiVersion: v1
kind: Namespace
metadata:
name: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: {{ .Values.labels.app.component }}
{{- end }}

View File

@@ -6,9 +6,9 @@ apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: bakery-ia-ci
namespace: tekton-pipelines
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: pipeline
spec:
workspaces:

View File

@@ -0,0 +1,51 @@
# ClusterRoleBinding for Tekton Triggers
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tekton-triggers-binding
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
subjects:
- kind: ServiceAccount
name: {{ .Values.serviceAccounts.triggers.name }}
namespace: {{ .Values.namespace }}
roleRef:
kind: ClusterRole
name: tekton-triggers-role
apiGroup: rbac.authorization.k8s.io
---
# ClusterRoleBinding for Pipeline execution
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tekton-pipeline-binding
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: pipeline
subjects:
- kind: ServiceAccount
name: {{ .Values.serviceAccounts.pipeline.name }}
namespace: {{ .Values.namespace }}
roleRef:
kind: ClusterRole
name: tekton-pipeline-role
apiGroup: rbac.authorization.k8s.io
---
# RoleBinding for EventListener
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tekton-triggers-eventlistener-binding
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
subjects:
- kind: ServiceAccount
name: {{ .Values.serviceAccounts.triggers.name }}
namespace: {{ .Values.namespace }}
roleRef:
kind: Role
name: tekton-triggers-eventlistener-role
apiGroup: rbac.authorization.k8s.io

View File

@@ -0,0 +1,72 @@
# Secret for Gitea webhook validation
# Used by EventListener to validate incoming webhooks
apiVersion: v1
kind: Secret
metadata:
name: gitea-webhook-secret
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
annotations:
note: "Webhook secret for validating incoming webhooks"
type: Opaque
stringData:
secretToken: {{ .Values.secrets.webhook.token | quote }}
---
# 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: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: build
annotations:
note: "Registry credentials for pushing images"
type: kubernetes.io/dockerconfigjson
stringData:
.dockerconfigjson: |
{
"auths": {
{{ .Values.secrets.registry.registryUrl | quote }}: {
"username": {{ .Values.secrets.registry.username | quote }},
"password": {{ .Values.secrets.registry.password | quote }}
}
}
}
---
# Secret for Git credentials (used by pipeline to push GitOps updates)
apiVersion: v1
kind: Secret
metadata:
name: gitea-git-credentials
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: gitops
annotations:
note: "Git credentials for GitOps updates"
type: Opaque
stringData:
username: {{ .Values.secrets.git.username | quote }}
password: {{ .Values.secrets.git.password | quote }}
---
# Secret for Flux GitRepository access
# Used by Flux to pull from Gitea repository
apiVersion: v1
kind: Secret
metadata:
name: gitea-credentials
namespace: {{ .Values.pipeline.deployment.fluxNamespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: flux
annotations:
note: "Credentials for Flux GitRepository access"
type: Opaque
stringData:
username: {{ .Values.secrets.git.username | quote }}
password: {{ .Values.secrets.git.password | quote }}

View File

@@ -0,0 +1,19 @@
# ServiceAccount for Tekton Triggers EventListener
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.serviceAccounts.triggers.name }}
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
---
# ServiceAccount for Pipeline execution
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.serviceAccounts.pipeline.name }}
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: pipeline

View File

@@ -5,9 +5,9 @@ apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: git-clone
namespace: tekton-pipelines
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: source
spec:
workspaces:

View File

@@ -0,0 +1,51 @@
# Tekton Kaniko Build Task for Bakery-IA CI/CD
# This task builds and pushes container images using Kaniko
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: kaniko-build
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: build
spec:
workspaces:
- name: source
description: Workspace containing the source code
- 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 to tag images with
results:
- name: build-status
description: Status of the build operation
steps:
- name: build-and-push
image: gcr.io/kaniko-project/executor:v1.15.0
env:
- name: DOCKER_CONFIG
value: /tekton/home/.docker
command:
- /kaniko/executor
args:
- --dockerfile=$(workspaces.source.path)/Dockerfile
- --destination=$(params.registry)/$(params.service):$(params.git-revision)
- --context=$(workspaces.source.path)
- --cache=true
- --cache-repo=$(params.registry)/cache
resources:
limits:
cpu: 2000m
memory: 4Gi
requests:
cpu: 500m
memory: 1Gi

View File

@@ -0,0 +1,86 @@
# Tekton Run Tests Task for Bakery-IA CI/CD
# This task runs tests on the source code
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: run-tests
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: test
spec:
workspaces:
- name: source
description: Workspace containing the source code
params:
- name: services
type: string
description: Comma-separated list of services to test
- name: skip-tests
type: string
description: Skip tests if "true"
default: "false"
steps:
- name: run-unit-tests
image: python:3.11-slim
workingDir: $(workspaces.source.path)
script: |
#!/bin/bash
set -e
echo "============================================"
echo "Running Unit Tests"
echo "Services: $(params.services)"
echo "Skip tests: $(params.skip-tests)"
echo "============================================"
if [ "$(params.skip-tests)" = "true" ]; then
echo "Skipping tests as requested"
exit 0
fi
# Install dependencies if requirements file exists
if [ -f "requirements.txt" ]; then
pip install --no-cache-dir -r requirements.txt
fi
# Run unit tests
python -m pytest tests/unit/ -v
echo "Unit tests completed successfully"
resources:
limits:
cpu: 1000m
memory: 2Gi
requests:
cpu: 200m
memory: 512Mi
- name: run-integration-tests
image: python:3.11-slim
workingDir: $(workspaces.source.path)
script: |
#!/bin/bash
set -e
echo "============================================"
echo "Running Integration Tests"
echo "Services: $(params.services)"
echo "============================================"
if [ "$(params.skip-tests)" = "true" ]; then
echo "Skipping integration tests as requested"
exit 0
fi
# Run integration tests
python -m pytest tests/integration/ -v
echo "Integration tests completed successfully"
resources:
limits:
cpu: 1000m
memory: 2Gi
requests:
cpu: 200m
memory: 512Mi

View File

@@ -0,0 +1,104 @@
# Tekton Update GitOps Task for Bakery-IA CI/CD
# This task updates GitOps manifests with new image tags
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: update-gitops
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: gitops
spec:
workspaces:
- name: source
description: Workspace containing the source code
- name: git-credentials
description: Git credentials for pushing changes
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 to tag images with
- name: git-branch
type: string
description: Git branch to push changes to
- name: dry-run
type: string
description: Dry run mode - don't push changes
default: "false"
steps:
- name: update-manifests
image: alpine/git:2.43.0
workingDir: $(workspaces.source.path)
env:
- name: GIT_USERNAME
valueFrom:
secretKeyRef:
name: gitea-git-credentials
key: username
- name: GIT_PASSWORD
valueFrom:
secretKeyRef:
name: gitea-git-credentials
key: password
script: |
#!/bin/bash
set -e
echo "============================================"
echo "Updating GitOps Manifests"
echo "Services: $(params.services)"
echo "Registry: $(params.registry)"
echo "Revision: $(params.git-revision)"
echo "Branch: $(params.git-branch)"
echo "Dry run: $(params.dry-run)"
echo "============================================"
# Configure git
git config --global user.email "ci@bakery-ia.local"
git config --global user.name "bakery-ia-ci"
# Clone the GitOps repository
REPO_URL="https://${GIT_USERNAME}:${GIT_PASSWORD}@gitea.bakery-ia.local/bakery/bakery-ia-gitops.git"
git clone "$REPO_URL" /tmp/gitops
cd /tmp/gitops
# Switch to target branch
git checkout "$(params.git-branch)" || git checkout -b "$(params.git-branch)"
# Update image tags in Kubernetes manifests
for service in $(echo "$(params.services)" | tr ',' '\n'); do
echo "Updating manifest for service: $service"
# Find and update the image tag in the deployment YAML
if [ -f "deployments/${service}-deployment.yaml" ]; then
sed -i "s|image: bakery/${service}:.*|image: $(params.registry)/bakery/${service}:$(params.git-revision)|g" "deployments/${service}-deployment.yaml"
fi
done
# Commit and push changes (unless dry-run)
if [ "$(params.dry-run)" != "true" ]; then
git add .
git commit -m "Update images for services: $(params.services) [skip ci]"
git push origin "$(params.git-branch)"
echo "GitOps manifests updated successfully"
else
echo "Dry run mode - changes not pushed"
git status
git diff
fi
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi

View File

@@ -0,0 +1,23 @@
# Tekton TriggerBinding for Bakery-IA CI/CD
# This binding extracts parameters from incoming webhook payloads
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
name: bakery-ia-trigger-binding
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
spec:
params:
- name: git-repo-url
value: "{{"{{ .payload.repository.clone_url }}"}}"
- name: git-revision
value: "{{"{{ .payload.after }}"}}"
- name: git-branch
value: "{{"{{ .payload.ref }}" | replace "refs/heads/" "" | replace "refs/tags/" "" }}"
- name: git-repo-name
value: "{{"{{ .payload.repository.name }}"}}"
- name: git-repo-full-name
value: "{{"{{ .payload.repository.full_name }}"}}"

View File

@@ -1,20 +1,13 @@
# 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
namespace: {{ .Values.namespace }}
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/name: {{ .Values.labels.app.name }}
app.kubernetes.io/component: triggers
spec:
params:
@@ -34,14 +27,14 @@ spec:
# Registry URL - keep in sync with pipeline-config ConfigMap
- name: registry-url
description: Container registry URL
default: "gitea.bakery-ia.local:5000"
default: {{ .Values.global.registry.url | quote }}
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: bakery-ia-ci-run-
labels:
app.kubernetes.io/name: bakery-ia-cicd
app.kubernetes.io/name: {{ .Values.labels.app.name }}
tekton.dev/pipeline: bakery-ia-ci
triggers.tekton.dev/trigger: bakery-ia-gitea-trigger
annotations:
@@ -51,7 +44,7 @@ spec:
spec:
pipelineRef:
name: bakery-ia-ci
serviceAccountName: tekton-pipeline-sa
serviceAccountName: {{ .Values.serviceAccounts.pipeline.name }}
workspaces:
- name: shared-workspace
volumeClaimTemplate:
@@ -59,7 +52,7 @@ spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
storage: {{ .Values.pipeline.workspace.size }}
- name: docker-credentials
secret:
secretName: gitea-registry-credentials
@@ -83,4 +76,4 @@ spec:
# Timeout for the entire pipeline run
timeouts:
pipeline: "1h0m0s"
tasks: "45m0s"
tasks: "45m0s"

View File

@@ -0,0 +1,24 @@
# Test values for Tekton Helm chart
# This file overrides default values for testing purposes
# Use a test namespace
namespace: "tekton-test"
# Test registry URL
global:
registry:
url: "localhost:5000"
# Test secrets
secrets:
webhook:
token: "test-webhook-token"
registry:
username: "test-user"
password: "test-password"
registryUrl: "localhost:5000"
git:
username: "test-git-user"
password: "test-git-password"

View File

@@ -0,0 +1,91 @@
# Default values for tekton-cicd Helm chart
# This file contains configurable values for the CI/CD pipeline
# Global settings
global:
# Registry configuration
registry:
url: "gitea.bakery-ia.local:5000"
# Git configuration
git:
branch: "main"
userName: "bakery-ia-ci"
userEmail: "ci@bakery-ia.local"
# Pipeline configuration
pipeline:
# Build configuration
build:
cacheTTL: "24h"
verbosity: "info"
# Test configuration
test:
skipTests: "false"
skipLint: "false"
# Deployment configuration
deployment:
namespace: "bakery-ia"
fluxNamespace: "flux-system"
# Workspace configuration
workspace:
size: "5Gi"
storageClass: "standard"
# Tekton controller settings
controller:
replicas: 1
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 100m
memory: 128Mi
# Tekton webhook settings
webhook:
replicas: 1
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 50m
memory: 64Mi
# Namespace for Tekton resources
namespace: "tekton-pipelines"
# Secrets configuration
secrets:
# Webhook secret for validating incoming webhooks
webhook:
token: "example-webhook-token-do-not-use-in-production"
# Registry credentials for pushing images
registry:
username: "example-user"
password: "example-password"
registryUrl: "gitea.bakery-ia.local:5000"
# Git credentials for GitOps updates
git:
username: "example-user"
password: "example-password"
# Service accounts
serviceAccounts:
triggers:
name: "tekton-triggers-sa"
pipeline:
name: "tekton-pipeline-sa"
# Labels to apply to resources
labels:
app:
name: "bakery-ia-cicd"
component: "tekton"

View File

@@ -1,222 +0,0 @@
# 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 * * *"

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- cleanup.yaml

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- pipeline-config.yaml

View File

@@ -1,41 +0,0 @@
# 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"

View File

@@ -1,11 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rbac/
- secrets/
- configs/
- tasks/
- triggers/
- pipelines/
- cleanup/

View File

@@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ci-pipeline.yaml
- prod-deploy-pipeline.yaml

View File

@@ -1,118 +0,0 @@
# 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)

View File

@@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rbac.yaml
- resource-quota.yaml

View File

@@ -1,64 +0,0 @@
# 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"

View File

@@ -1,4 +0,0 @@
# Ignore generated secrets
.webhook-secret
*-actual.yaml
sealed-secrets.yaml

View File

@@ -1,167 +0,0 @@
#!/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

View File

@@ -1,19 +0,0 @@
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

View File

@@ -1,79 +0,0 @@
# 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}"

View File

@@ -1,98 +0,0 @@
# 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"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,91 +0,0 @@
# 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

View File

@@ -1,35 +0,0 @@
# 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]"

View File

@@ -1,9 +0,0 @@
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

View File

@@ -1,31 +0,0 @@
# 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)