Compare commits
4
Commits
c431fc36c5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bca0da31f | ||
|
|
30d93adcad | ||
|
|
8cb03c4e81 | ||
|
|
1a2cca2cd2 |
@@ -0,0 +1,90 @@
|
||||
|
||||
kubectl get cluster.postgresql.cnpg.io -n <namespace>
|
||||
kubectl get backup -n <namespace>
|
||||
|
||||
|
||||
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: <pgclustername>-restored
|
||||
spec:
|
||||
instances: <instancenumber>
|
||||
|
||||
storage:
|
||||
size: <dbstorage>
|
||||
storageClass: csi-rbdfs-sc
|
||||
|
||||
walStorage:
|
||||
storageClass: csi-rbdfs-sc
|
||||
size: <dbstorage>
|
||||
|
||||
bootstrap:
|
||||
recovery:
|
||||
source: <pgclustername>-origin
|
||||
|
||||
backup:
|
||||
name: sporteams-backup-20260718020000 # nome dell'oggetto Backup da kubectl get backup -n <namespace>
|
||||
recoveryTarget: # opzionale in assenza applica tutto il journal (wal) a partire dal backup
|
||||
targetTime: "2026-07-18 15:30:00.000000+00" # timestamp in cui si ferma rebuild dal WAL
|
||||
|
||||
externalClusters:
|
||||
- name: <pgclustername>-origin
|
||||
barmanObjectStore:
|
||||
endpointURL: http://minio.minio.svc.cluster.local:9000
|
||||
destinationPath: s3://cnpgbackup2/<pgclustername>
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: minio-backup-creds
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: minio-backup-creds
|
||||
key: ACCESS_SECRET_KEY
|
||||
wal:
|
||||
compression: gzip
|
||||
maxParallel: 2
|
||||
data:
|
||||
compression: gzip
|
||||
immediateCheckpoint: false
|
||||
jobs: 2
|
||||
|
||||
# Dopo il restore, il cluster ripristinato può avere un backup indipendente:
|
||||
backup:
|
||||
barmanObjectStore:
|
||||
endpointURL: http://minio.minio.svc.cluster.local:9000
|
||||
destinationPath: s3://cnpgbackup2/<pgclustername>-restored
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: minio-backup-creds
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: minio-backup-creds
|
||||
key: ACCESS_SECRET_KEY
|
||||
wal:
|
||||
compression: gzip
|
||||
maxParallel: 2
|
||||
data:
|
||||
compression: gzip
|
||||
immediateCheckpoint: false
|
||||
jobs: 2
|
||||
retentionPolicy: "30d"
|
||||
|
||||
postgresql:
|
||||
parameters:
|
||||
max_connections: "300"
|
||||
shared_buffers: "1GB"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: "500m"
|
||||
memory: "1Gi"
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: "2Gi"
|
||||
|
||||
|
||||
### Utility varie ##########
|
||||
kubectl get cluster.postgresql.cnpg.io -n stsharedservices-dev
|
||||
kubectl -n minio exec -it minio-0 -- sh
|
||||
mc alias set local http://localhost:9000 minioadmin KAYQE1QA7uwUZ8uI
|
||||
mc ls local cnpgbackup2
|
||||
@@ -0,0 +1,173 @@
|
||||
# =====================================================================
|
||||
# values.yaml per kube-prometheus-stack su RKE2 (installazione diretta,
|
||||
# senza Rancher Manager)
|
||||
#
|
||||
# Repo: https://prometheus-community.github.io/helm-charts
|
||||
# Chart: kube-prometheus-stack
|
||||
#
|
||||
# Install:
|
||||
# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
|
||||
# helm repo update
|
||||
# helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
|
||||
# -n monitoring --create-namespace \
|
||||
# -f 01-kube-prometheus-stack-values.yaml
|
||||
# =====================================================================
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Prometheus
|
||||
# ---------------------------------------------------------------------
|
||||
prometheus:
|
||||
prometheusSpec:
|
||||
retention: 15d
|
||||
retentionSize: "10GB"
|
||||
|
||||
# Adatta alla storageClass disponibile nel tuo cluster RKE2
|
||||
# (es. longhorn, local-path-provisioner, ceph-rbd, ecc.)
|
||||
storageSpec:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: longhorn # <-- CAMBIA con la tua storageClass
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 50Gi
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
memory: 2Gi
|
||||
|
||||
# Permette a Prometheus di scoprire ServiceMonitor/PodMonitor
|
||||
# creati in QUALSIASI namespace (importante per uno stack "generico"
|
||||
# dove le verticali applicative, es. CNPG, vivono in namespace propri)
|
||||
serviceMonitorSelectorNilUsesHelmValues: false
|
||||
podMonitorSelectorNilUsesHelmValues: false
|
||||
ruleSelectorNilUsesHelmValues: false
|
||||
|
||||
# Su RKE2 il control-plane espone metriche ma va abilitato esplicitamente
|
||||
# lo scraping (vedi sezioni kubeEtcd/kubeControllerManager/kubeScheduler
|
||||
# più sotto). Se il tuo cluster è "hardened" e non espone questi endpoint
|
||||
# sui nodi worker, valuta di disabilitare le relative sezioni.
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Control plane RKE2 — di default questi componenti su RKE2 girano come
|
||||
# pod statici raggiungibili sul nodo server; verificare le porte reali
|
||||
# con `kubectl get pods -n kube-system` e `netstat` sul nodo se lo
|
||||
# scraping non trova i target.
|
||||
# ---------------------------------------------------------------------
|
||||
kubeEtcd:
|
||||
enabled: true
|
||||
service:
|
||||
port: 2381
|
||||
targetPort: 2381
|
||||
|
||||
kubeControllerManager:
|
||||
enabled: true
|
||||
service:
|
||||
port: 10257
|
||||
targetPort: 10257
|
||||
# RKE2 espone il controller-manager in HTTPS con cert self-signed:
|
||||
# potrebbe servire scrapeConfig con tlsConfig.insecureSkipVerify: true
|
||||
# (vedi kube-prometheus-stack docs, sezione "kubeControllerManager").
|
||||
|
||||
kubeScheduler:
|
||||
enabled: true
|
||||
service:
|
||||
port: 10259
|
||||
targetPort: 10259
|
||||
|
||||
# Traefik è deployato di default su RKE2 ed è considerato componente
|
||||
# interno. Se lo usi come ingress controller, aggiungi un ServiceMonitor
|
||||
# dedicato puntato al suo endpoint /metrics (di solito porta 9100).
|
||||
kubeProxy:
|
||||
enabled: true
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Alertmanager
|
||||
# ---------------------------------------------------------------------
|
||||
alertmanager:
|
||||
alertmanagerSpec:
|
||||
retention: 120h
|
||||
storage:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: longhorn # <-- CAMBIA con la tua storageClass
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
memory: 256Mi
|
||||
|
||||
# Config di base; per la config completa vedi il file
|
||||
# 04-alertmanager-config.yaml (AlertmanagerConfig CRD, più gestibile
|
||||
# in modo dichiarativo/GitOps rispetto a alertmanager.config qui).
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Grafana (incluso nel chart)
|
||||
# ---------------------------------------------------------------------
|
||||
grafana:
|
||||
enabled: true
|
||||
defaultDashboardsTimezone: Europe/Rome
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClassName: longhorn # <-- CAMBIA con la tua storageClass
|
||||
size: 5Gi
|
||||
|
||||
# Cambia in produzione: usa un Secret invece di plaintext
|
||||
adminPassword: "CHANGE_ME"
|
||||
|
||||
# Loki come datasource aggiuntivo (vedi file 03-loki-alloy)
|
||||
additionalDataSources:
|
||||
- name: Loki
|
||||
type: loki
|
||||
access: proxy
|
||||
url: http://loki.monitoring.svc.cluster.local:3100
|
||||
isDefault: false
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
memory: 256Mi
|
||||
|
||||
# Import automatico dashboard CNPG (ID 20417) via sidecar dashboards
|
||||
# già incluso nel chart: basta creare un ConfigMap con label
|
||||
# grafana_dashboard=1 (vedi file 02-cnpg-monitoring.yaml in fondo,
|
||||
# sezione dashboard, oppure importa manualmente da Grafana UI).
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# node-exporter
|
||||
# ---------------------------------------------------------------------
|
||||
nodeExporter:
|
||||
enabled: true
|
||||
|
||||
prometheus-node-exporter:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 30Mi
|
||||
limits:
|
||||
memory: 50Mi
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# kube-state-metrics
|
||||
# ---------------------------------------------------------------------
|
||||
kubeStateMetrics:
|
||||
enabled: true
|
||||
|
||||
kube-state-metrics:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
memory: 128Mi
|
||||
@@ -0,0 +1,96 @@
|
||||
# =====================================================================
|
||||
# Monitoring per CloudNativePG (CNPG)
|
||||
#
|
||||
# Applica con: kubectl apply -f 02-cnpg-monitoring.yaml
|
||||
#
|
||||
# Prerequisiti:
|
||||
# - CNPG operator già installato (namespace tipico: cnpg-system)
|
||||
# - kube-prometheus-stack già installato nel namespace "monitoring"
|
||||
# con podMonitorSelectorNilUsesHelmValues: false (vedi file 01),
|
||||
# così Prometheus scopre PodMonitor in TUTTI i namespace.
|
||||
# =====================================================================
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# 1) PodMonitor per l'operatore CNPG stesso
|
||||
# (metriche interne: reconcile loop, controller-runtime, porta 8080)
|
||||
# ---------------------------------------------------------------------
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PodMonitor
|
||||
metadata:
|
||||
name: cnpg-operator
|
||||
namespace: cnpg-system
|
||||
labels:
|
||||
release: kube-prometheus-stack
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: cloudnative-pg
|
||||
podMetricsEndpoints:
|
||||
- port: metrics
|
||||
interval: 30s
|
||||
|
||||
---
|
||||
# ---------------------------------------------------------------------
|
||||
# 2) PodMonitor generico per TUTTI i cluster Postgres gestiti da CNPG
|
||||
#
|
||||
# Nota: se preferisci il metodo "automatico" (enablePodMonitor: true
|
||||
# nella risorsa Cluster), questo PodMonitor manuale NON serve per
|
||||
# quel cluster specifico — ma il manuale è consigliato dalla doc
|
||||
# ufficiale CNPG perché dà controllo esplicito sul lifecycle,
|
||||
# indipendente dal singolo Cluster CR.
|
||||
#
|
||||
# Il selettore usa la label standard "cnpg.io/cluster" che CNPG
|
||||
# applica automaticamente a tutti i pod di ogni cluster gestito,
|
||||
# quindi UN SOLO PodMonitor copre TUTTI i cluster Postgres presenti
|
||||
# nel namespace target (adatta matchExpressions se i tuoi cluster
|
||||
# vivono in namespace diversi: serve un PodMonitor per namespace,
|
||||
# oppure passa a un selector cross-namespace via namespaceSelector).
|
||||
# ---------------------------------------------------------------------
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PodMonitor
|
||||
metadata:
|
||||
name: cnpg-clusters
|
||||
namespace: monitoring
|
||||
labels:
|
||||
release: kube-prometheus-stack
|
||||
spec:
|
||||
namespaceSelector:
|
||||
any: true # scropa i pod CNPG in QUALSIASI namespace
|
||||
selector:
|
||||
matchExpressions:
|
||||
- key: cnpg.io/cluster
|
||||
operator: Exists
|
||||
podMetricsEndpoints:
|
||||
- port: metrics # exporter per-istanza, porta 9187
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
path: /metrics
|
||||
|
||||
---
|
||||
# ---------------------------------------------------------------------
|
||||
# 3) Import automatico dashboard CNPG ufficiale (grafana.com ID 20417)
|
||||
# tramite sidecar dashboards di Grafana (già incluso nel chart
|
||||
# kube-prometheus-stack). Basta un ConfigMap con la label
|
||||
# "grafana_dashboard: '1'" nel namespace di Grafana.
|
||||
#
|
||||
# In alternativa più semplice: Grafana UI -> Dashboards -> Import
|
||||
# -> inserisci ID 20417 -> seleziona datasource Prometheus.
|
||||
# ---------------------------------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cnpg-grafana-dashboard
|
||||
namespace: monitoring
|
||||
labels:
|
||||
grafana_dashboard: "1"
|
||||
data:
|
||||
cnpg-dashboard.json: |
|
||||
{
|
||||
"annotations": {"list": []},
|
||||
"title": "CloudNativePG (import via ID 20417)",
|
||||
"__inputs": [],
|
||||
"__requires": [],
|
||||
"schemaVersion": 39,
|
||||
"panels": [],
|
||||
"_comment": "Placeholder: sostituisci con l'export JSON completo scaricato da https://grafana.com/grafana/dashboards/20417 per avere la dashboard reale, oppure importa manualmente da Grafana UI usando l'ID."
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
# =====================================================================
|
||||
# PrometheusRule: alert infrastrutturali + CNPG
|
||||
# Applica con: kubectl apply -f 03-alert-rules.yaml
|
||||
# =====================================================================
|
||||
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: infra-alerts
|
||||
namespace: monitoring
|
||||
labels:
|
||||
release: kube-prometheus-stack
|
||||
spec:
|
||||
groups:
|
||||
- name: node-health
|
||||
rules:
|
||||
- alert: NodeDown
|
||||
expr: up{job="node-exporter"} == 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Nodo {{ $labels.instance }} irraggiungibile"
|
||||
description: "node-exporter non risponde da 5 minuti su {{ $labels.instance }}."
|
||||
|
||||
- alert: NodeHighCPU
|
||||
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "CPU alta su {{ $labels.instance }}"
|
||||
description: "Uso CPU sopra 85% da 10 minuti."
|
||||
|
||||
- alert: NodeHighMemory
|
||||
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Memoria alta su {{ $labels.instance }}"
|
||||
description: "Uso memoria sopra 90% da 10 minuti."
|
||||
|
||||
- alert: NodeDiskSpaceLow
|
||||
expr: (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100 < 15
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Spazio disco basso su {{ $labels.instance }} ({{ $labels.mountpoint }})"
|
||||
description: "Meno del 15% di spazio libero da 10 minuti."
|
||||
|
||||
- alert: NodeDiskSpaceCritical
|
||||
expr: (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100 < 5
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Spazio disco CRITICO su {{ $labels.instance }} ({{ $labels.mountpoint }})"
|
||||
description: "Meno del 5% di spazio libero da 5 minuti."
|
||||
|
||||
- name: kubernetes-health
|
||||
rules:
|
||||
- alert: KubePodCrashLooping
|
||||
expr: increase(kube_pod_container_status_restarts_total[15m]) > 3
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} in crash loop"
|
||||
description: "Più di 3 restart negli ultimi 15 minuti."
|
||||
|
||||
- alert: KubePodNotReady
|
||||
expr: sum by (namespace, pod) (kube_pod_status_phase{phase=~"Pending|Unknown"}) > 0
|
||||
for: 15m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} non Ready da 15 minuti"
|
||||
|
||||
- alert: KubeDeploymentReplicasMismatch
|
||||
expr: kube_deployment_spec_replicas != kube_deployment_status_replicas_available
|
||||
for: 15m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Deployment {{ $labels.namespace }}/{{ $labels.deployment }} con repliche non allineate"
|
||||
|
||||
- alert: KubePersistentVolumeFillingUp
|
||||
expr: (kubelet_volume_stats_available_bytes / kubelet_volume_stats_capacity_bytes) * 100 < 10
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "PVC {{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} quasi pieno"
|
||||
description: "Meno del 10% di spazio libero sul volume."
|
||||
|
||||
- alert: EtcdInsufficientMembers
|
||||
expr: sum(up{job="kube-etcd"} == 1) < ((count(up{job="kube-etcd"}) + 1) / 2)
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Quorum etcd a rischio"
|
||||
description: "Meno della maggioranza dei membri etcd è raggiungibile."
|
||||
|
||||
- name: tls-certificates
|
||||
rules:
|
||||
# Richiede blackbox_exporter con probe HTTPS configurato sui tuoi
|
||||
# endpoint esterni; vedi nota nel file README.
|
||||
- alert: TLSCertExpiringSoon
|
||||
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 15
|
||||
for: 1h
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Certificato TLS per {{ $labels.instance }} in scadenza"
|
||||
description: "Meno di 15 giorni alla scadenza del certificato."
|
||||
|
||||
- alert: TLSCertExpired
|
||||
expr: probe_ssl_earliest_cert_expiry - time() < 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Certificato TLS per {{ $labels.instance }} SCADUTO"
|
||||
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: cnpg-alerts
|
||||
namespace: monitoring
|
||||
labels:
|
||||
release: kube-prometheus-stack
|
||||
spec:
|
||||
groups:
|
||||
- name: cnpg-health
|
||||
rules:
|
||||
- alert: CNPGClusterNotHealthy
|
||||
expr: cnpg_collector_up == 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Cluster CNPG {{ $labels.namespace }}/{{ $labels.pod }} non raggiungibile"
|
||||
description: "L'exporter CNPG non risponde da 5 minuti."
|
||||
|
||||
- alert: CNPGInstanceInUnexpectedRecovery
|
||||
expr: cnpg_pg_replication_in_recovery == 1 and on(pod) cnpg_pg_replication_is_wal_receiver_up == 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Istanza {{ $labels.pod }} in recovery inatteso"
|
||||
description: "Verifica se il ruolo primary/standby è quello atteso."
|
||||
|
||||
- alert: CNPGReplicationLagHigh
|
||||
expr: cnpg_pg_replication_lag > 300
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Replication lag alto su {{ $labels.pod }}"
|
||||
description: "Lag di replica superiore a 300 secondi."
|
||||
|
||||
- alert: CNPGWALArchivingFailing
|
||||
expr: increase(cnpg_pg_stat_archiver_failed_count[15m]) > 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Archiviazione WAL fallita su {{ $labels.pod }}"
|
||||
description: "Almeno un fallimento di archiviazione WAL negli ultimi 15 minuti — rischio per i backup e il point-in-time recovery."
|
||||
|
||||
- alert: CNPGBackupFailed
|
||||
expr: cnpg_collector_last_failed_backup_timestamp > cnpg_collector_last_available_backup_timestamp
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Ultimo backup fallito per {{ $labels.namespace }}/{{ $labels.pod }}"
|
||||
|
||||
- alert: CNPGNoRecentBackup
|
||||
expr: (time() - cnpg_collector_last_available_backup_timestamp) > 86400
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Nessun backup riuscito nelle ultime 24h per {{ $labels.namespace }}/{{ $labels.pod }}"
|
||||
|
||||
- alert: CNPGConnectionsNearLimit
|
||||
expr: (cnpg_backends_total / cnpg_pg_settings_setting{name="max_connections"}) * 100 > 85
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Connessioni Postgres vicine al limite su {{ $labels.pod }}"
|
||||
description: "Oltre l'85% di max_connections in uso."
|
||||
|
||||
# Nota: i nomi esatti delle metriche cnpg_* possono variare leggermente
|
||||
# tra versioni dell'operatore CNPG. Verifica sempre su
|
||||
# http://localhost:9090/graph con `{__name__=~"cnpg_.+"}` dopo il
|
||||
# port-forward a Prometheus, e allinea le espressioni se necessario:
|
||||
# kubectl port-forward -n monitoring svc/kube-prometheus-stack-prometheus 9090
|
||||
@@ -0,0 +1,105 @@
|
||||
# =====================================================================
|
||||
# Loki + Grafana Alloy — log aggregation
|
||||
#
|
||||
# Loki:
|
||||
# helm repo add grafana https://grafana.github.io/helm-charts
|
||||
# helm repo update
|
||||
# helm install loki grafana/loki -n monitoring -f loki-values.yaml
|
||||
#
|
||||
# Alloy (raccolta log, DaemonSet su ogni nodo RKE2):
|
||||
# helm install alloy grafana/alloy -n monitoring -f alloy-values.yaml
|
||||
#
|
||||
# Questo file contiene ENTRAMBI i set di values, separati da "---";
|
||||
# salvali in due file distinti se preferisci due comandi helm separati.
|
||||
# =====================================================================
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# loki-values.yaml (modalità single-binary, adatta a partire in piccolo;
|
||||
# per HA/produzione a lungo termine valutare la modalità "simple scalable"
|
||||
# con backend object storage S3-compatibile, es. MinIO)
|
||||
# ---------------------------------------------------------------------
|
||||
deploymentMode: SingleBinary
|
||||
|
||||
loki:
|
||||
auth_enabled: false
|
||||
commonConfig:
|
||||
replication_factor: 1
|
||||
storage:
|
||||
type: filesystem
|
||||
|
||||
singleBinary:
|
||||
replicas: 1
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: longhorn # <-- CAMBIA con la tua storageClass
|
||||
size: 50Gi
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
memory: 1Gi
|
||||
|
||||
# Disabilita i componenti "scalable" non necessari in modalità SingleBinary
|
||||
read:
|
||||
replicas: 0
|
||||
write:
|
||||
replicas: 0
|
||||
backend:
|
||||
replicas: 0
|
||||
|
||||
gateway:
|
||||
enabled: false
|
||||
|
||||
test:
|
||||
enabled: false
|
||||
|
||||
---
|
||||
# ---------------------------------------------------------------------
|
||||
# alloy-values.yaml (DaemonSet che raccoglie log da tutti i nodi/pod
|
||||
# e li invia a Loki)
|
||||
# ---------------------------------------------------------------------
|
||||
alloy:
|
||||
configMap:
|
||||
content: |
|
||||
discovery.kubernetes "pods" {
|
||||
role = "pod"
|
||||
}
|
||||
|
||||
discovery.relabel "pods" {
|
||||
targets = discovery.kubernetes.pods.targets
|
||||
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_namespace"]
|
||||
target_label = "namespace"
|
||||
}
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_name"]
|
||||
target_label = "pod"
|
||||
}
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_container_name"]
|
||||
target_label = "container"
|
||||
}
|
||||
}
|
||||
|
||||
loki.source.kubernetes "pods" {
|
||||
targets = discovery.relabel.pods.output
|
||||
forward_to = [loki.write.default.receiver]
|
||||
}
|
||||
|
||||
loki.write "default" {
|
||||
endpoint {
|
||||
url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push"
|
||||
}
|
||||
}
|
||||
|
||||
controller:
|
||||
type: daemonset
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
memory: 256Mi
|
||||
@@ -0,0 +1,62 @@
|
||||
# =====================================================================
|
||||
# AlertmanagerConfig — routing alert per severità
|
||||
# Applica con: kubectl apply -f 05-alertmanager-config.yaml
|
||||
#
|
||||
# Richiede i Secret con le credenziali (webhook Slack, integration key
|
||||
# PagerDuty) creati separatamente, es.:
|
||||
#
|
||||
# kubectl create secret generic alertmanager-slack \
|
||||
# -n monitoring --from-literal=url='https://hooks.slack.com/services/XXX/YYY/ZZZ'
|
||||
#
|
||||
# kubectl create secret generic alertmanager-pagerduty \
|
||||
# -n monitoring --from-literal=serviceKey='YOUR_PAGERDUTY_INTEGRATION_KEY'
|
||||
# =====================================================================
|
||||
|
||||
apiVersion: monitoring.coreos.com/v1alpha1
|
||||
kind: AlertmanagerConfig
|
||||
metadata:
|
||||
name: routing-config
|
||||
namespace: monitoring
|
||||
labels:
|
||||
# deve matchare alertmanagerConfigSelector del chart
|
||||
# (di default kube-prometheus-stack seleziona per release label)
|
||||
release: kube-prometheus-stack
|
||||
spec:
|
||||
route:
|
||||
groupBy: ["alertname", "namespace"]
|
||||
groupWait: 30s
|
||||
groupInterval: 5m
|
||||
repeatInterval: 4h
|
||||
receiver: "slack-default"
|
||||
routes:
|
||||
- matchers:
|
||||
- name: severity
|
||||
value: critical
|
||||
matchType: "="
|
||||
receiver: "pagerduty-critical"
|
||||
continue: true # invia ANCHE a slack-default per visibilità
|
||||
- matchers:
|
||||
- name: severity
|
||||
value: warning
|
||||
matchType: "="
|
||||
receiver: "slack-default"
|
||||
|
||||
receivers:
|
||||
- name: "slack-default"
|
||||
slackConfigs:
|
||||
- apiURL:
|
||||
name: alertmanager-slack
|
||||
key: url
|
||||
channel: "#alerts-k8s"
|
||||
sendResolved: true
|
||||
title: '{{ "{{" }} .CommonAnnotations.summary {{ "}}" }}'
|
||||
text: '{{ "{{" }} .CommonAnnotations.description {{ "}}" }}'
|
||||
|
||||
- name: "pagerduty-critical"
|
||||
pagerdutyConfigs:
|
||||
- serviceKey:
|
||||
name: alertmanager-pagerduty
|
||||
key: serviceKey
|
||||
severity: "critical"
|
||||
description: '{{ "{{" }} .CommonAnnotations.summary {{ "}}" }}'
|
||||
sendResolved: true
|
||||
@@ -0,0 +1,121 @@
|
||||
# Stack di monitoring/alerting per RKE2 — CNPG come verticale iniziale
|
||||
|
||||
Stack generico, installato senza passare da Rancher Manager: kube-prometheus-stack
|
||||
(Prometheus Operator + Prometheus + Alertmanager + Grafana + node-exporter +
|
||||
kube-state-metrics) + Loki/Alloy per i log + monitoring dedicato per CloudNativePG.
|
||||
|
||||
## Ordine di installazione
|
||||
|
||||
1. **Namespace + kube-prometheus-stack**
|
||||
```bash
|
||||
kubectl create namespace monitoring
|
||||
|
||||
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
|
||||
helm repo add grafana https://grafana.github.io/helm-charts
|
||||
helm repo update
|
||||
|
||||
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
|
||||
-n monitoring \
|
||||
-f 01-kube-prometheus-stack-values.yaml
|
||||
```
|
||||
|
||||
kube-prometheus-stack has been installed. Check its status by running:
|
||||
kubectl --namespace monitoring get pods -l "release=kube-prometheus-stack"
|
||||
|
||||
Get Grafana 'admin' user password by running:
|
||||
|
||||
kubectl --namespace monitoring get secrets kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
|
||||
|
||||
Access Grafana local instance:
|
||||
|
||||
export POD_NAME=$(kubectl --namespace monitoring get pod -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=kube-prometheus-stack" -oname)
|
||||
kubectl --namespace monitoring port-forward $POD_NAME 3000
|
||||
|
||||
Get your grafana admin user password by running:
|
||||
|
||||
kubectl get secret --namespace monitoring -l app.kubernetes.io/component=admin-secret -o jsonpath="{.items[0].data.admin-password}" | base64 --decode ; echo
|
||||
|
||||
|
||||
Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.
|
||||
|
||||
kubectl run -it --rm debug --image=curlimages/curl -n monitoring --restart=Never -- curl -v http://kube-prometheus-stack-grafana.monitoring.svc.cluster.local:80/
|
||||
|
||||
|
||||
|
||||
|
||||
2. **Monitoring CNPG** (dopo che l'operatore CNPG è già installato)
|
||||
```bash
|
||||
kubectl apply -f 02-cnpg-monitoring.yaml
|
||||
```
|
||||
|
||||
3. **Alert rules** (infra + CNPG)
|
||||
```bash
|
||||
kubectl apply -f 03-alert-rules.yaml
|
||||
```
|
||||
|
||||
4. **Loki + Alloy** (log aggregation) — dividi il file in due se preferisci
|
||||
comandi helm separati (`loki-values.yaml` / `alloy-values.yaml`)
|
||||
```bash
|
||||
helm install loki grafana/loki -n monitoring -f 04-loki-alloy-values.yaml
|
||||
helm install alloy grafana/alloy -n monitoring -f 04-loki-alloy-values.yaml
|
||||
```
|
||||
|
||||
5. **Alertmanager routing** — crea prima i Secret con le credenziali reali,
|
||||
poi applica la config
|
||||
```bash
|
||||
kubectl create secret generic alertmanager-slack \
|
||||
-n monitoring --from-literal=url='<SLACK_WEBHOOK_URL>'
|
||||
kubectl create secret generic alertmanager-pagerduty \
|
||||
-n monitoring --from-literal=serviceKey='<PAGERDUTY_KEY>'
|
||||
|
||||
kubectl apply -f 05-alertmanager-config.yaml
|
||||
```
|
||||
|
||||
## Cose da personalizzare prima di applicare in un cluster reale
|
||||
|
||||
- **storageClassName** in tutti i file (`longhorn` è solo un placeholder —
|
||||
sostituisci con la storageClass effettivamente disponibile sul tuo RKE2,
|
||||
verificabile con `kubectl get storageclass`).
|
||||
- **adminPassword** di Grafana nel file 01 — spostalo in un Secret,
|
||||
non lasciarlo in chiaro nei values in produzione.
|
||||
- **Nomi metriche CNPG** — verifica che corrispondano alla versione esatta
|
||||
dell'operatore CNPG installata (vedi nota in fondo al file 03).
|
||||
- **Dashboard CNPG** — il ConfigMap nel file 02 è un placeholder; scarica
|
||||
il JSON reale da https://grafana.com/grafana/dashboards/20417 e
|
||||
sostituiscilo, oppure importa manualmente da Grafana UI (Dashboards →
|
||||
Import → ID 20417).
|
||||
- **Control-plane RKE2** (kubeEtcd/kubeControllerManager/kubeScheduler nel
|
||||
file 01) — porte ed endpoint possono variare in base a come RKE2 è
|
||||
configurato (hardening CIS, profili custom). Verifica con
|
||||
`kubectl get pods -n kube-system` quali pod statici girano e su quali
|
||||
porte, e correggi se necessario.
|
||||
- **AlertmanagerConfig selector** — deve combaciare con
|
||||
`alertmanagerConfigSelector` impostato dal chart (di default seleziona
|
||||
per label `release: <helm-release-name>`); se hai chiamato la release
|
||||
diversamente da `kube-prometheus-stack`, aggiorna la label.
|
||||
|
||||
## Verifiche post-installazione
|
||||
|
||||
```bash
|
||||
# Prometheus targets (verifica che CNPG, node-exporter, ecc. siano UP)
|
||||
kubectl port-forward -n monitoring svc/kube-prometheus-stack-prometheus 9090
|
||||
# -> http://localhost:9090/targets
|
||||
|
||||
# Grafana
|
||||
kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80
|
||||
# -> http://localhost:3000 (utente: admin, password: quella impostata)
|
||||
|
||||
# Alertmanager
|
||||
kubectl port-forward -n monitoring svc/kube-prometheus-stack-alertmanager 9093
|
||||
# -> http://localhost:9093
|
||||
```
|
||||
|
||||
## Estensioni future (non incluse in questa prima fase)
|
||||
|
||||
- **blackbox_exporter** — probe HTTP/TCP/TLS per endpoint applicativi
|
||||
esterni e monitoraggio scadenza certificati (le regole `TLSCert*` nel
|
||||
file 03 presuppongono questo exporter già installato).
|
||||
- **Tempo + OpenTelemetry Collector** — tracing distribuito quando ci
|
||||
saranno più verticali applicative da correlare.
|
||||
- **Thanos/Mimir** — retention lunga e vista multi-cluster, se in futuro
|
||||
servirà storicizzare oltre i 15gg locali o aggregare più cluster RKE2.
|
||||
@@ -0,0 +1,38 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kube-prometheus-stack-grafana
|
||||
namespace: monitoring
|
||||
labels:
|
||||
app.kubernetes.io/instance: kube-prometheus-stack
|
||||
app.kubernetes.io/name: grafana
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/instance: kube-prometheus-stack
|
||||
app.kubernetes.io/name: grafana
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 80
|
||||
targetPort: grafana
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: grafana
|
||||
namespace: monitoring
|
||||
spec:
|
||||
hostnames:
|
||||
- grafana.internal
|
||||
parentRefs:
|
||||
- name: main-gateway
|
||||
namespace: nginx-gateway
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: kube-prometheus-stack-grafana
|
||||
port: 80
|
||||
@@ -0,0 +1,438 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# export-k8s-manifests.sh
|
||||
#
|
||||
# Ricostruisce i manifest YAML dichiarativi delle risorse installate
|
||||
# su un cluster Kubernetes, per una lista di namespace data in input.
|
||||
#
|
||||
# Rimuove i campi "runtime" (status, managedFields, resourceVersion, uid,
|
||||
# creationTimestamp, ownerReferences generate da controller, ecc.) in modo
|
||||
# da produrre YAML riapplicabili con `kubectl apply -f`.
|
||||
#
|
||||
# Uso:
|
||||
# ./export-k8s-manifests.sh -n ns1,ns2,ns3 [-o output_dir] [-c kubeconfig] [-k context]
|
||||
# ./export-k8s-manifests.sh -f namespaces.txt [-o output_dir]
|
||||
#
|
||||
# Dipendenze:
|
||||
# - kubectl (obbligatorio)
|
||||
# - yq (v4+, https://github.com/mikefarah/yq) — consigliato per pulizia campi
|
||||
# Se assente, viene usato un fallback con python3.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Default
|
||||
# ------------------------------------------------------------------
|
||||
OUTPUT_DIR="./k8s-export-$(date +%Y%m%d-%H%M%S)"
|
||||
NAMESPACES=()
|
||||
NAMESPACE_FILE=""
|
||||
KUBECONFIG_OPT=()
|
||||
CONTEXT_OPT=()
|
||||
INCLUDE_SECRETS=false
|
||||
|
||||
# Tipi di risorsa namespaced da esportare.
|
||||
# Personalizza questa lista secondo le tue esigenze.
|
||||
RESOURCE_TYPES=(
|
||||
configmap
|
||||
secret
|
||||
service
|
||||
serviceaccount
|
||||
deployment
|
||||
statefulset
|
||||
daemonset
|
||||
replicaset
|
||||
job
|
||||
cronjob
|
||||
ingress
|
||||
networkpolicy
|
||||
poddisruptionbudget
|
||||
horizontalpodautoscaler
|
||||
persistentvolumeclaim
|
||||
role
|
||||
rolebinding
|
||||
|
||||
# --- API Gateway (namespaced) -------------------------------------
|
||||
# Gateway API standard (gateway.networking.k8s.io)
|
||||
gateway
|
||||
httproute
|
||||
grpcroute
|
||||
tcproute
|
||||
tlsroute
|
||||
referencegrant
|
||||
# Istio
|
||||
virtualservice
|
||||
destinationrule
|
||||
# Traefik
|
||||
ingressroute
|
||||
middleware
|
||||
# Kong
|
||||
kongingress
|
||||
kongplugin
|
||||
kongconsumer
|
||||
# APISIX
|
||||
apisixroute
|
||||
apisixpluginconfig
|
||||
# NGINX Gateway Fabric — CRD di estensione (gateway.nginx.org)
|
||||
# Le risorse Gateway API standard (gateway, httproute, gatewayclass, ecc.)
|
||||
# sono già coperte sopra; queste sono le estensioni specifiche NGF per
|
||||
# configurazioni non esprimibili nello standard Gateway API.
|
||||
nginxproxy
|
||||
clientsettingspolicy
|
||||
observabilitypolicy
|
||||
upstreamsettingspolicy
|
||||
snippetsfilter
|
||||
|
||||
# CRD comuni — decommenta/aggiungi secondo gli operator installati
|
||||
cluster.postgresql.cnpg.io # CloudNativePG Cluster
|
||||
scheduledbackup.postgresql.cnpg.io
|
||||
certificate.cert-manager.io
|
||||
)
|
||||
|
||||
# Tipi di risorsa CLUSTER-SCOPED da esportare.
|
||||
# Vengono filtrate per pertinenza ai namespace indicati dove possibile
|
||||
# (es. ClusterRoleBinding con subject in uno dei namespace, PV con claim
|
||||
# in uno dei namespace). Le risorse "globali" (StorageClass, GatewayClass,
|
||||
# IngressClass, PriorityClass) vengono esportate sempre, essendo poche
|
||||
# e prive di legame diretto con un namespace.
|
||||
CLUSTER_RESOURCE_TYPES_FILTERED=(
|
||||
clusterrolebinding
|
||||
persistentvolume
|
||||
)
|
||||
CLUSTER_RESOURCE_TYPES_GLOBAL=(
|
||||
storageclass
|
||||
priorityclass
|
||||
# API Gateway (cluster-scoped)
|
||||
gatewayclass
|
||||
ingressclass
|
||||
# Da valutare se esportare sempre (potenzialmente numerose/rumorose):
|
||||
# clusterrole
|
||||
# customresourcedefinition
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Parsing argomenti
|
||||
# ------------------------------------------------------------------
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Uso: $0 -n ns1,ns2,ns3 [opzioni]
|
||||
$0 -f namespaces.txt [opzioni]
|
||||
|
||||
Opzioni:
|
||||
-n <ns1,ns2,...> Lista namespace separati da virgola
|
||||
-f <file> File con un namespace per riga (alternativo a -n)
|
||||
-o <dir> Directory di output (default: ${OUTPUT_DIR})
|
||||
-c <kubeconfig> Path al kubeconfig da usare
|
||||
-k <context> Nome del context kubectl da usare
|
||||
-s Includi anche i Secret (default: esclusi per sicurezza)
|
||||
-h Mostra questo help
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
while getopts "n:f:o:c:k:sh" opt; do
|
||||
case "$opt" in
|
||||
n) IFS=',' read -r -a NAMESPACES <<< "$OPTARG" ;;
|
||||
f) NAMESPACE_FILE="$OPTARG" ;;
|
||||
o) OUTPUT_DIR="$OPTARG" ;;
|
||||
c) KUBECONFIG_OPT=(--kubeconfig "$OPTARG") ;;
|
||||
k) CONTEXT_OPT=(--context "$OPTARG") ;;
|
||||
s) INCLUDE_SECRETS=true ;;
|
||||
h) usage ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n "$NAMESPACE_FILE" ]]; then
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
||||
NAMESPACES+=("$line")
|
||||
done < "$NAMESPACE_FILE"
|
||||
fi
|
||||
|
||||
if [[ ${#NAMESPACES[@]} -eq 0 ]]; then
|
||||
echo "Errore: nessun namespace specificato (usa -n o -f)" >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ "$INCLUDE_SECRETS" == false ]]; then
|
||||
RESOURCE_TYPES=("${RESOURCE_TYPES[@]/secret}")
|
||||
fi
|
||||
|
||||
KCTL=(kubectl "${KUBECONFIG_OPT[@]}" "${CONTEXT_OPT[@]}")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Verifica dipendenze
|
||||
# ------------------------------------------------------------------
|
||||
command -v kubectl >/dev/null 2>&1 || { echo "Errore: kubectl non trovato" >&2; exit 1; }
|
||||
|
||||
USE_YQ=false
|
||||
if command -v yq >/dev/null 2>&1; then
|
||||
USE_YQ=true
|
||||
elif command -v python3 >/dev/null 2>&1; then
|
||||
USE_YQ=false
|
||||
else
|
||||
echo "Errore: serve 'yq' oppure 'python3' per la pulizia dei manifest" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Verifica connessione al cluster..."
|
||||
"${KCTL[@]}" cluster-info >/dev/null 2>&1 || { echo "Errore: impossibile contattare il cluster" >&2; exit 1; }
|
||||
CLUSTER_NAME=$("${KCTL[@]}" config current-context 2>/dev/null || echo "unknown-cluster")
|
||||
echo "Cluster context: ${CLUSTER_NAME}"
|
||||
echo "Output dir: ${OUTPUT_DIR}"
|
||||
echo "Namespace da esportare: ${NAMESPACES[*]}"
|
||||
echo "Secret inclusi: ${INCLUDE_SECRETS}"
|
||||
echo
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# Gli script Python vengono scritti su file temporanei perché in bash
|
||||
# `python3 - <<'HEREDOC'` sovrascrive lo stdin del processo con l'heredoc,
|
||||
# rendendo impossibile leggere i dati dalla pipeline (il fd 0 è già consumato
|
||||
# prima che il codice Python arrivi a sys.stdin.read()).
|
||||
_PY_CLEAN=$(mktemp)
|
||||
_PY_FILTER=$(mktemp)
|
||||
trap 'rm -f "$_PY_CLEAN" "$_PY_FILTER"' EXIT
|
||||
|
||||
if [[ "$USE_YQ" == false ]]; then
|
||||
cat > "$_PY_CLEAN" << 'PYCLEAN'
|
||||
import sys, yaml
|
||||
doc = yaml.safe_load(sys.stdin)
|
||||
if doc is None:
|
||||
sys.exit(0)
|
||||
doc.pop("status", None)
|
||||
md = doc.get("metadata", {})
|
||||
for f in ("uid", "resourceVersion", "generation", "creationTimestamp",
|
||||
"selfLink", "managedFields", "ownerReferences"):
|
||||
md.pop(f, None)
|
||||
ann = md.get("annotations")
|
||||
if ann:
|
||||
ann.pop("kubectl.kubernetes.io/last-applied-configuration", None)
|
||||
ann.pop("deployment.kubernetes.io/revision", None)
|
||||
if not ann:
|
||||
md.pop("annotations", None)
|
||||
spec = doc.get("spec", {})
|
||||
spec.pop("clusterIP", None)
|
||||
spec.pop("clusterIPs", None)
|
||||
tmpl = spec.get("template", {})
|
||||
if isinstance(tmpl, dict):
|
||||
tmpl.get("metadata", {}).pop("creationTimestamp", None)
|
||||
print(yaml.dump(doc, sort_keys=False, default_flow_style=False), end="")
|
||||
PYCLEAN
|
||||
|
||||
cat > "$_PY_FILTER" << 'PYFILTER'
|
||||
import json, os, sys
|
||||
mode = sys.argv[1]
|
||||
data = json.load(sys.stdin)
|
||||
ns_set = set(os.environ.get("NS_CSV", "").split())
|
||||
for item in data.get("items", []):
|
||||
if mode == "subjects":
|
||||
subs = item.get("subjects") or []
|
||||
match = any(s.get("namespace") in ns_set for s in subs)
|
||||
else:
|
||||
claim = (item.get("spec") or {}).get("claimRef") or {}
|
||||
match = claim.get("namespace") in ns_set
|
||||
if match:
|
||||
print(item["metadata"]["name"])
|
||||
PYFILTER
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Funzione di pulizia dei campi runtime
|
||||
# ------------------------------------------------------------------
|
||||
clean_manifest_yq() {
|
||||
# Rimuove campi generati dal cluster, mantenendo lo spec dichiarativo
|
||||
yq eval '
|
||||
del(.status) |
|
||||
del(.metadata.uid) |
|
||||
del(.metadata.resourceVersion) |
|
||||
del(.metadata.generation) |
|
||||
del(.metadata.creationTimestamp) |
|
||||
del(.metadata.selfLink) |
|
||||
del(.metadata.managedFields) |
|
||||
del(.metadata.annotations."kubectl.kubernetes.io/last-applied-configuration") |
|
||||
del(.metadata.annotations."deployment.kubernetes.io/revision") |
|
||||
del(.metadata.ownerReferences) |
|
||||
del(.spec.clusterIP) |
|
||||
del(.spec.clusterIPs) |
|
||||
del(.spec.template.metadata.creationTimestamp)
|
||||
' -
|
||||
}
|
||||
|
||||
clean_manifest_python() {
|
||||
# Usa il file temporaneo creato all'avvio; l'heredoc inline non funziona
|
||||
# in pipeline perché sovrascrive lo stdin del processo.
|
||||
python3 "$_PY_CLEAN"
|
||||
}
|
||||
|
||||
clean_manifest() {
|
||||
if [[ "$USE_YQ" == true ]]; then
|
||||
clean_manifest_yq
|
||||
else
|
||||
clean_manifest_python
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Export per namespace / kind / risorsa
|
||||
# ------------------------------------------------------------------
|
||||
SUMMARY_FILE="${OUTPUT_DIR}/EXPORT_SUMMARY.md"
|
||||
{
|
||||
echo "# Export manifest Kubernetes"
|
||||
echo
|
||||
echo "- Cluster context: \`${CLUSTER_NAME}\`"
|
||||
echo "- Data export: $(date -u +'%Y-%m-%dT%H:%M:%SZ')"
|
||||
echo "- Namespace: ${NAMESPACES[*]}"
|
||||
echo
|
||||
echo "| Namespace | Kind | Nome | File |"
|
||||
echo "|---|---|---|---|"
|
||||
} > "$SUMMARY_FILE"
|
||||
|
||||
for ns in "${NAMESPACES[@]}"; do
|
||||
echo "== Namespace: ${ns} =="
|
||||
|
||||
if ! "${KCTL[@]}" get namespace "$ns" >/dev/null 2>&1; then
|
||||
echo " ATTENZIONE: namespace '${ns}' non trovato, salto." >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
NS_DIR="${OUTPUT_DIR}/${ns}"
|
||||
mkdir -p "$NS_DIR"
|
||||
|
||||
for kind in "${RESOURCE_TYPES[@]}"; do
|
||||
[[ -z "$kind" ]] && continue
|
||||
|
||||
# Il controllo api-resources è omesso: kubectl get <kind> 2>/dev/null || true
|
||||
# gestisce già i resource type non esistenti restituendo NAMES vuoto.
|
||||
NAMES=$("${KCTL[@]}" -n "$ns" get "$kind" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
|
||||
[[ -z "$NAMES" ]] && continue
|
||||
|
||||
KIND_DIR="${NS_DIR}/${kind}"
|
||||
mkdir -p "$KIND_DIR"
|
||||
|
||||
while IFS= read -r name; do
|
||||
[[ -z "$name" ]] && continue
|
||||
|
||||
# Salta le risorse generate automaticamente (es. default token secret,
|
||||
# ReplicaSet gestiti da Deployment, Pod gestiti da controller superiori)
|
||||
if [[ "$kind" == "secret" && "$name" =~ ^default-token- ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
OUT_FILE="${KIND_DIR}/${name}.yaml"
|
||||
echo " -> ${kind}/${name}"
|
||||
|
||||
if "${KCTL[@]}" -n "$ns" get "$kind" "$name" -o yaml 2>/dev/null | clean_manifest > "$OUT_FILE"; then
|
||||
echo "| ${ns} | ${kind} | ${name} | \`${ns}/${kind}/${name}.yaml\` |" >> "$SUMMARY_FILE"
|
||||
else
|
||||
echo " ATTENZIONE: export fallito per ${kind}/${name}" >&2
|
||||
rm -f "$OUT_FILE"
|
||||
fi
|
||||
done <<< "$NAMES"
|
||||
done
|
||||
done
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Export risorse cluster-scoped
|
||||
# ------------------------------------------------------------------
|
||||
echo
|
||||
echo "== Risorse cluster-scoped =="
|
||||
|
||||
CLUSTER_DIR="${OUTPUT_DIR}/_cluster-scoped"
|
||||
|
||||
# Cache unica delle risorse disponibili (evita N chiamate a api-resources)
|
||||
_AVAIL_NAMES=$("${KCTL[@]}" api-resources --no-headers 2>/dev/null | awk '{print tolower($1)}' || true)
|
||||
_AVAIL_KINDS=$("${KCTL[@]}" api-resources --no-headers 2>/dev/null | awk '{print tolower($NF)}' || true)
|
||||
|
||||
resource_type_exists() {
|
||||
local kind="$1"
|
||||
# Controlla sia la colonna NAME (plurale) sia la colonna KIND (singolare) del
|
||||
# risultato di api-resources per gestire plurali irregolari (es. ingress→ingresses).
|
||||
echo "$_AVAIL_NAMES" | grep -qx "${kind}s" \
|
||||
|| echo "$_AVAIL_KINDS" | grep -qx "$kind"
|
||||
}
|
||||
|
||||
export_cluster_resource() {
|
||||
local kind="$1"
|
||||
local name="$2"
|
||||
local kind_dir="${CLUSTER_DIR}/${kind}"
|
||||
mkdir -p "$kind_dir"
|
||||
local out_file="${kind_dir}/${name}.yaml"
|
||||
|
||||
echo " -> ${kind}/${name}"
|
||||
if "${KCTL[@]}" get "$kind" "$name" -o yaml 2>/dev/null | clean_manifest > "$out_file"; then
|
||||
echo "| (cluster) | ${kind} | ${name} | \`_cluster-scoped/${kind}/${name}.yaml\` |" >> "$SUMMARY_FILE"
|
||||
else
|
||||
echo " ATTENZIONE: export fallito per ${kind}/${name}" >&2
|
||||
rm -f "$out_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Risorse globali: esportate sempre, senza filtro namespace ---
|
||||
for kind in "${CLUSTER_RESOURCE_TYPES_GLOBAL[@]}"; do
|
||||
[[ -z "$kind" ]] && continue
|
||||
resource_type_exists "$kind" || continue
|
||||
|
||||
NAMES=$("${KCTL[@]}" get "$kind" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
|
||||
[[ -z "$NAMES" ]] && continue
|
||||
|
||||
while IFS= read -r name; do
|
||||
[[ -z "$name" ]] && continue
|
||||
export_cluster_resource "$kind" "$name"
|
||||
done <<< "$NAMES"
|
||||
done
|
||||
|
||||
# Filtra gli item di un array JSON kubectl (-o json) tenendo solo quelli
|
||||
# per cui `jq_path` (letto tramite kubectl jsonpath-like con yq/python) ha
|
||||
# un valore presente nella lista NAMESPACES. Stampa i nomi corrispondenti.
|
||||
# $1 = json completo (stdin) $2 = "subjects" | "claimref"
|
||||
filter_by_namespace_field() {
|
||||
local mode="$1"
|
||||
if [[ "$USE_YQ" == true ]]; then
|
||||
if [[ "$mode" == "subjects" ]]; then
|
||||
yq eval '.items[] | select((.subjects // []) | any_c(.namespace == env(NS_MATCH))) | .metadata.name' -
|
||||
else
|
||||
yq eval '.items[] | select(.spec.claimRef.namespace == env(NS_MATCH)) | .metadata.name' -
|
||||
fi
|
||||
else
|
||||
python3 "$_PY_FILTER" "$mode"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- ClusterRoleBinding: solo quelli con subject in uno dei namespace indicati ---
|
||||
if resource_type_exists clusterrolebinding; then
|
||||
MATCHED_CRB=""
|
||||
for ns in "${NAMESPACES[@]}"; do
|
||||
RESULT=$("${KCTL[@]}" get clusterrolebinding -o json 2>/dev/null \
|
||||
| NS_CSV="${NAMESPACES[*]}" NS_MATCH="$ns" filter_by_namespace_field subjects || true)
|
||||
MATCHED_CRB="${MATCHED_CRB}
|
||||
${RESULT}"
|
||||
done
|
||||
MATCHED_CRB=$(echo "$MATCHED_CRB" | sort -u | sed '/^$/d')
|
||||
|
||||
while IFS= read -r name; do
|
||||
[[ -z "$name" ]] && continue
|
||||
export_cluster_resource clusterrolebinding "$name"
|
||||
done <<< "$MATCHED_CRB"
|
||||
fi
|
||||
|
||||
# --- PersistentVolume: solo quelli con claimRef in uno dei namespace indicati ---
|
||||
if resource_type_exists persistentvolume; then
|
||||
MATCHED_PV=""
|
||||
for ns in "${NAMESPACES[@]}"; do
|
||||
RESULT=$("${KCTL[@]}" get persistentvolume -o json 2>/dev/null \
|
||||
| NS_CSV="${NAMESPACES[*]}" NS_MATCH="$ns" filter_by_namespace_field claimref || true)
|
||||
MATCHED_PV="${MATCHED_PV}
|
||||
${RESULT}"
|
||||
done
|
||||
MATCHED_PV=$(echo "$MATCHED_PV" | sort -u | sed '/^$/d')
|
||||
|
||||
while IFS= read -r name; do
|
||||
[[ -z "$name" ]] && continue
|
||||
export_cluster_resource persistentvolume "$name"
|
||||
done <<< "$MATCHED_PV"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Export completato in: ${OUTPUT_DIR}"
|
||||
echo "Riepilogo: ${SUMMARY_FILE}"
|
||||
+12
-6
@@ -4,25 +4,31 @@ set -euo pipefail
|
||||
# Uso: ./addlistener.sh [env] [properties_file]
|
||||
# env e' ignorato: endpoint viene sempre letto dalla chiave "endpoint".
|
||||
ENVIRONMENT="${1:-dev}"
|
||||
PROPERTIES_FILE="${2:-env/${ENVIRONMENT}/values.env}"
|
||||
# Hardening per evitare errori con set -u in esecuzioni/sourcing anomali
|
||||
: "${PROPERTIES_FILE:=env/${ENVIRONMENT}/values.env}"
|
||||
ENV_DIR="env/${ENVIRONMENT}"
|
||||
|
||||
if [[ -n "$ENVIRONMENT" ]]; then
|
||||
echo "Ambiente richiesto: $ENVIRONMENT (chiave usata: endpoint)"
|
||||
fi
|
||||
|
||||
if [[ ! -d "$ENV_DIR" ]]; then
|
||||
echo "Warning: directory non trovata: $ENV_DIR" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -f "${PROPERTIES_FILE:-}" ]]; then
|
||||
# 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:]]*//')"
|
||||
elif [[ -d "$ENV_DIR" ]]; then
|
||||
# Estrae endpoint da qualsiasi file .env nella directory dell'ambiente
|
||||
endpoint_raw="$({ grep -r '^[[:space:]]*endpoint[[:space:]]*=' "$ENV_DIR" 2>/dev/null | head -n1 || true; } | sed -E 's/^[^:]*:[[:space:]]*endpoint[[:space:]]*=[[:space:]]*//')"
|
||||
else
|
||||
echo "Warning: file non trovato: ${PROPERTIES_FILE:-<non impostato>} e directory non trovata: $ENV_DIR" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 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
|
||||
echo "La chiave endpoint non e valorizzata in ${PROPERTIES_FILE:-<non impostato>}" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ACTION=""
|
||||
NAMESPACE=""
|
||||
DRY_RUN=false
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Uso:
|
||||
|
||||
$0 hibernate [--namespace NAMESPACE] [--dry-run]
|
||||
$0 resume [--namespace NAMESPACE] [--dry-run]
|
||||
|
||||
Esempi:
|
||||
|
||||
# Mostra cosa verrebbe ibernato in tutti i namespace
|
||||
$0 hibernate --dry-run
|
||||
|
||||
# Iberna tutti i cluster CNPG
|
||||
$0 hibernate
|
||||
|
||||
# Iberna solo i cluster di un namespace
|
||||
$0 hibernate --namespace produzione
|
||||
|
||||
# Mostra cosa verrebbe riattivato
|
||||
$0 resume --dry-run
|
||||
|
||||
# Riattiva tutti i cluster CNPG
|
||||
$0 resume
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
#
|
||||
# Parsing argomenti
|
||||
#
|
||||
if [[ $# -lt 1 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ACTION="$1"
|
||||
shift
|
||||
|
||||
case "$ACTION" in
|
||||
hibernate|resume)
|
||||
;;
|
||||
*)
|
||||
echo "Errore: azione non valida: $ACTION"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--namespace|-n)
|
||||
NAMESPACE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Errore: parametro sconosciuto: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
#
|
||||
# Controlli prerequisiti
|
||||
#
|
||||
command -v kubectl >/dev/null 2>&1 || {
|
||||
echo "Errore: kubectl non trovato"
|
||||
exit 1
|
||||
}
|
||||
|
||||
kubectl cnpg version >/dev/null 2>&1 || {
|
||||
echo "Errore: plugin kubectl-cnpg non disponibile o non funzionante"
|
||||
exit 1
|
||||
}
|
||||
|
||||
#
|
||||
# Recupero cluster CNPG
|
||||
#
|
||||
# label applicata dallo script per tracciare le ibernazioni proprie
|
||||
SCRIPT_LABEL="cnpg-maintenance-hibernated"
|
||||
|
||||
JSONPATH='{range .items[*]}{.metadata.namespace}{";"}{.metadata.name}{";"}{.status.readyInstances}{";"}{.metadata.annotations.cnpg\.io/hibernation}{";"}{.metadata.labels.cnpg-maintenance-hibernated}{"\n"}{end}'
|
||||
|
||||
if [[ -n "$NAMESPACE" ]]; then
|
||||
|
||||
CLUSTERS=$(kubectl get cluster.postgresql.cnpg.io \
|
||||
-n "$NAMESPACE" \
|
||||
-o jsonpath="$JSONPATH")
|
||||
|
||||
else
|
||||
|
||||
CLUSTERS=$(kubectl get cluster.postgresql.cnpg.io \
|
||||
-A \
|
||||
-o jsonpath="$JSONPATH")
|
||||
|
||||
fi
|
||||
|
||||
if [[ -z "$CLUSTERS" ]]; then
|
||||
echo "Nessun cluster CNPG trovato."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "========================================"
|
||||
echo " CNPG BULK OPERATION"
|
||||
echo "========================================"
|
||||
echo "Azione: $ACTION"
|
||||
|
||||
if [[ -n "$NAMESPACE" ]]; then
|
||||
echo "Namespace: $NAMESPACE"
|
||||
else
|
||||
echo "Namespace: TUTTI"
|
||||
fi
|
||||
|
||||
echo "Dry-run: $DRY_RUN"
|
||||
echo "========================================"
|
||||
echo
|
||||
|
||||
#
|
||||
# Elaborazione cluster
|
||||
#
|
||||
while IFS=";" read -r NS CLUSTER READY_INSTANCES HIBERNATION_ANNOTATION SCRIPT_HIBERNATED; do
|
||||
|
||||
[[ -z "$CLUSTER" ]] && continue
|
||||
|
||||
if [[ "$ACTION" == "hibernate" ]]; then
|
||||
# skip clusters con no ready instances (già ibernati o non attivi)
|
||||
if [[ -z "$READY_INSTANCES" || "$READY_INSTANCES" -lt 1 ]]; then
|
||||
echo "Cluster: $NS/$CLUSTER — SALTATO (readyInstances=${READY_INSTANCES:-0}, già ibernato o non attivo)"
|
||||
echo
|
||||
continue
|
||||
fi
|
||||
else
|
||||
# resume: solo cluster ibernati da questo script (label presente) e con annotazione CNPG attiva
|
||||
if [[ "$SCRIPT_HIBERNATED" != "true" || "$HIBERNATION_ANNOTATION" != "on" ]]; then
|
||||
echo "Cluster: $NS/$CLUSTER — SALTATO (non ibernato da questo script)"
|
||||
echo
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Cluster: $NS/$CLUSTER (readyInstances=${READY_INSTANCES:-0}, hibernation=${HIBERNATION_ANNOTATION:-none}, script-label=${SCRIPT_HIBERNATED:-none})"
|
||||
|
||||
if [[ "$ACTION" == "hibernate" ]]; then
|
||||
|
||||
CMD=(
|
||||
kubectl
|
||||
cnpg
|
||||
hibernate
|
||||
on
|
||||
"$CLUSTER"
|
||||
-n
|
||||
"$NS"
|
||||
)
|
||||
|
||||
else
|
||||
|
||||
CMD=(
|
||||
kubectl
|
||||
cnpg
|
||||
hibernate
|
||||
off
|
||||
"$CLUSTER"
|
||||
-n
|
||||
"$NS"
|
||||
)
|
||||
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
|
||||
echo "[DRY-RUN] ${CMD[*]}"
|
||||
if [[ "$ACTION" == "hibernate" ]]; then
|
||||
echo "[DRY-RUN] kubectl label cluster.postgresql.cnpg.io $CLUSTER -n $NS ${SCRIPT_LABEL}=true"
|
||||
else
|
||||
echo "[DRY-RUN] kubectl label cluster.postgresql.cnpg.io $CLUSTER -n $NS ${SCRIPT_LABEL}-"
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
echo "Esecuzione: ${CMD[*]}"
|
||||
|
||||
if "${CMD[@]}"; then
|
||||
echo "OK: $NS/$CLUSTER"
|
||||
if [[ "$ACTION" == "hibernate" ]]; then
|
||||
kubectl label cluster.postgresql.cnpg.io "$CLUSTER" -n "$NS" "${SCRIPT_LABEL}=true" --overwrite
|
||||
else
|
||||
kubectl label cluster.postgresql.cnpg.io "$CLUSTER" -n "$NS" "${SCRIPT_LABEL}-"
|
||||
fi
|
||||
else
|
||||
echo "ERRORE: $NS/$CLUSTER"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
done <<< "$CLUSTERS"
|
||||
|
||||
echo "========================================"
|
||||
echo "Operazione completata"
|
||||
echo "========================================"
|
||||
@@ -0,0 +1,287 @@
|
||||
# Clone and Transform Script
|
||||
|
||||
Script bash per clonare un repository GitLab usando API e token privato, con sostituzione automatica di stringhe nei file.
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- `bash` (4.0+)
|
||||
- `git`
|
||||
- `curl` (opzionale, per verificare l'accesso)
|
||||
- Token privato GitLab con almeno scope `api` o `read_repository`
|
||||
|
||||
## Preparazione
|
||||
|
||||
### 1. Creare il file `transform.txt`
|
||||
|
||||
Crea un file `transform.txt` nella stessa directory dello script con il formato:
|
||||
|
||||
```txt
|
||||
<old_string>=<new_string>
|
||||
<old_string>=<new_string>
|
||||
...
|
||||
```
|
||||
|
||||
**Esempio di transform.txt:**
|
||||
|
||||
```txt
|
||||
# Configurazioni di sviluppo verso produzione
|
||||
DATABASE_HOST=localhost=DATABASE_HOST=prod.example.com
|
||||
DATABASE_PORT=5432=DATABASE_PORT=5432
|
||||
API_ENDPOINT=http://localhost:8080=API_ENDPOINT=https://api.prod.com
|
||||
ENVIRONMENT=development=ENVIRONMENT=production
|
||||
LOG_LEVEL=debug=LOG_LEVEL=info
|
||||
SECRET_KEY=dev-secret=SECRET_KEY=prod-secret-key-xyz
|
||||
```
|
||||
|
||||
### 2. Ottenere un token GitLab
|
||||
|
||||
1. Accedi a GitLab
|
||||
2. Vai a **Profile → Access Tokens**
|
||||
3. Crea un nuovo token con scopes:
|
||||
- `api` - accesso completo
|
||||
- oppure `read_repository` - solo lettura
|
||||
4. Copia il token (non sarà più visibile dopo)
|
||||
|
||||
### 3. Rendere eseguibile lo script
|
||||
|
||||
```bash
|
||||
chmod +x clone-and-transform.sh
|
||||
```
|
||||
|
||||
## Utilizzo
|
||||
|
||||
### Sintassi base
|
||||
|
||||
```bash
|
||||
./clone-and-transform.sh -u <URL> -t <TOKEN> -r <REPO-PATH> -d <DEST-DIR>
|
||||
```
|
||||
|
||||
### Parametri
|
||||
|
||||
| Parametro | Breve | Descrizione | Esempio |
|
||||
|-----------|-------|-------------|---------|
|
||||
| `--url` | `-u` | URL base di GitLab | `https://gitlab.com` |
|
||||
| `--token` | `-t` | Token di accesso privato | `glpat-xxxxx` |
|
||||
| `--repo` | `-r` | Percorso del repository | `mygroup/myproject` |
|
||||
| `--dest` | `-d` | Directory di destinazione | `./cloned-repo` |
|
||||
| `--transform` | `-f` | Path del file transform.txt | `./transform.txt` (default) |
|
||||
| `--help` | `-h` | Mostra l'aiuto | - |
|
||||
|
||||
### Esempi di utilizzo
|
||||
|
||||
**Esempio 1: Clone con trasformazioni usando file default**
|
||||
|
||||
```bash
|
||||
./clone-and-transform.sh \
|
||||
-u https://gitlab.com \
|
||||
-t glpat-xxxxxxxxxxxx \
|
||||
-r mygroup/myproject \
|
||||
-d ./my-cloned-repo
|
||||
```
|
||||
|
||||
**Esempio 2: Clone con file transform.txt personalizzato**
|
||||
|
||||
```bash
|
||||
./clone-and-transform.sh \
|
||||
-u https://gitlab.internal.com \
|
||||
-t glpat-xxxxxxxxxxxx \
|
||||
-r company/backend \
|
||||
-d ./backend-clone \
|
||||
-f ./custom-transform.txt
|
||||
```
|
||||
|
||||
**Esempio 3: Clone da GitLab Enterprise**
|
||||
|
||||
```bash
|
||||
./clone-and-transform.sh \
|
||||
-u https://gitlab.mycompany.com \
|
||||
-t glpat-xxxxxxxxxxxx \
|
||||
-r products/idcidp \
|
||||
-d ./idcidp-prod
|
||||
```
|
||||
|
||||
## Come funziona lo script
|
||||
|
||||
1. **Validazione**: Verifica i parametri e l'esistenza di `transform.txt`
|
||||
2. **Clone**: Clona il repository usando autenticazione OAuth2
|
||||
3. **Scoperta directory**: Identifica directory `kubernetes`, `containers`, `conf`
|
||||
4. **Trasformazione**: Per ogni file trovato applica le sostituzioni definite in `transform.txt`
|
||||
5. **Report**: Mostra il number di file modificati e suggerisce i prossimi passi
|
||||
|
||||
### Directory elaborate
|
||||
|
||||
Lo script elabora automaticamente questi percorsi:
|
||||
- `kubernetes/`
|
||||
- `containers/`
|
||||
- `conf/`
|
||||
|
||||
Se una directory non esiste, viene saltata con un avvertimento.
|
||||
|
||||
### File binari
|
||||
|
||||
I file binari (immagini, archivi, ecc.) vengono automaticamente saltati per evitare corruzioni.
|
||||
|
||||
## Output esempio
|
||||
|
||||
```
|
||||
[INFO] Starting GitLab repository clone and transformation...
|
||||
[INFO] GitLab URL: https://gitlab.com
|
||||
[INFO] Repository: mygroup/myproject
|
||||
[INFO] Destination: ./my-cloned-repo
|
||||
[INFO] Transform file: ./transform.txt
|
||||
[INFO] Cloning repository...
|
||||
[INFO] Repository cloned successfully
|
||||
[INFO] Found directory: kubernetes
|
||||
[INFO] Found directory: containers
|
||||
[INFO] Applying transformations from ./transform.txt...
|
||||
[INFO] Processing directory: kubernetes
|
||||
[INFO] Modified: ./my-cloned-repo/kubernetes/deployment.yaml
|
||||
[INFO] Modified: ./my-cloned-repo/kubernetes/service.yaml
|
||||
[INFO] Processing directory: containers
|
||||
[INFO] ========== TRANSFORMATION SUMMARY ==========
|
||||
Total files processed: 12
|
||||
Files modified: 5
|
||||
[INFO] ==========================================
|
||||
[INFO] Transformation completed successfully!
|
||||
|
||||
Next steps:
|
||||
1. Review changes: cd my-cloned-repo && git diff
|
||||
2. Commit changes: git add . && git commit -m 'Apply transformations'
|
||||
3. Push changes: git push
|
||||
```
|
||||
|
||||
## Verifica delle modifiche
|
||||
|
||||
Dopo l'esecuzione, è consigliato verificare le modifiche:
|
||||
|
||||
```bash
|
||||
cd ./my-cloned-repo
|
||||
git diff
|
||||
```
|
||||
|
||||
Per vedere solo i file modificati:
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
## Commit e Push
|
||||
|
||||
Se le modifiche sono corrette:
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "Apply environment transformations"
|
||||
git push
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Errore: "Failed to clone repository"
|
||||
|
||||
**Causa**: Token non valido o senza permessi
|
||||
|
||||
**Soluzione**:
|
||||
1. Verifica il token nel profilo GitLab
|
||||
2. Assicurati che il token abbia scope `api`
|
||||
3. Verifica che il repository sia accessibile con il token
|
||||
|
||||
### Errore: "Transform file not found"
|
||||
|
||||
**Causa**: Il file `transform.txt` non esiste
|
||||
|
||||
**Soluzione**:
|
||||
1. Crea il file `transform.txt` nella directory dove esegui lo script
|
||||
2. Usa l'opzione `-f` per specificare un percorso personalizzato
|
||||
|
||||
### Nessun file modificato
|
||||
|
||||
**Causa**: Le stringhe in `transform.txt` non corrispondono ai file
|
||||
|
||||
**Soluzione**:
|
||||
1. Verifica il contenuto dei file nella directory clonata
|
||||
2. Assicurati che le stringhe in `transform.txt` siano corrette
|
||||
3. Usa pattern più generici se necessario
|
||||
|
||||
### Errore di escaping in stringhe complesse
|
||||
|
||||
Se le stringhe contengono caratteri speciali (barre, ampersand, etc.):
|
||||
|
||||
**Lo script gestisce automaticamente l'escaping**, ma se riscontri problemi:
|
||||
1. Usa sequenze di escape nel file transform.txt
|
||||
2. Ad esempio, per una barra inversa: `path\\old=path\\new`
|
||||
|
||||
## Sicurezza
|
||||
|
||||
### Best practices
|
||||
|
||||
1. **Non** commettere il token nel repository
|
||||
2. Usa variabili d'ambiente per il token:
|
||||
```bash
|
||||
./clone-and-transform.sh \
|
||||
-u https://gitlab.com \
|
||||
-t $GITLAB_TOKEN \
|
||||
-r mygroup/myproject \
|
||||
-d ./clone
|
||||
```
|
||||
|
||||
3. Usa file `.gitignore` per `transform.txt` se contiene dati sensibili:
|
||||
```bash
|
||||
echo "transform.txt" >> .gitignore
|
||||
```
|
||||
|
||||
4. Revoca il token dopo l'uso se è ad uso singolo
|
||||
|
||||
## Opzioni avanzate
|
||||
|
||||
### Usare con script di automazione
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
export GITLAB_TOKEN="glpat-xxxxx"
|
||||
export GITLAB_URL="https://gitlab.internal.com"
|
||||
|
||||
./clone-and-transform.sh \
|
||||
-u $GITLAB_URL \
|
||||
-t $GITLAB_TOKEN \
|
||||
-r company/backend \
|
||||
-d ./backend-clone
|
||||
```
|
||||
|
||||
### Variazione per ambienti multipli
|
||||
|
||||
Crea diversi file di trasformazione:
|
||||
- `transform-dev.txt`
|
||||
- `transform-qa.txt`
|
||||
- `transform-prod.txt`
|
||||
|
||||
```bash
|
||||
# Per development
|
||||
./clone-and-transform.sh \
|
||||
-u https://gitlab.com \
|
||||
-t $GITLAB_TOKEN \
|
||||
-r mygroup/myproject \
|
||||
-d ./clone-dev \
|
||||
-f transform-dev.txt
|
||||
|
||||
# Per production
|
||||
./clone-and-transform.sh \
|
||||
-u https://gitlab.com \
|
||||
-t $GITLAB_TOKEN \
|
||||
-r mygroup/myproject \
|
||||
-d ./clone-prod \
|
||||
-f transform-prod.txt
|
||||
```
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.0 (Initial Release)
|
||||
- Clone repository con token privato
|
||||
- Applicazione di trasformazioni su directory specifiche
|
||||
- Supporto per file UTF-8
|
||||
- Skip automatico file binari
|
||||
- Output colorato e dettagliato
|
||||
|
||||
## Licenza
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Configuration
|
||||
SOURCE_ENV="${1:-dbdev}"
|
||||
DBNAME="${2:-geco}"
|
||||
POD_NAME="${3:-postgresql-1-postgresql-0}"
|
||||
CONTAINER_NAME="${4:-postgresql-server}"
|
||||
BACKUP_REMOTE_DIR="${5:-/shared/backup}"
|
||||
BACKUP_LOCAL_DIR="${6:-.}"
|
||||
COMPRESSION="${7:-true}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $*"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR:${NC} $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] WARN:${NC} $*"
|
||||
}
|
||||
|
||||
# Determine backup file extension
|
||||
if [ "${COMPRESSION}" = "true" ]; then
|
||||
BACKUP_FILE="${DBNAME}-${SOURCE_ENV}-$(date +%Y%m%d-%H%M%S).backup.gz"
|
||||
DUMP_FORMAT="c"
|
||||
else
|
||||
BACKUP_FILE="${DBNAME}-${SOURCE_ENV}-$(date +%Y%m%d-%H%M%S).backup"
|
||||
DUMP_FORMAT="c"
|
||||
fi
|
||||
|
||||
log "Inizio backup database PostgreSQL"
|
||||
log "Namespace: ${SOURCE_ENV}"
|
||||
log "Pod: ${POD_NAME}"
|
||||
log "Database: ${DBNAME}"
|
||||
log "File di backup: ${BACKUP_FILE}"
|
||||
|
||||
# Check if namespace exists
|
||||
if ! kubectl get namespace "${SOURCE_ENV}" &>/dev/null; then
|
||||
error "Namespace '${SOURCE_ENV}' non trovato"
|
||||
fi
|
||||
|
||||
# Check if pod exists
|
||||
if ! kubectl get pod "${POD_NAME}" -n "${SOURCE_ENV}" &>/dev/null; then
|
||||
error "Pod '${POD_NAME}' non trovato nel namespace '${SOURCE_ENV}'"
|
||||
fi
|
||||
|
||||
# Check if pod is running
|
||||
POD_STATUS=$(kubectl get pod "${POD_NAME}" -n "${SOURCE_ENV}" -o jsonpath='{.status.phase}')
|
||||
if [ "${POD_STATUS}" != "Running" ]; then
|
||||
error "Pod '${POD_NAME}' non è in stato Running (stato attuale: ${POD_STATUS})"
|
||||
fi
|
||||
|
||||
log "Creazione backup sul pod..."
|
||||
|
||||
# Create backup on remote pod
|
||||
if [ "${COMPRESSION}" = "true" ]; then
|
||||
kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${SOURCE_ENV}" \
|
||||
-- pg_dump -U postgres -d "${DBNAME}" -F "${DUMP_FORMAT}" -b -v | gzip > "${BACKUP_LOCAL_DIR}/${BACKUP_FILE}" || error "Backup fallito"
|
||||
else
|
||||
kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${SOURCE_ENV}" \
|
||||
-- pg_dump -U postgres -d "${DBNAME}" -F "${DUMP_FORMAT}" -b -v > "${BACKUP_LOCAL_DIR}/${BACKUP_FILE}" || error "Backup fallito"
|
||||
fi
|
||||
|
||||
log "Backup completato"
|
||||
|
||||
# Verify file exists and has content
|
||||
if [ ! -f "${BACKUP_LOCAL_DIR}/${BACKUP_FILE}" ]; then
|
||||
error "File di backup non trovato: ${BACKUP_LOCAL_DIR}/${BACKUP_FILE}"
|
||||
fi
|
||||
|
||||
FILE_SIZE=$(du -h "${BACKUP_LOCAL_DIR}/${BACKUP_FILE}" | cut -f1)
|
||||
if [ -z "${FILE_SIZE}" ] || [ "${FILE_SIZE}" = "0" ]; then
|
||||
error "File di backup vuoto o non valido"
|
||||
fi
|
||||
|
||||
log "✅ Backup completato con successo"
|
||||
log "Percorso: ${BACKUP_LOCAL_DIR}/${BACKUP_FILE}"
|
||||
log "Dimensione: ${FILE_SIZE}"
|
||||
|
||||
log "Inizio trasferimento file di backup su server remoto..."
|
||||
if sshpass -p "POC-25_sts" scp "${BACKUP_LOCAL_DIR}/${BACKUP_FILE}" barucci@10.20.1.101:/home/barucci/froms/; then
|
||||
log "✅ File trasferito con successo su 10.20.1.101"
|
||||
log "Destinazione: barucci@10.20.1.101:/home/barucci/froms/${BACKUP_FILE}"
|
||||
else
|
||||
warn "Trasferimento file fallito, ma backup locale è disponibile in ${BACKUP_LOCAL_DIR}/${BACKUP_FILE}"
|
||||
fi
|
||||
@@ -0,0 +1,773 @@
|
||||
#!/bin/bash
|
||||
|
||||
################################################################################
|
||||
# Script: clone-and-transform.sh
|
||||
# Description: Clone GitLab repository using private token and apply
|
||||
# string substitutions from transform.env
|
||||
# Usage: ./clone-and-transform.sh <gitlab-url> <token> <repo-path> <dest-dir>
|
||||
################################################################################
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to display usage
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-u, --url <URL> GitLab API base URL (e.g., https://gitlab.com)
|
||||
-t, --token <TOKEN> Private access token
|
||||
-r, --repo <REPO-PATH> Repository path (e.g., group/project)
|
||||
-d, --dest <DEST-DIR> Destination directory for clone / transformation source
|
||||
-f, --transform <FILE> Path to transform.env file (default: ./transform.env)
|
||||
--gitea-repo <REPO-PATH> Repository path to clone from Gitea (default: same as --repo)
|
||||
-transformation Skip GitLab clone; run Gitea clone + transform + commit/push
|
||||
-h, --help Show this help message
|
||||
|
||||
Example:
|
||||
$0 -u https://gitlab.com -t glpat-xxxxx -r mygroup/myproject -d ./cloned-repo
|
||||
$0 -transformation -d ./existing-repo --gitea-repo mygroup/myproject -f ./transform.env
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Default values
|
||||
GITLAB_URL=""
|
||||
PRIVATE_TOKEN=""
|
||||
REPO_PATH=""
|
||||
DEST_DIR=""
|
||||
TRANSFORM_FILE="./transform.env"
|
||||
GITEA_URL="https://git.italiadatacenter.com"
|
||||
GITEA_TOKEN="65fc5f07365bd3f89c5b9ddca3d2d2e7b3777a88"
|
||||
GITEA_REPO_PATH=""
|
||||
GITEA_DEST_DIR="target-repo"
|
||||
TRANSFORMATION_ONLY=false
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-u|--url)
|
||||
GITLAB_URL="$2"
|
||||
shift 2
|
||||
;;
|
||||
-t|--token)
|
||||
PRIVATE_TOKEN="$2"
|
||||
shift 2
|
||||
;;
|
||||
-r|--repo)
|
||||
REPO_PATH="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d|--dest)
|
||||
DEST_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
-f|--transform)
|
||||
TRANSFORM_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--gitea-repo)
|
||||
GITEA_REPO_PATH="$2"
|
||||
shift 2
|
||||
;;
|
||||
-transformation)
|
||||
TRANSFORMATION_ONLY=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
print_error "Unknown option: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate required parameters
|
||||
if [[ "$TRANSFORMATION_ONLY" == true ]]; then
|
||||
if [[ -z "$DEST_DIR" ]]; then
|
||||
print_error "Missing required parameter: --dest"
|
||||
usage
|
||||
fi
|
||||
else
|
||||
if [[ -z "$GITLAB_URL" ]] || [[ -z "$PRIVATE_TOKEN" ]] || [[ -z "$REPO_PATH" ]] || [[ -z "$DEST_DIR" ]]; then
|
||||
print_error "Missing required parameters"
|
||||
usage
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if transform.env exists
|
||||
if [[ ! -f "$TRANSFORM_FILE" ]]; then
|
||||
print_error "Transform file not found: $TRANSFORM_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate transform.env format
|
||||
if ! grep -Eq '^[[:space:]]*[^#[:space:]].*===.*$' "$TRANSFORM_FILE"; then
|
||||
print_error "Transform file format invalid. Expected: <old_string>===<new_string>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$TRANSFORMATION_ONLY" == true ]]; then
|
||||
print_info "Mode: transformation only (GitLab clone skipped)"
|
||||
else
|
||||
print_info "Starting GitLab repository clone and transformation..."
|
||||
print_info "GitLab URL: $GITLAB_URL"
|
||||
print_info "Repository: $REPO_PATH"
|
||||
fi
|
||||
print_info "Destination: $DEST_DIR"
|
||||
print_info "Transform file: $TRANSFORM_FILE"
|
||||
print_info "Gitea instance: $GITEA_URL"
|
||||
print_info "Gitea destination: $GITEA_DEST_DIR"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HTTPROUTE_TEMPLATE_FILE="$SCRIPT_DIR/httproute.yaml"
|
||||
|
||||
if [[ "$TRANSFORMATION_ONLY" == false ]]; then
|
||||
# Build repository URLs
|
||||
BASE_URL="${GITLAB_URL%/}"
|
||||
if [[ ! "$BASE_URL" =~ ^https?:// ]]; then
|
||||
print_error "GitLab URL must start with http:// or https://"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_REPO_URL="${BASE_URL}/${REPO_PATH}.git"
|
||||
AUTH_REPO_URL="$(printf '%s\n' "$TARGET_REPO_URL" | sed -E "s#^(https?://)#\\1oauth2:${PRIVATE_TOKEN}@#")"
|
||||
|
||||
# Always use a fresh clone to avoid stale/outdated repository state
|
||||
if [[ -e "$DEST_DIR" ]]; then
|
||||
if [[ -z "$DEST_DIR" || "$DEST_DIR" == "/" || "$DEST_DIR" == "." ]]; then
|
||||
print_error "Refusing to remove unsafe destination path: $DEST_DIR"
|
||||
exit 1
|
||||
fi
|
||||
print_info "Destination already exists, removing it before clone: $DEST_DIR"
|
||||
rm -rf -- "$DEST_DIR"
|
||||
fi
|
||||
|
||||
print_info "Cloning repository..."
|
||||
print_info "Running: git clone $(printf '%s' "$AUTH_REPO_URL" | sed -E 's#(https?://)[^@]+@#\1***@#') $DEST_DIR"
|
||||
GIT_TERMINAL_PROMPT=0 git clone --quiet "$AUTH_REPO_URL" "$DEST_DIR" 2>/dev/null || {
|
||||
print_error "Failed to clone repository"
|
||||
print_info "Ensure your token has 'api' or 'read_repository' scope"
|
||||
exit 1
|
||||
}
|
||||
print_info "Repository cloned successfully"
|
||||
fi # end TRANSFORMATION_ONLY skip
|
||||
|
||||
# Clone additional repository from Gitea into target-repo
|
||||
if [[ -z "$GITEA_REPO_PATH" ]]; then
|
||||
GITEA_REPO_PATH="$REPO_PATH"
|
||||
fi
|
||||
|
||||
GITEA_BASE_URL="${GITEA_URL%/}"
|
||||
if [[ ! "$GITEA_BASE_URL" =~ ^https?:// ]]; then
|
||||
print_error "Gitea URL must start with http:// or https://"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITEA_TARGET_REPO_URL="${GITEA_BASE_URL}/${GITEA_REPO_PATH}.git"
|
||||
GITEA_AUTH_REPO_URL="$(printf '%s\n' "$GITEA_TARGET_REPO_URL" | sed -E "s#^(https?://)#\\1${GITEA_TOKEN}@#")"
|
||||
|
||||
if [[ -e "$GITEA_DEST_DIR" ]]; then
|
||||
if [[ -z "$GITEA_DEST_DIR" || "$GITEA_DEST_DIR" == "/" || "$GITEA_DEST_DIR" == "." ]]; then
|
||||
print_error "Refusing to remove unsafe destination path: $GITEA_DEST_DIR"
|
||||
exit 1
|
||||
fi
|
||||
print_info "Gitea destination already exists, removing it before clone: $GITEA_DEST_DIR"
|
||||
rm -rf -- "$GITEA_DEST_DIR"
|
||||
fi
|
||||
|
||||
print_info "Cloning additional repository from Gitea..."
|
||||
print_info "Running: git clone $(printf '%s' "$GITEA_AUTH_REPO_URL" | sed -E 's#(https?://)[^@]+@#\1***@#') $GITEA_DEST_DIR"
|
||||
GIT_TERMINAL_PROMPT=0 git clone --quiet "$GITEA_AUTH_REPO_URL" "$GITEA_DEST_DIR" 2>/dev/null || {
|
||||
print_error "Failed to clone additional Gitea repository"
|
||||
print_info "Check Gitea token permissions and repository path"
|
||||
exit 1
|
||||
}
|
||||
print_info "Additional Gitea repository cloned successfully"
|
||||
|
||||
# In transformation-only mode, seed DEST_DIR from the freshly cloned Gitea repo
|
||||
if [[ "$TRANSFORMATION_ONLY" == true ]]; then
|
||||
if [[ -e "$DEST_DIR" ]]; then
|
||||
if [[ -z "$DEST_DIR" || "$DEST_DIR" == "/" || "$DEST_DIR" == "." ]]; then
|
||||
print_error "Refusing to remove unsafe destination path: $DEST_DIR"
|
||||
exit 1
|
||||
fi
|
||||
print_info "Removing existing DEST_DIR before seeding from Gitea: $DEST_DIR"
|
||||
rm -rf -- "$DEST_DIR"
|
||||
fi
|
||||
print_info "Copying Gitea clone ($GITEA_DEST_DIR) -> $DEST_DIR ..."
|
||||
cp -R "$GITEA_DEST_DIR" "$DEST_DIR"
|
||||
print_info "Copy completed"
|
||||
fi
|
||||
|
||||
# Check if target directories exist
|
||||
DIRS_TO_PROCESS=("kubernetes" "container" "conf" "envs")
|
||||
FOUND_DIRS=()
|
||||
|
||||
for dir in "${DIRS_TO_PROCESS[@]}"; do
|
||||
if [[ -d "$DEST_DIR/$dir" ]]; then
|
||||
FOUND_DIRS+=("$dir")
|
||||
print_info "Found directory: $dir"
|
||||
fi
|
||||
done
|
||||
|
||||
# Also search for envs directories nested in subdirectories
|
||||
while IFS= read -r envs_abs; do
|
||||
envs_rel="${envs_abs#${DEST_DIR}/}"
|
||||
# Skip top-level envs (already handled above)
|
||||
if [[ "$envs_rel" != "envs" ]]; then
|
||||
FOUND_DIRS+=("$envs_rel")
|
||||
print_info "Found nested envs directory: $envs_rel"
|
||||
fi
|
||||
done < <(find "$DEST_DIR" -mindepth 2 -type d -name "envs" 2>/dev/null)
|
||||
|
||||
if [[ ${#FOUND_DIRS[@]} -eq 0 ]]; then
|
||||
print_warning "No target directories (kubernetes/container/conf/envs) found in cloned repository"
|
||||
fi
|
||||
|
||||
# Function to apply transformations to a file
|
||||
apply_transformations() {
|
||||
local file="$1"
|
||||
local temp_file
|
||||
local changes=0
|
||||
|
||||
temp_file="$(mktemp "${file}.tmp.XXXXXX")"
|
||||
|
||||
# Create a copy
|
||||
cp "$file" "$temp_file"
|
||||
|
||||
# Apply each transformation from transform.env (format: old_string===new_string)
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# Skip empty lines and comments
|
||||
[[ -z "$line" ]] && continue
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ "$line" != *"==="* ]] && continue
|
||||
|
||||
local old_string new_string
|
||||
old_string="${line%%===*}"
|
||||
new_string="${line#*===}"
|
||||
|
||||
# Trim whitespace
|
||||
old_string=$(echo "$old_string" | xargs)
|
||||
new_string=$(echo "$new_string" | xargs)
|
||||
|
||||
# Check if substitution would occur
|
||||
if grep -Fq -- "$old_string" "$temp_file" 2>/dev/null; then
|
||||
# Use sed with proper escaping for replacement
|
||||
local old_escaped new_escaped
|
||||
old_escaped=$(printf '%s\n' "$old_string" | sed 's/[&|\\]/\\&/g')
|
||||
new_escaped=$(printf '%s\n' "$new_string" | sed 's/[&|\\]/\\&/g')
|
||||
sed -i "s|${old_escaped}|${new_escaped}|g" "$temp_file"
|
||||
changes=$((changes + 1))
|
||||
fi
|
||||
done < "$TRANSFORM_FILE"
|
||||
|
||||
# Compare and update if changes were made
|
||||
if ! diff -q "$file" "$temp_file" > /dev/null 2>&1; then
|
||||
mv "$temp_file" "$file"
|
||||
return 0
|
||||
else
|
||||
rm "$temp_file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to apply automatic placeholder transformation: %string% -> <string>
|
||||
apply_placeholder_transformations() {
|
||||
local file="$1"
|
||||
local temp_file
|
||||
|
||||
temp_file="$(mktemp "${file}.tmp.XXXXXX")"
|
||||
cp "$file" "$temp_file"
|
||||
|
||||
# Convert placeholders enclosed in %...% to <...>
|
||||
sed -i -E 's/%([^%]+)%/<\1>/g' "$temp_file"
|
||||
|
||||
if ! diff -q "$file" "$temp_file" > /dev/null 2>&1; then
|
||||
mv "$temp_file" "$file"
|
||||
return 0
|
||||
else
|
||||
rm "$temp_file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Apply transformations to files in target directories
|
||||
print_info "Applying transformations from $TRANSFORM_FILE..."
|
||||
|
||||
total_files=0
|
||||
modified_files=0
|
||||
|
||||
for dir in "${FOUND_DIRS[@]}"; do
|
||||
target_path="$DEST_DIR/$dir"
|
||||
print_info "Processing directory: $dir"
|
||||
|
||||
# Find all files in the directory and its subdirectories
|
||||
while IFS= read -r file; do
|
||||
total_files=$((total_files + 1))
|
||||
|
||||
# Skip binary files
|
||||
if file "$file" | grep -q "binary"; then
|
||||
print_warning "Skipping binary file: $file"
|
||||
continue
|
||||
fi
|
||||
|
||||
if apply_transformations "$file"; then
|
||||
print_info " Modified: $file"
|
||||
modified_files=$((modified_files + 1))
|
||||
fi
|
||||
done < <(find "$target_path" -type f 2>/dev/null)
|
||||
done
|
||||
|
||||
# Apply automatic %string% -> <string> transformation to all files in target directory
|
||||
print_info "Applying automatic placeholder transformation (%...% -> <...>) in $DEST_DIR..."
|
||||
|
||||
placeholder_total_files=0
|
||||
placeholder_modified_files=0
|
||||
|
||||
while IFS= read -r file; do
|
||||
placeholder_total_files=$((placeholder_total_files + 1))
|
||||
|
||||
# Skip binary files
|
||||
if file "$file" | grep -q "binary"; then
|
||||
print_warning "Skipping binary file: $file"
|
||||
continue
|
||||
fi
|
||||
|
||||
if apply_placeholder_transformations "$file"; then
|
||||
print_info " Placeholder updated: $file"
|
||||
placeholder_modified_files=$((placeholder_modified_files + 1))
|
||||
fi
|
||||
done < <(find "$DEST_DIR" -type f 2>/dev/null)
|
||||
|
||||
# Post-transformation directory operations on DEST_DIR
|
||||
if [[ "$TRANSFORMATION_ONLY" == false ]]; then
|
||||
print_info "Applying post-transformation directory operations on $DEST_DIR..."
|
||||
|
||||
# 1) Rename container -> containers
|
||||
if [[ -d "$DEST_DIR/container" ]]; then
|
||||
mv -- "$DEST_DIR/container" "$DEST_DIR/containers"
|
||||
print_info "Renamed: container -> containers"
|
||||
else
|
||||
print_warning "Directory not found, skipping rename: $DEST_DIR/container"
|
||||
fi
|
||||
|
||||
# 2) Rename conf -> env (fail if env already exists)
|
||||
if [[ -d "$DEST_DIR/conf" ]]; then
|
||||
if [[ -e "$DEST_DIR/env" ]]; then
|
||||
print_error "Cannot rename conf -> env: destination $DEST_DIR/env already exists"
|
||||
exit 1
|
||||
fi
|
||||
mv -- "$DEST_DIR/conf" "$DEST_DIR/env"
|
||||
print_info "Renamed: conf -> env"
|
||||
else
|
||||
print_warning "Directory not found, skipping rename: $DEST_DIR/conf"
|
||||
fi
|
||||
|
||||
# 3) Remove .git directory
|
||||
if [[ -d "$DEST_DIR/.git" ]]; then
|
||||
rm -rf -- "$DEST_DIR/.git"
|
||||
print_info "Removed: $DEST_DIR/.git"
|
||||
else
|
||||
print_warning "Directory not found, skipping removal: $DEST_DIR/.git"
|
||||
fi
|
||||
|
||||
# 4) Remove .gitlab-ci.yml
|
||||
if [[ -f "$DEST_DIR/.gitlab-ci.yml" ]]; then
|
||||
rm -f -- "$DEST_DIR/.gitlab-ci.yml"
|
||||
print_info "Removed: $DEST_DIR/.gitlab-ci.yml"
|
||||
else
|
||||
print_warning "File not found, skipping removal: $DEST_DIR/.gitlab-ci.yml"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
print_info "========== TRANSFORMATION SUMMARY =========="
|
||||
echo "Total files processed: $total_files"
|
||||
echo "Files modified: $modified_files"
|
||||
echo "Placeholder files scanned: $placeholder_total_files"
|
||||
echo "Placeholder files modified: $placeholder_modified_files"
|
||||
print_info "=========================================="
|
||||
|
||||
if [[ $modified_files -gt 0 ]]; then
|
||||
print_info "Transformation completed successfully!"
|
||||
else
|
||||
print_warning "No files were modified. Check transform.env contents and patterns."
|
||||
fi
|
||||
|
||||
# Rename configuration.txt -> values.env under env directory
|
||||
if [[ -d "$DEST_DIR/env" ]]; then
|
||||
print_info "Rinomino configuration.txt -> values.env in $DEST_DIR/env ..."
|
||||
while IFS= read -r f; do
|
||||
target_path="$(dirname "$f")/values.env"
|
||||
mv -- "$f" "$target_path"
|
||||
print_info " Renamed: $f -> $target_path"
|
||||
done < <(find "$DEST_DIR/env" -type f -name "configuration.txt")
|
||||
else
|
||||
print_warning "Directory $DEST_DIR/env non trovata, skip rename configuration.txt"
|
||||
fi
|
||||
|
||||
# Move env/configuration.env to DEST_DIR root as properties.env
|
||||
if [[ -f "$DEST_DIR/env/configuration.env" ]]; then
|
||||
if [[ -f "$DEST_DIR/properties.env" ]]; then
|
||||
rm -f -- "$DEST_DIR/properties.env"
|
||||
print_info "Removed existing file: $DEST_DIR/properties.env"
|
||||
fi
|
||||
mv -- "$DEST_DIR/env/configuration.env" "$DEST_DIR/properties.env"
|
||||
print_info "Moved: $DEST_DIR/env/configuration.env -> $DEST_DIR/properties.env"
|
||||
else
|
||||
print_warning "File non trovato, skip move: $DEST_DIR/env/configuration.env"
|
||||
fi
|
||||
|
||||
# If an Ingress resource exists in kubernetes YAMLs, generate httproute.yaml from template
|
||||
# and remove Ingress resource definitions from manifests.
|
||||
K8S_DIR="$DEST_DIR/kubernetes"
|
||||
if [[ -d "$K8S_DIR" ]]; then
|
||||
print_info "Checking Kubernetes manifests for Ingress resources in $K8S_DIR ..."
|
||||
|
||||
ingress_found=false
|
||||
service_name=""
|
||||
ingress_service_name=""
|
||||
ingress_port=""
|
||||
|
||||
while IFS= read -r -d '' k8s_file; do
|
||||
if [[ -z "$service_name" ]]; then
|
||||
service_name="$(awk '
|
||||
/^[[:space:]]*kind:[[:space:]]*Service([[:space:]]|$)/ { in_service=1; next }
|
||||
in_service && /^[[:space:]]*kind:[[:space:]]*/ { in_service=0 }
|
||||
in_service && /^[[:space:]]*name:[[:space:]]*/ {
|
||||
value=$0
|
||||
sub(/^[[:space:]]*name:[[:space:]]*/, "", value)
|
||||
gsub(/[[:space:]]+$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
' "$k8s_file")"
|
||||
fi
|
||||
|
||||
if grep -Eq '^[[:space:]]*kind:[[:space:]]*Ingress([[:space:]]|$)' "$k8s_file"; then
|
||||
ingress_found=true
|
||||
|
||||
if [[ -z "$ingress_port" ]]; then
|
||||
ingress_port="$(awk '
|
||||
/^[[:space:]]*kind:[[:space:]]*Ingress([[:space:]]|$)/ { in_ingress=1; next }
|
||||
in_ingress && /^[[:space:]]*kind:[[:space:]]*/ { in_ingress=0 }
|
||||
in_ingress && /^[[:space:]]*number:[[:space:]]*[0-9]+/ {
|
||||
value=$0
|
||||
sub(/.*number:[[:space:]]*/, "", value)
|
||||
gsub(/[^0-9].*$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
' "$k8s_file")"
|
||||
fi
|
||||
|
||||
if [[ -z "$ingress_service_name" ]]; then
|
||||
ingress_service_name="$(awk '
|
||||
/^[[:space:]]*kind:[[:space:]]*Ingress([[:space:]]|$)/ { in_ingress=1; next }
|
||||
in_ingress && /^[[:space:]]*kind:[[:space:]]*/ { in_ingress=0 }
|
||||
in_ingress && /^[[:space:]]*name:[[:space:]]*/ {
|
||||
value=$0
|
||||
sub(/^[[:space:]]*name:[[:space:]]*/, "", value)
|
||||
gsub(/[[:space:]]+$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
' "$k8s_file")"
|
||||
fi
|
||||
fi
|
||||
done < <(find "$K8S_DIR" -type f \( -name "*.yaml" -o -name "*.yml" \) -print0)
|
||||
|
||||
if [[ "$ingress_found" == true ]]; then
|
||||
if [[ ! -f "$HTTPROUTE_TEMPLATE_FILE" ]]; then
|
||||
print_error "HTTPRoute template not found: $HTTPROUTE_TEMPLATE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$service_name" ]]; then
|
||||
service_name="$ingress_service_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$service_name" ]]; then
|
||||
print_error "Cannot create HTTPRoute: service name not found in Kubernetes YAML"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$ingress_port" ]]; then
|
||||
print_error "Cannot create HTTPRoute: ingress port number not found in Kubernetes YAML"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
httproute_output="$K8S_DIR/httproute.yaml"
|
||||
sed -e "s|<svc-name>|$service_name|g" -e "s|<port>|$ingress_port|g" "$HTTPROUTE_TEMPLATE_FILE" > "$httproute_output"
|
||||
print_info "Created HTTPRoute file: $httproute_output"
|
||||
|
||||
while IFS= read -r -d '' k8s_file; do
|
||||
temp_k8s_file="$(mktemp "${k8s_file}.tmp.XXXXXX")"
|
||||
awk '
|
||||
function flush_doc() {
|
||||
if (!doc_started) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!doc_is_ingress) {
|
||||
if (output_count > 0) {
|
||||
printf "---\n"
|
||||
}
|
||||
printf "%s", doc
|
||||
output_count++
|
||||
}
|
||||
|
||||
doc = ""
|
||||
doc_started = 0
|
||||
doc_is_ingress = 0
|
||||
}
|
||||
|
||||
/^[[:space:]]*---[[:space:]]*$/ {
|
||||
flush_doc()
|
||||
next
|
||||
}
|
||||
|
||||
{
|
||||
doc_started = 1
|
||||
doc = doc $0 "\n"
|
||||
if ($0 ~ /^[[:space:]]*kind:[[:space:]]*Ingress([[:space:]]|$)/) {
|
||||
doc_is_ingress = 1
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
flush_doc()
|
||||
}
|
||||
' "$k8s_file" > "$temp_k8s_file"
|
||||
|
||||
if [[ ! -s "$temp_k8s_file" ]]; then
|
||||
rm -f -- "$k8s_file"
|
||||
rm -f -- "$temp_k8s_file"
|
||||
print_info "Removed ingress-only manifest file: $k8s_file"
|
||||
else
|
||||
mv -- "$temp_k8s_file" "$k8s_file"
|
||||
fi
|
||||
done < <(find "$K8S_DIR" -type f \( -name "*.yaml" -o -name "*.yml" \) -print0)
|
||||
|
||||
print_info "Ingress resource definitions removed from Kubernetes manifests"
|
||||
else
|
||||
print_info "No Ingress resource found in Kubernetes manifests"
|
||||
fi
|
||||
|
||||
# If a PersistentVolume resource exists in kubernetes YAMLs,
|
||||
# remove PersistentVolume resource definitions from manifests.
|
||||
pv_found=false
|
||||
while IFS= read -r -d '' k8s_file; do
|
||||
if grep -Eq '^[[:space:]]*kind:[[:space:]]*PersistentVolume([[:space:]]|$)' "$k8s_file"; then
|
||||
pv_found=true
|
||||
break
|
||||
fi
|
||||
done < <(find "$K8S_DIR" -type f \( -name "*.yaml" -o -name "*.yml" \) -print0)
|
||||
|
||||
if [[ "$pv_found" == true ]]; then
|
||||
print_info "PersistentVolume resources found, removing from Kubernetes manifests ..."
|
||||
|
||||
while IFS= read -r -d '' k8s_file; do
|
||||
temp_k8s_file="$(mktemp "${k8s_file}.tmp.XXXXXX")"
|
||||
awk '
|
||||
function flush_doc() {
|
||||
if (!doc_started) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!doc_is_pv) {
|
||||
if (output_count > 0) {
|
||||
printf "---\n"
|
||||
}
|
||||
printf "%s", doc
|
||||
output_count++
|
||||
}
|
||||
|
||||
doc = ""
|
||||
doc_started = 0
|
||||
doc_is_pv = 0
|
||||
}
|
||||
|
||||
/^[[:space:]]*---[[:space:]]*$/ {
|
||||
flush_doc()
|
||||
next
|
||||
}
|
||||
|
||||
{
|
||||
doc_started = 1
|
||||
doc = doc $0 "\n"
|
||||
if ($0 ~ /^[[:space:]]*kind:[[:space:]]*PersistentVolume([[:space:]]|$)/) {
|
||||
doc_is_pv = 1
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
flush_doc()
|
||||
}
|
||||
' "$k8s_file" > "$temp_k8s_file"
|
||||
|
||||
if [[ ! -s "$temp_k8s_file" ]]; then
|
||||
rm -f -- "$k8s_file"
|
||||
rm -f -- "$temp_k8s_file"
|
||||
print_info "Removed persistent-volume-only manifest file: $k8s_file"
|
||||
else
|
||||
mv -- "$temp_k8s_file" "$k8s_file"
|
||||
fi
|
||||
done < <(find "$K8S_DIR" -type f \( -name "*.yaml" -o -name "*.yml" \) -print0)
|
||||
|
||||
print_info "PersistentVolume resource definitions removed from Kubernetes manifests"
|
||||
else
|
||||
print_info "No PersistentVolume resource found in Kubernetes manifests"
|
||||
fi
|
||||
|
||||
# Normalize line endings to LF for kubernetes manifests.
|
||||
while IFS= read -r -d '' k8s_file; do
|
||||
sed -i 's/\r$//' "$k8s_file"
|
||||
done < <(find "$K8S_DIR" -type f \( -name "*.yaml" -o -name "*.yml" \) -print0)
|
||||
print_info "Normalized line endings to LF for Kubernetes YAML files"
|
||||
else
|
||||
print_warning "Directory not found, skipping Kubernetes ingress conversion: $K8S_DIR"
|
||||
fi
|
||||
|
||||
# Conditional directory restructuring: check if src/ and containers/ exist,
|
||||
# and if containers/ has exactly 1 subdirectory
|
||||
print_info "Checking conditions for directory restructuring..."
|
||||
|
||||
SRC_PATH="$DEST_DIR/src"
|
||||
CONTAINERS_PATH="$DEST_DIR/containers"
|
||||
RESTRUCTURE=false
|
||||
CONTAINERS_SUBDIR=""
|
||||
|
||||
if [[ -d "$SRC_PATH" ]]; then
|
||||
print_info " ✓ Found: $SRC_PATH"
|
||||
|
||||
if [[ -d "$CONTAINERS_PATH" ]]; then
|
||||
print_info " ✓ Found: $CONTAINERS_PATH"
|
||||
|
||||
# Count subdirectories in containers/
|
||||
subdir_count=0
|
||||
subdirs=()
|
||||
|
||||
while IFS= read -r subdir; do
|
||||
subdirs+=("$subdir")
|
||||
subdir_count=$((subdir_count + 1))
|
||||
done < <(find "$CONTAINERS_PATH" -mindepth 1 -maxdepth 1 -type d)
|
||||
|
||||
if [[ $subdir_count -eq 1 ]]; then
|
||||
CONTAINERS_SUBDIR="${subdirs[0]##*/}" # Extract basename
|
||||
print_info " ✓ Found exactly 1 subdirectory in containers: $CONTAINERS_SUBDIR"
|
||||
RESTRUCTURE=true
|
||||
elif [[ $subdir_count -eq 0 ]]; then
|
||||
print_warning " ✗ No subdirectories found in $CONTAINERS_PATH"
|
||||
else
|
||||
print_warning " ✗ Found $subdir_count subdirectories in $CONTAINERS_PATH (expected 1)"
|
||||
fi
|
||||
else
|
||||
print_warning " ✗ Directory not found: $CONTAINERS_PATH"
|
||||
fi
|
||||
else
|
||||
print_warning " ✗ Directory not found: $SRC_PATH"
|
||||
fi
|
||||
|
||||
if [[ "$RESTRUCTURE" == true ]]; then
|
||||
print_info "========== STARTING CONDITIONAL DIRECTORY RESTRUCTURING =========="
|
||||
print_info "Target subdirectory name: $CONTAINERS_SUBDIR"
|
||||
|
||||
# 1) Rename src -> _src
|
||||
if [[ -d "$SRC_PATH" ]]; then
|
||||
mv -- "$SRC_PATH" "$DEST_DIR/_src"
|
||||
print_info "Step 1/6: Renamed src -> _src"
|
||||
fi
|
||||
|
||||
# 2) Create src/<containers_subdir>/
|
||||
mkdir -p "$DEST_DIR/src/$CONTAINERS_SUBDIR"
|
||||
print_info "Step 2/6: Created directory src/$CONTAINERS_SUBDIR"
|
||||
|
||||
# 3) Move _src -> src/<containers_subdir>/
|
||||
mv -- "$DEST_DIR/_src" "$DEST_DIR/src/$CONTAINERS_SUBDIR/_src"
|
||||
print_info "Step 3/6: Moved _src -> src/$CONTAINERS_SUBDIR/_src"
|
||||
|
||||
# 4) Rename _src -> src (within src/<containers_subdir>/)
|
||||
mv -- "$DEST_DIR/src/$CONTAINERS_SUBDIR/_src" "$DEST_DIR/src/$CONTAINERS_SUBDIR/src"
|
||||
print_info "Step 4/6: Renamed _src -> src/$CONTAINERS_SUBDIR/src"
|
||||
|
||||
# 5) Move *.json, *.js, .prettierrc from root to src/<containers_subdir>/
|
||||
print_info "Step 5/6: Moving files from root to src/$CONTAINERS_SUBDIR/..."
|
||||
|
||||
# Move *.json files
|
||||
while IFS= read -r json_file; do
|
||||
if [[ -f "$json_file" ]]; then
|
||||
mv -- "$json_file" "$DEST_DIR/src/$CONTAINERS_SUBDIR/"
|
||||
print_info " Moved: $(basename "$json_file") -> src/$CONTAINERS_SUBDIR/"
|
||||
fi
|
||||
done < <(find "$DEST_DIR" -maxdepth 1 -type f -name "*.json")
|
||||
|
||||
# Move *.js files
|
||||
while IFS= read -r js_file; do
|
||||
if [[ -f "$js_file" ]]; then
|
||||
mv -- "$js_file" "$DEST_DIR/src/$CONTAINERS_SUBDIR/"
|
||||
print_info " Moved: $(basename "$js_file") -> src/$CONTAINERS_SUBDIR/"
|
||||
fi
|
||||
done < <(find "$DEST_DIR" -maxdepth 1 -type f -name "*.js")
|
||||
|
||||
# Move .prettierrc if exists
|
||||
if [[ -f "$DEST_DIR/.prettierrc" ]]; then
|
||||
mv -- "$DEST_DIR/.prettierrc" "$DEST_DIR/src/$CONTAINERS_SUBDIR/"
|
||||
print_info " Moved: .prettierrc -> src/$CONTAINERS_SUBDIR/"
|
||||
fi
|
||||
|
||||
# 6) Move envs/ directory if exists
|
||||
if [[ -d "$DEST_DIR/envs" ]]; then
|
||||
mv -- "$DEST_DIR/envs" "$DEST_DIR/src/$CONTAINERS_SUBDIR/"
|
||||
print_info "Step 6/6: Moved envs directory -> src/$CONTAINERS_SUBDIR/envs"
|
||||
else
|
||||
print_warning "Step 6/6: envs directory not found at root, skipping move"
|
||||
fi
|
||||
|
||||
print_info "========== DIRECTORY RESTRUCTURING COMPLETED SUCCESSFULLY =========="
|
||||
else
|
||||
print_info "Directory restructuring conditions not met, skipping directory reorganization"
|
||||
fi
|
||||
fi # end TRANSFORMATION_ONLY skip for directory operations
|
||||
|
||||
# Copy transformed content from source repo to target repo
|
||||
print_info "Copying transformed content: $DEST_DIR -> $GITEA_DEST_DIR ..."
|
||||
cp -R "./${DEST_DIR}/." "./${GITEA_DEST_DIR}/"
|
||||
print_info "Copy completed"
|
||||
|
||||
# Commit and push changes on target Gitea repository with skip CI/CD marker
|
||||
print_info "Preparing commit and push in $GITEA_DEST_DIR ..."
|
||||
git -C "$GITEA_DEST_DIR" add -A
|
||||
|
||||
if git -C "$GITEA_DEST_DIR" diff --cached --quiet; then
|
||||
print_warning "No changes detected in $GITEA_DEST_DIR, skipping commit and push"
|
||||
else
|
||||
COMMIT_MESSAGE="chore: sync transformed content [skip ci]"
|
||||
git -C "$GITEA_DEST_DIR" commit -m "$COMMIT_MESSAGE"
|
||||
git -C "$GITEA_DEST_DIR" push
|
||||
print_info "Commit and push completed"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: httproute2
|
||||
spec:
|
||||
hostnames:
|
||||
- <endpoint>
|
||||
parentRefs:
|
||||
- name: main-gateway
|
||||
namespace: nginx-gateway
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: <svc-name
|
||||
port: 80
|
||||
@@ -0,0 +1,2 @@
|
||||
delete from campionati_ftp;
|
||||
insert into campionati_ftp (knrtcx,ktipax,ksgtcl,ksgtcx,kdetcx,id,prov,reg,kris7x,israg,c5,refertodigitale) select knrtcx,ktipax,ksgtcl,ksgtcx,kdetcx,id,prov,reg,kris7x,israg,c5,refertodigitale from dblink('dbname=pgare','select * from siglecampionati') AS campionati(knrtcx character(16),ktipax text,ksgtcl text,ksgtcx text,kdetcx text,id integer,prov character(32),reg character(36),kris7x text,israg boolean,c5 boolean,refertodigitale boolean);
|
||||
@@ -0,0 +1,2 @@
|
||||
delete from companies_ftp;
|
||||
insert into companies_ftp(scoso,scomo,scomi,scomc,scomf,ssigl,sdeno,sind2,scaps,slocr,spvre,scapc,sloco,steds,stepr,stesr,scapo,sfaxs,stelx,spiva,snatu,ssito,smail,spresc,spresn,f0dei,f0ini,f0loi,f9coi,f0prv,f0cap,scosop,stid) select * from dblink('dbname=pgare','select scoso,scomo,scomi,scomc,scomf,ssigl,sdeno,sind2,scaps,slocr,spvre,scapc,sloco,steds,stepr,stesr,scapo,sfaxs,stelx,spiva,snatu,ssito,smail,spresc,spresn,f0dei,f0ini,f0loi,f9coi,f0prv,f0cap,scosop,stid from anagrafe') AS anagrafe(scoso integer,scomo text,scomi text,scomc text,scomf text,ssigl text,sdeno text,sind2 text,scaps text,slocr text,spvre text,scapc text,sloco text,steds text,stepr text,stesr text,scapo text,sfaxs text,stelx text,spiva text,snatu text,ssito text,smail text,spresc text,spresn text,f0dei text,f0ini text,f0loi text,f9coi text,f0prv text,f0cap text,scosop integer,stid integer);
|
||||
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# m h dom mon dow command
|
||||
0 5 * * * /home/gitlab-runner/dockeclean.sh
|
||||
0 * * * * /project/inbound_figc/journal_old2new.sh > /project/inbound_figc/journal_old2new.out
|
||||
30 4 * * * /project/inbound_figc/mainload.sh > /project/inbound_figc/loadmain.out
|
||||
0 3 * * * /project/inbound_figc/backup.sh > /project/inbound_figc/backup.out
|
||||
@@ -0,0 +1 @@
|
||||
SELECT "aaagare"();
|
||||
@@ -0,0 +1,11 @@
|
||||
userid="sportftp"
|
||||
password="\$porteams2019"
|
||||
|
||||
|
||||
ftp -n 212.77.67.140 << EOFTP >ftpget.$$
|
||||
user $userid $password
|
||||
prompt
|
||||
cd files
|
||||
mget *.csv
|
||||
quit
|
||||
EOFTP
|
||||
@@ -0,0 +1,14 @@
|
||||
delete from journal_ftp;
|
||||
insert into journal_ftp(id,chi,quando,idp,ida,ora,data,idc) select * from dblink('dbname=pgare','select id,chi,quando,idp,ida,ora,data,idc from vjournal') AS journal(id integer,chi numeric,quando timestamp,idp integer,ida integer,ora text,data date,idc varchar);
|
||||
insert into journal_figc(id_utente_modificatore,matricola_figc_sq_modificatrice,created_at,id_partita,id_azione,orario,data,matricola_impianto_figc,azione_annullata,id_old)
|
||||
select
|
||||
null as id_utente_modificatore,
|
||||
null as matricola_figc_sq_modificatrice,
|
||||
j.quando as created_at,
|
||||
p.id as id_partita,
|
||||
j.ida as id_azione,
|
||||
j.ora::time as orario,
|
||||
j.data as data,
|
||||
idc as matricola_impianto_figc,
|
||||
false as azione_annullata,j.id
|
||||
from journal_ftp j,partite_figc p where j.idp=p.id_partita_ftp and j.id not in(select id_old from journal_figc) order by j.id;
|
||||
@@ -0,0 +1,12 @@
|
||||
insert into journal_figc(id_utente_modificatore,matricola_figc_sq_modificatrice,created_at,id_partita,id_azione,orario,data,matricola_impianto_figc,azione_annullata,id_old)
|
||||
select
|
||||
null as id_utente_modificatore,
|
||||
null as matricola_figc_sq_modificatrice,
|
||||
j.quando as created_at,
|
||||
p.id as id_partita,
|
||||
j.ida as id_azione,
|
||||
j.ora::time as orario,
|
||||
j.data as data,
|
||||
idc as matricola_impianto_figc,
|
||||
false as azione_annullata,j.id
|
||||
from journal_ftp j,partite_figc p where j.idp=p.id_partita_ftp and j.id not in(select id_old from journal_figc) order by j.id;
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
. ./.profile
|
||||
cd /project/inbound_figc
|
||||
/usr/local/bin/kubectl cp journal.sql db/postgresql-1-postgresql-0:/etc/journal.sql -c postgresql-server
|
||||
/usr/local/bin/kubectl exec -it postgresql-1-postgresql-0 -c postgresql-server -n db -- psql -U postgres -d gare -f /etc/journal.sql
|
||||
@@ -0,0 +1 @@
|
||||
insert into partite(idpf,ids) select distinct * from pgpartite2insert;
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
sed -i "s/\"/ /g" $1
|
||||
|
||||
if [[ $2 == "cal" ]]
|
||||
then
|
||||
echo "strutr;r0cmp;r0gir;r0gio;r0anr;r0dai;r0ma1;r0ma2;r0no1;r0pu1;r0pu2;void1;void2;void3;void4;void5;void6;void7;r0imp;r0ori;void8"|cat - $1 > /tmp/out && mv /tmp/out $1"_int"
|
||||
fi
|
||||
|
||||
|
||||
if [[ $2 == "imp" ]]
|
||||
then
|
||||
echo "i0str;i0imp;i0dei;i0ini;i0loi;i0prv;void"|cat - $1 > /tmp/out && mv /tmp/out $1"_int"
|
||||
fi
|
||||
|
||||
if [[ $2 == "ana" ]]
|
||||
then
|
||||
echo "SCOSO;SCOMO;SCOMI;SCOMC;SCOMF;SSIGL;SDENO;DESCA;X21;X23;X24;SPC01;SIND1;SIND2;SCAPS;SLOCR;SPVRE;SINC1;SINC2;SCAPC;SLOCO;SPVCO;STEDS;STEPR;STESR;SCAPO;SFAXS;STELX;SPIVA;SNATU;SSITO;SMAIL;SPRESC;SPRESN;F0DEI;F0INI;F0LOI;F9COI;F0PRV;F0CAP;C0DEI;C0INI;C0LOI;C9COI;C0PRV;C0CAP;D0DEI;D0INI;D0LOI;D9COI;D0PRV;D0CAP;SAT01;SAT02;SAT03;SAT04;SAT05;SAT06;SAT07;SAT08;SAT09;SAT10;void"|cat - $1 > /tmp/out && mv /tmp/out $1"_int"
|
||||
fi
|
||||
|
||||
if [[ $2 == "cla" ]]
|
||||
then
|
||||
echo "REG;KSGTCL;GIRONE;IDSOC;PUNTI;GF;GS;GIOCATE;VINTE;PAREGGIATE;perse"|cat - $1 > /tmp/out && mv /tmp/out $1"_int"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
iconv -f iso-8859-1 -t UTF-8 $1"_int" -o $1"_int_utf8"
|
||||
sed '1s/^\xEF\xBB\xBF//' < $1"_int_utf8" >$1"_int_utf8_nobom.csv"
|
||||
sed -i '1s/^\xEF\xBB\xBF//' $1"_int_utf8"
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
cd /project/inbound_figc
|
||||
|
||||
kubectl cp cserc17f.csv_int_utf8_nobom.csv dbdev/postgresql-1-postgresql-0:/etc/cserc17f_2load_utf8_nobom.csv
|
||||
kubectl cp cseri17f.csv_int_utf8_nobom.csv dbdev/postgresql-1-postgresql-0:/etc/cseri17f_2load_utf8_nobom.csv
|
||||
kubectl cp csexc17f.csv_int_utf8_nobom.csv dbdev/postgresql-1-postgresql-0:/etc/csexc17f_2load_utf8_nobom.csv
|
||||
kubectl cp csezc17f.csv_int_utf8_nobom.csv dbdev/postgresql-1-postgresql-0:/etc/csezc17f_2load_utf8_nobom.csv
|
||||
#k cp csezc17f.csv_int_utf8_nobom.csv dbdev/postgresql-1-postgresql-0:/etc/csezc17f_2load_utf8_nobom.csv
|
||||
|
||||
|
||||
kubectl cp gareload.sql dbdev/postgresql-1-postgresql-0:/etc/gareload.sql
|
||||
kubectl exec -it postgresql-1-postgresql-0 -c postgresql-server -n dbdev -- psql -U postgres -d gare -f /etc/gareload.sql
|
||||
|
||||
#kubectl cp loadclub.sql db/postgresql-1-postgresql-0:/etc/loadclub.sql
|
||||
#kubectl exec -it postgresql-1-postgresql-0 -c postgresql-server -n db -- psql -U postgres -d segdigi -f /etc/loadclub.sql
|
||||
@@ -0,0 +1,2 @@
|
||||
delete from partite_ftp;
|
||||
insert into partite_ftp(id,strutr,r0cmp,r0gir,r0gio,r0anr,r0dai,r0ma1,r0ma2,r0pu1,r0pu2,r0cov,r0rdi,rfl0,r0imp,r0ori,color,data,valid) select * from dblink('dbname=pgare','select id,strutr,r0cmp,r0gir,r0gio,r0anr,r0dai,r0ma1,r0ma2,r0pu1,r0pu2,r0cov,r0rdi,r0fl0,r0imp,r0ori,color,data,valid from calendari') AS calendari(id integer, strutr integer,r0cmp character(8),r0gir character(8),r0gio character(8),r0anr character(3),r0dai character(6),r0ma1 integer,r0ma2 integer,r0pu1 character(3),r0pu2 character(3),r0cov character(3),r0rdi character(3),r0fl0 character(3),r0imp character(8),r0ori character(4),color character(10),data date,valid boolean);
|
||||
@@ -0,0 +1,4 @@
|
||||
SELECT "aaapgare"();
|
||||
SELECT "aaapgare_imp"();
|
||||
SELECT "aaapgare_ana"();
|
||||
SELECT "aaacla"();
|
||||
@@ -0,0 +1,162 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Configuration
|
||||
TARGET_ENV="${1:-stsharedservices-dev}"
|
||||
DBNAME="${2:-geco}"
|
||||
POD_NAME="${3:-stpgclusterdev-1}"
|
||||
CONTAINER_NAME="${4:-postgres}"
|
||||
BACKUP_FILE="${5:-.}"
|
||||
DROP_IF_EXISTS="${6:-true}"
|
||||
DB_USER="${7:-${PGUSER:-postgres}}"
|
||||
DB_PASSWORD="${8:-${PGPASSWORD:-}}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Logging functions
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $*"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR:${NC} $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] WARN:${NC} $*"
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] INFO:${NC} $*"
|
||||
}
|
||||
|
||||
log "Inizio restore database PostgreSQL"
|
||||
log "Namespace target: ${TARGET_ENV}"
|
||||
log "Pod target: ${POD_NAME}"
|
||||
log "Database: ${DBNAME}"
|
||||
log "DB user: ${DB_USER}"
|
||||
log "File backup: ${BACKUP_FILE}"
|
||||
|
||||
# Validate backup file exists
|
||||
if [ ! -f "${BACKUP_FILE}" ]; then
|
||||
error "File di backup non trovato: ${BACKUP_FILE}"
|
||||
fi
|
||||
|
||||
FILE_SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
|
||||
log "Dimensione file backup: ${FILE_SIZE}"
|
||||
|
||||
# Determine if backup is compressed
|
||||
if [[ "${BACKUP_FILE}" == *.gz ]]; then
|
||||
IS_COMPRESSED=true
|
||||
info "Backup compresso (gzip) rilevato"
|
||||
else
|
||||
IS_COMPRESSED=false
|
||||
info "Backup non compresso rilevato"
|
||||
fi
|
||||
|
||||
# Check if namespace exists
|
||||
if ! kubectl get namespace "${TARGET_ENV}" &>/dev/null; then
|
||||
error "Namespace '${TARGET_ENV}' non trovato"
|
||||
fi
|
||||
|
||||
# Check if pod exists
|
||||
if ! kubectl get pod "${POD_NAME}" -n "${TARGET_ENV}" &>/dev/null; then
|
||||
error "Pod '${POD_NAME}' non trovato nel namespace '${TARGET_ENV}'"
|
||||
fi
|
||||
|
||||
# Check if pod is running
|
||||
POD_STATUS=$(kubectl get pod "${POD_NAME}" -n "${TARGET_ENV}" -o jsonpath='{.status.phase}')
|
||||
if [ "${POD_STATUS}" != "Running" ]; then
|
||||
error "Pod '${POD_NAME}' non è in stato Running (stato attuale: ${POD_STATUS})"
|
||||
fi
|
||||
|
||||
# Check database connectivity
|
||||
info "Verifica connessione al database..."
|
||||
if ! kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" pg_isready -U "${DB_USER}" -d "${DBNAME}" &>/dev/null; then
|
||||
error "Impossibile connettersi a PostgreSQL nel pod '${POD_NAME}'"
|
||||
fi
|
||||
|
||||
log "Connessione al database verificata"
|
||||
|
||||
# Drop database if requested
|
||||
if [ "${DROP_IF_EXISTS}" = "true" ]; then
|
||||
warn "Elimino il database '${DBNAME}' se esiste..."
|
||||
kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" psql -U "${DB_USER}" -c "DROP DATABASE IF EXISTS \"${DBNAME}\" WITH (FORCE);" 2>/dev/null || warn "Database non esiste o elimina non è riuscita"
|
||||
|
||||
info "Creo nuovo database '${DBNAME}'..."
|
||||
kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" psql -U "${DB_USER}" -c "CREATE DATABASE \"${DBNAME}\";" || error "Impossibile creare il database '${DBNAME}'"
|
||||
fi
|
||||
|
||||
log "Inizio restore del database..."
|
||||
|
||||
# Execute restore
|
||||
if [ "${IS_COMPRESSED}" = "true" ]; then
|
||||
info "Decompressione e restore in corso..."
|
||||
gunzip -c "${BACKUP_FILE}" | kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" pg_restore -U "${DB_USER}" -d "${DBNAME}" --no-owner -v 2>&1 | grep -E "(NOTICE|ERROR|completed)" || true
|
||||
else
|
||||
info "Restore da file plain SQL in corso..."
|
||||
cat "${BACKUP_FILE}" | kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" psql -U "${DB_USER}" -d "${DBNAME}" -v ON_ERROR_STOP=1 2>&1 | grep -E "(NOTICE|ERROR|INSERT|CREATE)" | tail -20 || true
|
||||
fi
|
||||
|
||||
log "Restore completato"
|
||||
|
||||
info "Allineo owner delle tabelle al proprietario del database target..."
|
||||
TARGET_DB_OWNER=$(kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" psql -U "${DB_USER}" -d postgres -t -A -c "SELECT pg_get_userbyid(datdba) FROM pg_database WHERE datname='${DBNAME}';")
|
||||
|
||||
if [ -z "${TARGET_DB_OWNER}" ]; then
|
||||
error "Impossibile determinare l'owner del database '${DBNAME}'"
|
||||
fi
|
||||
|
||||
kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" psql -U "${DB_USER}" -d "${DBNAME}" -v ON_ERROR_STOP=1 -c "DO \$\$ DECLARE r RECORD; BEGIN FOR r IN SELECT n.nspname, c.relname FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r', 'p') AND n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname NOT LIKE 'pg_toast%' LOOP EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', r.nspname, r.relname, '${TARGET_DB_OWNER}'); END LOOP; END \$\$;" >/dev/null
|
||||
|
||||
log "Owner tabelle allineato a '${TARGET_DB_OWNER}'"
|
||||
|
||||
# Verify restore success
|
||||
info "Verifica integrità database..."
|
||||
TABLE_COUNT=$(kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" psql -U "${DB_USER}" -d "${DBNAME}" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';" 2>/dev/null || echo "0")
|
||||
|
||||
if [ "${TABLE_COUNT}" -gt 0 ]; then
|
||||
log "✅ Restore completato con successo"
|
||||
log "Database '${DBNAME}' contiene ${TABLE_COUNT} tabelle"
|
||||
else
|
||||
warn "⚠️ Database '${DBNAME}' non contiene tabelle pubbliche (potrebbe essere empty)"
|
||||
fi
|
||||
|
||||
# Get database size
|
||||
DB_SIZE=$(kubectl exec -i "${POD_NAME}" \
|
||||
-c "${CONTAINER_NAME}" \
|
||||
-n "${TARGET_ENV}" \
|
||||
-- env "PGPASSWORD=${DB_PASSWORD}" psql -U "${DB_USER}" -t -c "SELECT pg_size_pretty(pg_database_size('${DBNAME}'));" 2>/dev/null || echo "N/A")
|
||||
|
||||
log "Dimensione database: ${DB_SIZE}"
|
||||
log "✅ Restore completato con successo"
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Launcher script for clone-and-transform.sh
|
||||
# Fill all values below before running.
|
||||
|
||||
|
||||
GITLAB_URL="https://gitlab.com"
|
||||
GITLAB_TOKEN="glpat-E67BjdXoKvSEgHG31XZW0GM6MQpvOjEKdTozNzF5OQ8.01.171191k74"
|
||||
GITLAB_REPO_PATH=$1
|
||||
DEST_DIR="./work-repo"
|
||||
TRANSFORM_FILE=$3
|
||||
GITEA_REPO_PATH=$2
|
||||
TRANSFORMATION_ONLY="${4:-false}"
|
||||
#
|
||||
#GITLAB_URL="https://gitlab.com"
|
||||
#GITLAB_TOKEN="glpat-xxxxxxxxxxxxxxxxxxxx"
|
||||
#GITLAB_REPO_PATH="group/project"
|
||||
#DEST_DIR="./work-repo"
|
||||
#TRANSFORM_FILE="./transform.env"
|
||||
#GITEA_REPO_PATH="group/project"
|
||||
|
||||
# Set to "true" to skip GitLab clone and run only: Gitea clone + transform + commit/push
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MAIN_SCRIPT="${SCRIPT_DIR}/clone-and-transform.sh"
|
||||
|
||||
if [[ ! -f "$MAIN_SCRIPT" ]]; then
|
||||
echo "[ERROR] Main script not found: $MAIN_SCRIPT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x "$MAIN_SCRIPT"
|
||||
|
||||
if [[ "$TRANSFORMATION_ONLY" == "true" ]]; then
|
||||
"$MAIN_SCRIPT" \
|
||||
-transformation \
|
||||
--dest "$DEST_DIR" \
|
||||
--transform "$TRANSFORM_FILE" \
|
||||
--gitea-repo "$GITEA_REPO_PATH"
|
||||
else
|
||||
"$MAIN_SCRIPT" \
|
||||
--url "$GITLAB_URL" \
|
||||
--token "$GITLAB_TOKEN" \
|
||||
--repo "$GITLAB_REPO_PATH" \
|
||||
--dest "$DEST_DIR" \
|
||||
--transform "$TRANSFORM_FILE" \
|
||||
--gitea-repo "$GITEA_REPO_PATH"
|
||||
fi
|
||||
@@ -0,0 +1,49 @@
|
||||
---1) creazione nuova org su gitea Sporteams
|
||||
---2) crezione progetto stsharedservices
|
||||
---3) deploy postregsdb su stsharedservices
|
||||
---4) deploy rabbitmq su stsharedservices
|
||||
---5) export db Geco
|
||||
---6) import db Geco
|
||||
---7) creazione progetti gecofe e gecobe
|
||||
8) clonazione da analoghi st
|
||||
9) deploy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
kubectl cnpg hibernate on pg-athleteos -n athleteos-dev
|
||||
|
||||
|
||||
stpgclusterdev-rw.stsharedservices-dev.svc.cluster.local
|
||||
|
||||
kubectl cnpg delete cluster stpgclusterdev
|
||||
|
||||
devVkn9tcHSxK7E
|
||||
|
||||
|
||||
Backup
|
||||
#!/bin/sh
|
||||
source_env=dbdev
|
||||
#target_env=dbqa
|
||||
dbname=geco
|
||||
|
||||
backupfile=${dbname}.backup
|
||||
#cd /shared/dbdata/sql
|
||||
|
||||
kubectl exec -it postgresql-1-postgresql-0 -c postgresql-server -n ${source_env} -- pg_dump -U postgres -d ${dbname} -F c -b -v -f /shared/backup/$backupfile
|
||||
kubectl cp ${source_env}/postgresql-1-postgresql-0:/shared/backup/${backupfile} ${backupfile}
|
||||
|
||||
#mv $backupfile restore_${backupfile}
|
||||
kubectl cp ${backupfile} ${target_env}/postgresql-1-postgresql-0:/tmp/restore_${backupfile}
|
||||
kubectl exec -it postgresql-1-postgresql-0 -c postgresql-server -n ${target_env} -- psql -U postgres -c "drop database ${dbname}"
|
||||
kubectl exec -it postgresql-1-postgresql-0 -c postgresql-server -n ${target_env} -- psql -U postgres -c "create database ${dbname}"
|
||||
kubectl exec -it postgresql-1-postgresql-0 -c postgresql-server -n ${target_env} -- pg_restore -U postgres -d ${dbname} /tmp/restore_${backupfile}
|
||||
|
||||
|
||||
scp geco-20260418-171917.backup.gz barucci@10.20.1.101:/root/work/fromst
|
||||
|
||||
iZmBf42AVzDtD8ZcA9pFvtIGX6cbg9VvzjE0Jd7LkZnplvpceCAYjLqQRr7Z7SsA
|
||||
|
||||
|
||||
rabbitmqcluster.dbdev.svc.cluster.local=stmqclusterdev.stsharedservices-dev.svc.cluster.local
|
||||
@@ -0,0 +1,33 @@
|
||||
# Example transformations:
|
||||
# DATABASE_HOST=localhost=DATABASE_HOST=10.0.0.5
|
||||
# API_PORT=8080=API_PORT=3000
|
||||
# ENVIRONMENT=dev=ENVIRONMENT=prod
|
||||
# OLD_SECRET_KEY=abc123=NEW_SECRET_KEY=xyz789
|
||||
|
||||
#Postgres uri
|
||||
postgresql-1-postgresql-svc.dbdev.svc.cluster.local=postgresql-rw.stsharedservices-dev.svc.cluster.local
|
||||
|
||||
#dns domain
|
||||
sporteams.app=pigreco66.it
|
||||
|
||||
#postgres user password
|
||||
devVkn9tcHSxK7E=iZmBf42AVzDtD8ZcA9pFvtIGX6cbg9VvzjE0Jd7LkZnplvpceCAYjLqQRr7Z7SsA
|
||||
|
||||
#rabbitMQ uri
|
||||
rabbitmqcluster.dbdev.svc.cluster.local=stmqclusterdev.stsharedservices-dev.svc.cluster.local
|
||||
|
||||
#RABBITMQ_USER
|
||||
t2rW7tOSXkq2Oh6XZIrVlBp8Cd3kzd7M=default_user_wN2410ODWhkuXJynz9C
|
||||
|
||||
#RABBITMQ_PASSWORD
|
||||
q2zB-eeyJwDltXDhEFzJ7QhPsrOg4XDe=oENtKdDBUWOePk5OTQ36JInQ8XPsZD8W
|
||||
|
||||
#GECO_INTERNAL_URL =
|
||||
nest.gecobedev=nest.st-geco-backend
|
||||
|
||||
#docker image
|
||||
eu.gcr.io/sporteamscloud/nest%name%:%tag%=<IMAGE_TAG_nest>
|
||||
eu.gcr.io/sporteamscloud/nginx%name%:%tag%=<IMAGE_TAG_nginx>
|
||||
|
||||
#parametri
|
||||
%backendurl%=<backendurl>
|
||||
Reference in New Issue
Block a user