This commit is contained in:
alessandro
2026-07-17 09:42:52 +02:00
commit 20d506407a
93 changed files with 14526 additions and 0 deletions

View File

@@ -0,0 +1,154 @@
# Pipeline Gitea: `DEV_build_deploy.yaml`
## Scopo
Questa pipeline esegue build, publish immagini Docker e deploy Kubernetes dell'ambiente `dev`.
Flusso alto livello:
1. Checkout del repository.
2. Login al registry Harbor.
3. Build e push delle immagini per ogni servizio in `containers/*`.
4. Creazione del file `kubeconfig` da secret.
5. Sostituzione variabili nei manifest Kubernetes.
6. Deploy dei manifest e configurazione listener HTTPS sul Gateway.
## Trigger e runner
- Trigger: `push` su branch `main`.
- Job: `docker`.
- Runner richiesto: `POC-Master0`.
## Definizione pipeline
File: `pipeline/DEV_build_deploy.yaml`
Step principali:
- `actions/checkout@v4`
- `docker/login-action@v3`
- `/root/work/pipeline/build_container.sh ${{ github.event.repository.name }} ${{ vars.REGISTRY }}`
- creazione `./kubeconfig` da `${{ secrets.KUBECONFIG_DEV }}`
- `/root/work/pipeline/customize.sh dev`
- `/root/work/pipeline/deploy.sh`
## Script eseguiti dalla pipeline
### 1) `build_container.sh`
Responsabilità:
- Itera tutte le directory `containers/*/`.
- Copia i sorgenti da `src/<container>/` dentro `containers/<container>/`.
- Rileva il Dockerfile (`dockerfile` oppure `Dockerfile`).
- Costruisce due tag immagine:
- `${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:${COMMIT_SHA}`
- `${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:latest`
- Esegue push su registry.
- Scrive su file `./imglist` una riga per container:
- `IMAGE_TAG_<container>=<image-tag-con-sha>`
Supporto multi-arch:
- Se presente `containers/<container>/platform.conf` con chiave `platform=...`, usa `docker buildx build --platform ... --push`.
- In assenza di `platform.conf` usa `docker build` + `docker push` classico.
Input (argomenti):
- `$1`: `REPO_NAME` (passato dalla pipeline con `${{ github.event.repository.name }}`).
- `$2`: `REGISTRY_URL` (passato da `${{ vars.REGISTRY }}`).
Prerequisiti runtime:
- Docker daemon disponibile nel runner.
- Permessi push su registry.
- Struttura cartelle coerente tra `containers/` e `src/`.
### 2) `customize.sh`
Responsabilità funzionale attesa:
- Carica variabili da:
- `env/<ENV>/values.env`
- `properties.env`
- file temporaneo con:
- `TAG=<commit_sha>`
- contenuto di `imglist` (generato da `build_container.sh`)
- Cerca tutti i manifest `kubernetes/**/*.yaml`.
- Esegue sostituzione placeholder nel formato `<CHIAVE>` con i valori trovati.
Input (argomenti):
- `$1`: ambiente (`dev|qa|prod`).
- In pipeline attuale viene usato `dev`.
Placeholder parametrizzabili nei manifest:
- Tutte le chiavi presenti in `env/<ENV>/values.env`.
- Tutte le chiavi presenti in `properties.env`.
- `TAG`.
- `IMAGE_TAG_<container>` (una per ciascun container buildato).
Output:
- Manifest Kubernetes in-place con valori sostituiti.
### 3) `deploy.sh`
Responsabilità:
- Cerca manifest CNPG (`apiVersion: postgresql.cnpg.io/v1`).
- Se trovato:
- Estrae `metadata.name` del cluster PostgreSQL.
- Ricava namespace dal role `namespace-deployer` nel cluster.
- Crea/aggiorna ConfigMap `service-config` con:
- `tipodb=postgres`
- `urldb=<pg_name>.<namespace>.svc.cluster.local`
- Applica tutti i manifest YAML trovati in `kubernetes/` (directory per directory).
- Invoca `/root/work/pipeline/addlistener.sh` per configurare listener HTTPS sul Gateway.
Prerequisiti runtime:
- `kubectl` disponibile nel runner.
- `./kubeconfig` presente e valido.
- Opzionale `yq` (se assente, usa fallback con `awk` per estrazione nome CNPG).
### 4) `addlistener.sh`
Responsabilità:
- Legge la chiave `endpoint` da `properties.env`.
- Calcola token host-based dal dominio.
- Invoca `/root/work/pipeline/add-listener.sh` passando:
- `<endpoint>`
- `https-<token>`
- `<token>-secret`
Input (argomenti):
- `$1` opzionale: path file properties (default `properties.env`).
Comportamento:
- Se file non esiste o `endpoint` non valorizzato: termina senza errore bloccante (`exit 0`).
### 5) `add-listener.sh`
Responsabilità:
- Effettua patch JSON sulla risorsa Gateway Kubernetes:
- Gateway: `main-gateway`
- Namespace: `nginx-gateway`
- Aggiunge un listener HTTPS con certificato TLS da Secret.
- Verifica idempotenza per `name` e `hostname` già presenti.
Input (argomenti):
- `$1`: `hostname`
- `$2`: `name`
- `$3`: `secret-name`
## Parametrizzazione complessiva
### Variabili Gitea Actions
- `vars.REGISTRY`: URL registry target.
### Secret Gitea Actions
- `secrets.HARBOR_USERNAME`
- `secrets.HARBOR_PASSWORD`
- `secrets.KUBECONFIG_DEV`
### File di configurazione repository
- `env/dev/values.env` (o `qa`, `prod` se si cambia argomento di `customize.sh`)
- `properties.env`
- `containers/<service>/platform.conf` (opzionale)
### Parametri indiretti derivati
- Nome repository da `${{ github.event.repository.name }}`.
- SHA commit da `git rev-parse HEAD`.
- Tag immagine per servizio in `imglist`.
## Note operative importanti
1. Il file `imglist` viene creato/appeso da `build_container.sh` e poi letto da `customize.sh`; il job deve mantenere lo stesso workspace tra step.
2. I manifest Kubernetes vengono modificati in-place da `sed -i`.
3. La parte Gateway dipende da:
- presenza di `endpoint` in `properties.env`
- esistenza della risorsa `Gateway/nginx-gateway/main-gateway`
- esistenza del Secret TLS con nome `<token>-secret`

View File

@@ -0,0 +1,36 @@
name: Build and Deploy
on:
push:
branches:
- main
jobs:
docker:
runs-on: POC-Master0
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: ${{ vars.REGISTRY }}
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}
- name: Build container
run: /root/work/pipeline/build_container.sh ${{ github.event.repository.name }} ${{ vars.REGISTRY }}
- name: Create kubeconfig
run: |
echo "${{ secrets.KUBECONFIG_DEV }}" > ./kubeconfig
chmod 600 ./kubeconfig
- name: variables sustitution
run: /root/work/pipeline/customize.sh dev
- name: k8s deploy
run: /root/work/pipeline/deploy.sh

81
pipeline/add-listener.sh Normal file
View File

@@ -0,0 +1,81 @@
#!/bin/bash
# Aggiunge un listener HTTPS direttamente sulla risorsa K8s Gateway
# main-gateway nel namespace nginx-gateway, tramite kubectl patch.
#
# Uso:
# ./add-listener.sh <hostname> <name> <secret-name>
#
# Esempio:
# ./add-listener.sh sonarqube.italiadatacenter.com https-sonarqube sonarqube-secret
set -euo pipefail
GATEWAY_NAME="main-gateway"
GATEWAY_NS="nginx-gateway"
HOSTNAME_VAL="${1:-}"
NAME_VAL="${2:-}"
SECRET_NAME="${3:-}"
if [[ -z "$HOSTNAME_VAL" || -z "$NAME_VAL" || -z "$SECRET_NAME" ]]; then
echo "Uso: $0 <hostname> <name> <secret-name>"
echo "Es.: $0 sonarqube.italiadatacenter.com https-sonarqube sonarqube-secret"
exit 1
fi
# Controlla idempotenza: verifica se il listener esiste già per nome o hostname
EXISTING=$(kubectl get gateway "$GATEWAY_NAME" -n "$GATEWAY_NS" \
-o jsonpath='{.spec.listeners[*].name}')
if echo "$EXISTING" | grep -qw "$NAME_VAL"; then
echo "Attenzione: listener con name '${NAME_VAL}' già presente. Nessuna modifica."
exit 0
fi
EXISTING_HOSTS=$(kubectl get gateway "$GATEWAY_NAME" -n "$GATEWAY_NS" \
-o jsonpath='{.spec.listeners[*].hostname}')
if echo "$EXISTING_HOSTS" | grep -qw "$HOSTNAME_VAL"; then
echo "Attenzione: listener con hostname '${HOSTNAME_VAL}' già presente. Nessuna modifica."
exit 0
fi
# JSON Patch: aggiunge il nuovo listener in append alla lista
PATCH=$(cat <<EOF
[{
"op": "add",
"path": "/spec/listeners/-",
"value": {
"allowedRoutes": {
"namespaces": {
"from": "All"
}
},
"hostname": "${HOSTNAME_VAL}",
"name": "${NAME_VAL}",
"port": 443,
"protocol": "HTTPS",
"tls": {
"certificateRefs": [
{
"group": "",
"kind": "Secret",
"name": "${SECRET_NAME}"
}
],
"mode": "Terminate"
}
}
}]
EOF
)
kubectl patch gateway "$GATEWAY_NAME" \
-n "$GATEWAY_NS" \
--type=json \
-p "$PATCH"
echo "Listener aggiunto alla risorsa ${GATEWAY_NS}/${GATEWAY_NAME}:"
echo " hostname : ${HOSTNAME_VAL}"
echo " name : ${NAME_VAL}"
echo " secret : ${SECRET_NAME}"

34
pipeline/addlistener.sh Normal file
View File

@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail
# Usa properties.env nella directory corrente, oppure un path passato come primo argomento.
PROPERTIES_FILE="${1:-properties.env}"
if [[ ! -f "$PROPERTIES_FILE" ]]; then
echo "Warning: file non trovato: $PROPERTIES_FILE" >&2
exit 0
fi
# Estrae endpoint ignorando commenti e spazi, supportando anche endpoint = valore
endpoint_raw="$({ grep -E '^[[:space:]]*endpoint[[:space:]]*=' "$PROPERTIES_FILE" | tail -n1 || true; } | sed -E 's/^[[:space:]]*endpoint[[:space:]]*=[[:space:]]*//')"
# Rimuove eventuali virgolette e spazi ai bordi
endpoint="$(echo "$endpoint_raw" | sed -E 's/^[[:space:]"\x27]+//; s/[[:space:]"\x27]+$//')"
if [[ -z "$endpoint" ]]; then
echo "La chiave endpoint non e valorizzata in $PROPERTIES_FILE" >&2
exit 0
fi
# Per calcolare il token usa host pulito (senza schema e path)
host_for_token="${endpoint#*://}"
host_for_token="${host_for_token%%/*}"
token="${host_for_token%%.*}"
if [[ -z "$token" ]]; then
echo "Impossibile estrarre il token da endpoint: $endpoint" >&2
exit 1
fi
# Output richiesto: <endpoint> https-<token> <token>-secret
/root/work/pipeline/add-listener.sh $endpoint https-$token $token-secret

View File

@@ -0,0 +1,58 @@
#!/bin/bash
set -e
set -o pipefail
echo "progetto" $1
REPO_NAME=$1
COMMIT_SHA=$(git rev-parse HEAD)
REGISTRY_URL=$2
for dir in containers/*/; do
CONTAINER_NAME=$(basename "$dir")
cp -R src/${CONTAINER_NAME}/. containers/${CONTAINER_NAME}/.
ls -la $dir
# Cerca sia dockerfile che Dockerfile
if [ -f "$dir/dockerfile" ]; then
DOCKERFILE="$dir/dockerfile"
elif [ -f "$dir/Dockerfile" ]; then
DOCKERFILE="$dir/Dockerfile"
else
echo "Dockerfile non trovato in $dir"
continue
fi
IMAGE_TAG="${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:${COMMIT_SHA}"
IMAGE_TAG_LATEST="${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:latest"
echo "IMAGE_TAG_${CONTAINER_NAME}=$IMAGE_TAG" >> ./imglist
PLATFORM_CONF="$dir/platform.conf"
BUILD_PLATFORM=""
if [ -f "$PLATFORM_CONF" ]; then
BUILD_PLATFORM=$(grep -E '^[[:space:]]*platform[[:space:]]*=' "$PLATFORM_CONF" | tail -n 1 | cut -d '=' -f 2- | tr -d '[:space:]')
if [ -n "$BUILD_PLATFORM" ]; then
echo "platform.conf trovato in $dir: uso platform=$BUILD_PLATFORM"
else
echo "platform.conf trovato in $dir ma variabile platform non valorizzata, uso build standard"
fi
fi
if [ -f "$DOCKERFILE" ]; then
if [ -n "$BUILD_PLATFORM" ]; then
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker buildx create --driver docker-container --use
docker buildx inspect --bootstrap
docker buildx build --platform "$BUILD_PLATFORM" -t "$IMAGE_TAG" -t "$IMAGE_TAG_LATEST" -f "$DOCKERFILE" "$dir" --push
else
docker build -t "$IMAGE_TAG" -t "$IMAGE_TAG_LATEST" -f "$DOCKERFILE" "$dir"
docker push "$IMAGE_TAG"
docker push "$IMAGE_TAG_LATEST"
fi
echo "Build e push completate: $IMAGE_TAG"
else
echo "Dockerfile non trovato in $dir"
fi
done

55
pipeline/customize.sh Normal file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
# Usage: ./customize.sh dev|qa|prod
echo "tetst" $IMAGE_TAG_backend
ENV=$1
VALUES_DIR="env/$ENV"
PROPERTIES_FILE="properties.env"
YAML_DIR="kubernetes"
# Estrai l'hash completo del commit e crea una variabile temporanea per la sostituzione
TAG=$(git rev-parse HEAD)
TMP_TAG_FILE=$(mktemp)
TMP_VALUES_FILE=$(mktemp)
echo "TAG=$TAG" > "$TMP_TAG_FILE"
cat ./imglist >> "$TMP_TAG_FILE"
if [ ! -d "$VALUES_DIR" ] || ! ls "$VALUES_DIR"/*.env &>/dev/null; then
echo "Nessun file .env trovato in $VALUES_DIR"
exit 1
fi
if [ ! -f "$PROPERTIES_FILE" ]; then
echo "File $PROPERTIES_FILE non trovato."
exit 1
fi
# Crea una lista key=value temporanea partendo da tutti i file *.env in env/$ENV e aggiunge env dinamico
> "$TMP_VALUES_FILE"
for env_file in "$VALUES_DIR"/*.env; do
cat "$env_file" >> "$TMP_VALUES_FILE"
printf '\n' >> "$TMP_VALUES_FILE"
done
printf '\nenv=%s\n' "$ENV" >> "$TMP_VALUES_FILE"
# Trova tutti i file .yaml nella directory kubernetes e sottodirectory
find "$YAML_DIR" -type f -name "*.yaml" | while read YAML_FILE; do
while IFS='=' read -r key value; do
sed -i "s|<$key>|$value|g" "$YAML_FILE"
done < "$TMP_VALUES_FILE"
while IFS='=' read -r key value; do
sed -i "s|<$key>|$value|g" "$YAML_FILE"
done < "$PROPERTIES_FILE"
# Sostituzione dinamica della chiave TAG
while IFS='=' read -r key value; do
sed -i "s|<$key>|$value|g" "$YAML_FILE"
done < "$TMP_TAG_FILE"
echo "Sostituzione completata per file $YAML_FILE ambiente $ENV."
cat $YAML_FILE
done
cat "$TMP_VALUES_FILE"
rm -f "$TMP_TAG_FILE" "$TMP_VALUES_FILE"

60
pipeline/deploy.sh Normal file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
set -euo pipefail
# Esegue kubectl apply per ogni sottodirectory di kubernetes separatamente
YAML_DIR="kubernetes"
# ---------------------------------------------------------------------------
# Cerca risorse postgresql.cnpg.io/v1 nei manifest e deploya una ConfigMap
# ---------------------------------------------------------------------------
CNPG_FILE=$(grep -rl "postgresql.cnpg.io/v1" "$YAML_DIR" 2>/dev/null | head -1 || true)
if [ -n "$CNPG_FILE" ]; then
echo "Trovata risorsa postgresql.cnpg.io/v1 in: $CNPG_FILE"
# Estrae metadata.name dal manifest CNPG preferendo yq, altrimenti awk
if command -v yq >/dev/null 2>&1; then
PG_NAME=$(yq eval 'select(.apiVersion == "postgresql.cnpg.io/v1") | .metadata.name' "$CNPG_FILE")
else
PG_NAME=$(awk '/postgresql\.cnpg\.io\/v1/{found=1} found && /^metadata:/{meta=1} meta && /^\s+name:/{print $2; exit}' "$CNPG_FILE")
fi
# Ricava il namespace dal Role namespace-deployer presente nel cluster
PG_NS=$(kubectl --kubeconfig=./kubeconfig get role namespace-deployer \
--no-headers \
-o custom-columns='NS:.metadata.namespace' 2>/dev/null | head -1 || true)
#PG_NS="${PG_NS:-default}"
if [ -z "$PG_NAME" ]; then
echo "⚠️ Impossibile estrarre metadata.name dal cluster CNPG, skip ConfigMap." >&2
else
echo " → cluster: $PG_NAME namespace: $PG_NS"
kubectl --kubeconfig=./kubeconfig apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: service-config
namespace: ${PG_NS}
data:
tipodb: "postgres"
urldb: "${PG_NAME}.${PG_NS}.svc.cluster.local"
EOF
echo "ConfigMap db-config deployata in namespace ${PG_NS}."
fi
fi
# ---------------------------------------------------------------------------
# Deploy di tutti i manifest Kubernetes
# ---------------------------------------------------------------------------
find "$YAML_DIR" -type d | while read DIR; do
if ls "$DIR"/*.yaml 1> /dev/null 2>&1; then
echo "Deploy delle risorse nella directory $DIR..."
kubectl --kubeconfig=./kubeconfig apply -f "$DIR"
fi
done
echo "Deploy completato di tutte le directory YAML."
echo "Eseguo check su endpoint da pubblicare"
/root/work/pipeline/addlistener.sh