From d9bc71fb7c0fdc3f2705244806cc80dffc680b4f Mon Sep 17 00:00:00 2001 From: math974 Date: Fri, 24 Oct 2025 20:09:27 +0200 Subject: [PATCH 01/13] feat(kubernetes): deploy kubernetes config --- .github/workflows/deploy-dev-unified.yml | 135 +++++++++++++--------- .github/workflows/deploy-prod-unified.yml | 10 +- 2 files changed, 87 insertions(+), 58 deletions(-) diff --git a/.github/workflows/deploy-dev-unified.yml b/.github/workflows/deploy-dev-unified.yml index 6a86c85..c3c3e2f 100644 --- a/.github/workflows/deploy-dev-unified.yml +++ b/.github/workflows/deploy-dev-unified.yml @@ -20,7 +20,6 @@ env: GKE_ZONE: ${{ secrets.GKE_ZONE }} GCP_REGION: ${{ secrets.GCP_REGION }} IMAGE_NAME: tasks-app - INSTANCE_NAME: tasks-mysql jobs: # ===== PHASE 1: TERRAFORM ===== @@ -277,59 +276,81 @@ jobs: echo "✅ Images poussées avec succès vers $REGISTRY" # ===== PHASE 4: KUBERNETES DEPLOYMENT ===== - #deploy-dev: - # name: Deploy to GKE - # runs-on: ubuntu-latest - # needs: [build-and-push] - # if: github.event_name == 'push' - # environment: - # name: development - # url: https://tasks-app-dev.example.com - # steps: - # - name: Checkout - # uses: actions/checkout@v4 -# - # - name: Auth to Google Cloud (WIF) - # uses: google-github-actions/auth@v2 - # with: - # workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} - # service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} -# - # - name: Set up Cloud SDK - # uses: google-github-actions/setup-gcloud@v1 -# - # - name: Get Artifact Registry URL - # run: | - # ARTIFACT_REGISTRY_URL=$(gcloud artifacts repositories list --format="value(name)" --filter="format=DOCKER" --project=$PROJECT_ID | head -1) - # echo "REGISTRY=$ARTIFACT_REGISTRY_URL" >> $GITHUB_ENV -# - # - name: Configure kubectl - # run: | - # gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $PROJECT_ID -# - # - name: Install Helm - # uses: azure/setup-helm@v3 - # with: - # version: '3.12.0' -# - # - name: Get database password from Secret Manager - # run: | - # DB_PASSWORD=$(gcloud secrets versions access latest --secret="${INSTANCE_NAME}-app-db-password" --project=$PROJECT_ID) - # echo "DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV -# - # - name: Deploy to GKE with Helm - # run: | - # helm upgrade --install tasks-app-dev ./helm/tasks-app \ - # --namespace tasks-dev \ - # --create-namespace \ - # --values ./helm/tasks-app/values-dev.yaml \ - # --set image.repository=$REGISTRY/$PROJECT_ID/$IMAGE_NAME \ - # --set image.tag=dev-latest \ - # --set secrets.dbPassword=$DB_PASSWORD \ - # --wait --timeout=5m -# - # - name: Verify deployment - # run: | - # kubectl get pods -n tasks-dev - # kubectl get services -n tasks-dev - # kubectl get ingress -n tasks-dev + deploy-dev: + name: Deploy to GKE + runs-on: ubuntu-latest + needs: [build-and-push] + if: github.event_name == 'push' + environment: + name: Develop + url: https://tasks-app-dev.example.com + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Auth to Google Cloud (WIF) + uses: google-github-actions/auth@v2 + with: + workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v1 + + - name: Get Artifact Registry URL + run: | + echo "🔍 Récupération de l'URL Artifact Registry..." + REPO_NAME=$(gcloud artifacts repositories list --format="value(name)" --filter="format=DOCKER" --project=$PROJECT_ID | head -1) + if [ -n "$REPO_NAME" ]; then + DOCKER_REGISTRY_URL="${GCP_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}" + echo "REGISTRY=$DOCKER_REGISTRY_URL" >> $GITHUB_ENV + echo "✅ Artifact Registry URL: $DOCKER_REGISTRY_URL" + else + echo "❌ Aucun Artifact Registry trouvé !" + exit 1 + fi + + - name: Configure kubectl + run: | + echo "🔧 Configuration de kubectl..." + gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $PROJECT_ID + echo "✅ kubectl configuré pour le cluster $GKE_CLUSTER" + + - name: Install Helm + uses: azure/setup-helm@v3 + with: + version: '3.12.0' + + - name: Get database password from Secret Manager + run: | + echo "🔐 Récupération du mot de passe de la base de données..." + + # Récupérer le nom de l'instance Cloud SQL dynamiquement + DB_INSTANCE_NAME=$(gcloud sql instances list --project=$PROJECT_ID --format="value(name)" | head -1) + echo "Instance Cloud SQL trouvée: $DB_INSTANCE_NAME" + + # Récupérer le mot de passe depuis Secret Manager + DB_PASSWORD=$(gcloud secrets versions access latest --secret="${DB_INSTANCE_NAME}-app-db-password" --project=$PROJECT_ID) + echo "DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV + echo "✅ Mot de passe récupéré depuis Secret Manager pour l'instance $DB_INSTANCE_NAME" + + - name: Deploy to GKE with Helm + run: | + echo "🚀 Déploiement sur GKE avec Helm..." + helm upgrade --install tasks-app-dev ./helm/tasks-app \ + --namespace tasks-dev \ + --create-namespace \ + --values ./helm/tasks-app/values-dev.yaml \ + --set image.repository=$REGISTRY \ + --set image.tag=dev-latest \ + --set secrets.dbPassword=$DB_PASSWORD \ + --wait --timeout=5m + echo "✅ Déploiement terminé" + + - name: Verify deployment + run: | + echo "🔍 Vérification du déploiement..." + kubectl get pods -n tasks-dev + kubectl get services -n tasks-dev + kubectl get ingress -n tasks-dev + echo "✅ Vérification terminée" diff --git a/.github/workflows/deploy-prod-unified.yml b/.github/workflows/deploy-prod-unified.yml index 514ee28..ee9c9ca 100644 --- a/.github/workflows/deploy-prod-unified.yml +++ b/.github/workflows/deploy-prod-unified.yml @@ -322,8 +322,16 @@ jobs: # # - name: Get database password from Secret Manager # run: | - # DB_PASSWORD=$(gcloud secrets versions access latest --secret="${INSTANCE_NAME}-app-db-password" --project=$PROJECT_ID) + # echo "🔐 Récupération du mot de passe de la base de données..." + # + # # Récupérer le nom de l'instance Cloud SQL dynamiquement + # DB_INSTANCE_NAME=$(gcloud sql instances list --project=$PROJECT_ID --format="value(name)" | head -1) + # echo "Instance Cloud SQL trouvée: $DB_INSTANCE_NAME" + # + # # Récupérer le mot de passe depuis Secret Manager + # DB_PASSWORD=$(gcloud secrets versions access latest --secret="${DB_INSTANCE_NAME}-app-db-password" --project=$PROJECT_ID) # echo "DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV + # echo "✅ Mot de passe récupéré depuis Secret Manager pour l'instance $DB_INSTANCE_NAME" # # - name: Deploy to GKE with Helm # run: | From 71040fa17e4e20a11088d8dcb99ee19a7ebd98d2 Mon Sep 17 00:00:00 2001 From: math974 Date: Fri, 24 Oct 2025 20:33:15 +0200 Subject: [PATCH 02/13] fix(kubernetes): get location cluster variable dynamicly --- .github/workflows/deploy-dev-unified.yml | 27 ++++++++++++++++++-- .github/workflows/deploy-prod-unified.yml | 30 ++++++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-dev-unified.yml b/.github/workflows/deploy-dev-unified.yml index c3c3e2f..36bea9c 100644 --- a/.github/workflows/deploy-dev-unified.yml +++ b/.github/workflows/deploy-dev-unified.yml @@ -313,8 +313,31 @@ jobs: - name: Configure kubectl run: | echo "🔧 Configuration de kubectl..." - gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $PROJECT_ID - echo "✅ kubectl configuré pour le cluster $GKE_CLUSTER" + + # Récupérer la location du cluster depuis GCP + CLUSTER_LOCATION=$(gcloud container clusters list \ + --project=$PROJECT_ID \ + --filter="name=$GKE_CLUSTER" \ + --format="value(location)") + + echo "📍 Emplacement du cluster: $CLUSTER_LOCATION" + + # Vérifier si c'est une région ou une zone + if [[ $CLUSTER_LOCATION == *-*-[a-z] ]]; then + # Format comme europe-west9-c → cluster zoné + echo "🔸 Cluster zoné détecté" + gcloud container clusters get-credentials $GKE_CLUSTER \ + --zone $CLUSTER_LOCATION \ + --project $PROJECT_ID + else + # Format comme europe-west1 → cluster régional + echo "🔹 Cluster régional détecté" + gcloud container clusters get-credentials $GKE_CLUSTER \ + --region $CLUSTER_LOCATION \ + --project $PROJECT_ID + fi + + echo "✅ kubectl configuré pour le cluster $GKE_CLUSTER en $CLUSTER_LOCATION" - name: Install Helm uses: azure/setup-helm@v3 diff --git a/.github/workflows/deploy-prod-unified.yml b/.github/workflows/deploy-prod-unified.yml index ee9c9ca..3af22cd 100644 --- a/.github/workflows/deploy-prod-unified.yml +++ b/.github/workflows/deploy-prod-unified.yml @@ -313,7 +313,35 @@ jobs: # # - name: Configure kubectl # run: | - # gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $PROJECT_ID + # echo "🔧 Configuration de kubectl..." + # + # # Récupérer le nom et la location du cluster depuis GCP + # CLUSTER_INFO=$(gcloud container clusters list \ + # --project=$PROJECT_ID \ + # --format="value(name,location)" | head -1) + # + # CLUSTER_NAME=$(echo $CLUSTER_INFO | cut -d' ' -f1) + # CLUSTER_LOCATION=$(echo $CLUSTER_INFO | cut -d' ' -f2) + # + # echo "📍 Cluster trouvé: $CLUSTER_NAME" + # echo "📍 Emplacement du cluster: $CLUSTER_LOCATION" + # + # # Vérifier si c'est une région ou une zone + # if [[ $CLUSTER_LOCATION == *-*-[a-z] ]]; then + # # Format comme europe-west9-c → cluster zoné + # echo "🔸 Cluster zoné détecté" + # gcloud container clusters get-credentials $CLUSTER_NAME \ + # --zone $CLUSTER_LOCATION \ + # --project $PROJECT_ID + # else + # # Format comme europe-west1 → cluster régional + # echo "🔹 Cluster régional détecté" + # gcloud container clusters get-credentials $CLUSTER_NAME \ + # --region $CLUSTER_LOCATION \ + # --project $PROJECT_ID + # fi + # + # echo "✅ kubectl configuré pour le cluster $CLUSTER_NAME en $CLUSTER_LOCATION" # # - name: Install Helm # uses: azure/setup-helm@v3 From c5aec25251e3a712442816a16c6d24770a797652 Mon Sep 17 00:00:00 2001 From: math974 Date: Fri, 24 Oct 2025 20:43:12 +0200 Subject: [PATCH 03/13] fix(kubernetes): install gke auth plugin --- .github/workflows/deploy-dev-unified.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/deploy-dev-unified.yml b/.github/workflows/deploy-dev-unified.yml index 36bea9c..d3eae1f 100644 --- a/.github/workflows/deploy-dev-unified.yml +++ b/.github/workflows/deploy-dev-unified.yml @@ -297,6 +297,12 @@ jobs: - name: Set up Cloud SDK uses: google-github-actions/setup-gcloud@v1 + - name: Install gke-gcloud-auth-plugin + run: | + echo "🔧 Installation du plugin gke-gcloud-auth-plugin..." + gcloud components install gke-gcloud-auth-plugin --quiet + echo "✅ Plugin gke-gcloud-auth-plugin installé" + - name: Get Artifact Registry URL run: | echo "🔍 Récupération de l'URL Artifact Registry..." From 71691a6b2254b8ca00d8fe3b409aa1191dcd2a1b Mon Sep 17 00:00:00 2001 From: math974 Date: Fri, 24 Oct 2025 20:53:57 +0200 Subject: [PATCH 04/13] fix(kubernetes): problem syntax --- helm/tasks-app/templates/_helpers.tpl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/helm/tasks-app/templates/_helpers.tpl b/helm/tasks-app/templates/_helpers.tpl index 45733b5..54921be 100644 --- a/helm/tasks-app/templates/_helpers.tpl +++ b/helm/tasks-app/templates/_helpers.tpl @@ -11,16 +11,16 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this If release name contains chart name it will be used as a full name. */}} {{- define "tasks-app.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} {{- end }} {{/* From 64986dac20ce5388760bb37a55657a1382653056 Mon Sep 17 00:00:00 2001 From: math974 Date: Fri, 24 Oct 2025 21:02:27 +0200 Subject: [PATCH 05/13] fix(kubernetes): problem syntax --- helm/tasks-app/templates/_helpers.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm/tasks-app/templates/_helpers.tpl b/helm/tasks-app/templates/_helpers.tpl index 54921be..26f1ff8 100644 --- a/helm/tasks-app/templates/_helpers.tpl +++ b/helm/tasks-app/templates/_helpers.tpl @@ -11,7 +11,7 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this If release name contains chart name it will be used as a full name. */}} {{- define "tasks-app.fullname" -}} -{{- if .Values.fullnameOverride -}} +{{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} {{- $name := default .Chart.Name .Values.nameOverride -}} From e58784be807f3b1943319a980b9ae88fdff8a14f Mon Sep 17 00:00:00 2001 From: math974 Date: Fri, 24 Oct 2025 21:29:24 +0200 Subject: [PATCH 06/13] feat(kubernet): syntax --- helm/tasks-app/templates/_helpers.tpl | 18 +++++++++--------- helm/tasks-app/templates/deployment.yaml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/helm/tasks-app/templates/_helpers.tpl b/helm/tasks-app/templates/_helpers.tpl index 26f1ff8..45733b5 100644 --- a/helm/tasks-app/templates/_helpers.tpl +++ b/helm/tasks-app/templates/_helpers.tpl @@ -12,15 +12,15 @@ If release name contains chart name it will be used as a full name. */}} {{- define "tasks-app.fullname" -}} {{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} {{- end }} {{/* diff --git a/helm/tasks-app/templates/deployment.yaml b/helm/tasks-app/templates/deployment.yaml index c51662d..4e04ce0 100644 --- a/helm/tasks-app/templates/deployment.yaml +++ b/helm/tasks-app/templates/deployment.yaml @@ -57,7 +57,7 @@ spec: - name: {{ $key }} valueFrom: configMapKeyRef: - name: {{ include "tasks-app.fullname" . }}-config + name: {{ include "tasks-app.fullname" $ }}-config key: {{ $key }} {{- end }} {{- end }} From e22064b03af8ca2ad80cab196193e9727d9bd133 Mon Sep 17 00:00:00 2001 From: math974 Date: Fri, 24 Oct 2025 22:16:23 +0200 Subject: [PATCH 07/13] feat(kubernetes): set var --- .github/workflows/deploy-dev-unified.yml | 24 ++++++++++++--- app/main.py | 39 ++++++++++++++++++++++-- helm/tasks-app/templates/deployment.yaml | 8 +++-- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deploy-dev-unified.yml b/.github/workflows/deploy-dev-unified.yml index d3eae1f..d76af15 100644 --- a/.github/workflows/deploy-dev-unified.yml +++ b/.github/workflows/deploy-dev-unified.yml @@ -352,12 +352,20 @@ jobs: - name: Get database password from Secret Manager run: | - echo "🔐 Récupération du mot de passe de la base de données..." + echo "🔐 Récupération des informations de connexion à la base de données..." # Récupérer le nom de l'instance Cloud SQL dynamiquement DB_INSTANCE_NAME=$(gcloud sql instances list --project=$PROJECT_ID --format="value(name)" | head -1) echo "Instance Cloud SQL trouvée: $DB_INSTANCE_NAME" + # Récupérer l'IP privée de l'instance Cloud SQL + DB_HOST=$(gcloud sql instances describe $DB_INSTANCE_NAME --project=$PROJECT_ID --format="value(ipAddresses[0].ipAddress)") + echo "DB_HOST=$DB_HOST" >> $GITHUB_ENV + echo "✅ IP de la base de données: $DB_HOST" + + # Récupérer le nom de la base de données + echo "DB_NAME=tasksdb" >> $GITHUB_ENV + # Récupérer le mot de passe depuis Secret Manager DB_PASSWORD=$(gcloud secrets versions access latest --secret="${DB_INSTANCE_NAME}-app-db-password" --project=$PROJECT_ID) echo "DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV @@ -366,14 +374,22 @@ jobs: - name: Deploy to GKE with Helm run: | echo "🚀 Déploiement sur GKE avec Helm..." + echo " 📦 Image: $REGISTRY/$IMAGE_NAME:dev-latest" + echo " 🗄️ DB Host: $DB_HOST" + echo " 📊 DB Name: $DB_NAME" + helm upgrade --install tasks-app-dev ./helm/tasks-app \ --namespace tasks-dev \ --create-namespace \ --values ./helm/tasks-app/values-dev.yaml \ - --set image.repository=$REGISTRY \ + --set image.repository=$REGISTRY/$IMAGE_NAME \ --set image.tag=dev-latest \ - --set secrets.dbPassword=$DB_PASSWORD \ - --wait --timeout=5m + --set env.DB_HOST=$DB_HOST \ + --set env.DB_NAME=$DB_NAME \ + --set env.DB_USER=app_user \ + --set secrets.dbPassword="$DB_PASSWORD" \ + --wait --timeout=10m \ + --debug --atomic echo "✅ Déploiement terminé" - name: Verify deployment diff --git a/app/main.py b/app/main.py index 3e187c2..3a93d78 100644 --- a/app/main.py +++ b/app/main.py @@ -23,7 +23,24 @@ @app.get("/health") async def health_check(): """Health check endpoint for Docker healthcheck""" - return {"status": "healthy", "service": "tasks-api"} + from core.db import get_db + try: + # Vérifier la connexion à la DB + with get_db() as conn: + cur = conn.cursor() + cur.execute("SELECT 1") + cur.fetchone() + return {"status": "healthy", "service": "tasks-api", "database": "connected"} + except Exception as e: + return JSONResponse( + status_code=503, + content={ + "status": "unhealthy", + "service": "tasks-api", + "database": "disconnected", + "error": str(e) + } + ) @app.middleware("http") @@ -100,7 +117,25 @@ async def unhandled_exception_handler(request: Request, exc: Exception): @app.on_event("startup") def on_startup(): - init_db() + import time + max_retries = 5 + retry_delay = 5 # secondes + + for attempt in range(max_retries): + try: + print(f"Tentative de connexion à la base de données ({attempt + 1}/{max_retries})...") + init_db() + print("✅ Connexion à la base de données réussie !") + break + except Exception as e: + print(f"❌ Erreur de connexion à la base de données: {e}") + if attempt < max_retries - 1: + print(f"⏳ Nouvelle tentative dans {retry_delay} secondes...") + time.sleep(retry_delay) + else: + print("⚠️ Impossible de se connecter à la base de données après plusieurs tentatives.") + print("⚠️ L'application démarre mais sera en mode dégradé.") + # start background scheduler loop = asyncio.get_event_loop() # store task on app.state to allow cancellation diff --git a/helm/tasks-app/templates/deployment.yaml b/helm/tasks-app/templates/deployment.yaml index 4e04ce0..5f39d0d 100644 --- a/helm/tasks-app/templates/deployment.yaml +++ b/helm/tasks-app/templates/deployment.yaml @@ -65,14 +65,18 @@ spec: httpGet: path: /health port: http - initialDelaySeconds: 30 + initialDelaySeconds: 60 periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 readinessProbe: httpGet: path: /health port: http - initialDelaySeconds: 5 + initialDelaySeconds: 15 periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} From 30cb920fca2ade459838cae1930c1058304fa76c Mon Sep 17 00:00:00 2001 From: TomD <83023881+TomDesalmand@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:33:32 +0200 Subject: [PATCH 08/13] feat(fix-setstring) --- .github/workflows/deploy-dev-unified.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-dev-unified.yml b/.github/workflows/deploy-dev-unified.yml index d76af15..500a5c0 100644 --- a/.github/workflows/deploy-dev-unified.yml +++ b/.github/workflows/deploy-dev-unified.yml @@ -387,7 +387,7 @@ jobs: --set env.DB_HOST=$DB_HOST \ --set env.DB_NAME=$DB_NAME \ --set env.DB_USER=app_user \ - --set secrets.dbPassword="$DB_PASSWORD" \ + --set-string secrets.dbPassword="$DB_PASSWORD" \ --wait --timeout=10m \ --debug --atomic echo "✅ Déploiement terminé" From 1c5734a5caee05f60489646cba9fcdfec1582e05 Mon Sep 17 00:00:00 2001 From: TomD <83023881+TomDesalmand@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:50:33 +0200 Subject: [PATCH 09/13] feat(fix-setstring) --- .github/workflows/deploy-dev-unified.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy-dev-unified.yml b/.github/workflows/deploy-dev-unified.yml index 500a5c0..64af928 100644 --- a/.github/workflows/deploy-dev-unified.yml +++ b/.github/workflows/deploy-dev-unified.yml @@ -382,11 +382,11 @@ jobs: --namespace tasks-dev \ --create-namespace \ --values ./helm/tasks-app/values-dev.yaml \ - --set image.repository=$REGISTRY/$IMAGE_NAME \ - --set image.tag=dev-latest \ - --set env.DB_HOST=$DB_HOST \ - --set env.DB_NAME=$DB_NAME \ - --set env.DB_USER=app_user \ + --set image.repository="$REGISTRY/$IMAGE_NAME" \ + --set-string image.tag=dev-latest \ + --set-string env.DB_HOST=$DB_HOST \ + --set-string env.DB_NAME=$DB_NAME \ + --set-string env.DB_USER=tasks_app \ --set-string secrets.dbPassword="$DB_PASSWORD" \ --wait --timeout=10m \ --debug --atomic From b47f5b167ae33a5416489b0b6b07e462fc2c45f5 Mon Sep 17 00:00:00 2001 From: TomD <83023881+TomDesalmand@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:29:18 +0200 Subject: [PATCH 10/13] feat(prod update) --- .github/workflows/deploy-prod-unified.yml | 252 ++++++++++++---------- environments/dev/terraform.tfvars | 2 +- environments/prd/terraform.tfvars | 2 +- helm/tasks-app/values.yaml | 2 +- 4 files changed, 144 insertions(+), 114 deletions(-) diff --git a/.github/workflows/deploy-prod-unified.yml b/.github/workflows/deploy-prod-unified.yml index 3af22cd..9fbdc80 100644 --- a/.github/workflows/deploy-prod-unified.yml +++ b/.github/workflows/deploy-prod-unified.yml @@ -2,15 +2,15 @@ name: Deploy to Production (Unified) on: push: - branches: [ main ] + branches: [main] tags: - - 'v*' + - "v*" workflow_dispatch: inputs: force_deploy: - description: 'Force deployment (skip tests)' + description: "Force deployment (skip tests)" required: false - default: 'false' + default: false type: boolean permissions: @@ -182,17 +182,17 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.11' - + python-version: "3.11" + - name: Install dependencies run: | cd app pip install -r requirements.txt - + - name: Run tests run: | cd app @@ -222,26 +222,26 @@ jobs: - name: Get Artifact Registry URL run: | echo "🔍 Recherche d'Artifact Registry dans le projet $PROJECT_ID..." - + # Lister tous les repositories pour debug echo "📋 Repositories disponibles:" gcloud artifacts repositories list --project=$PROJECT_ID - + # Récupérer le nom du repository REPO_NAME=$(gcloud artifacts repositories list --format="value(name)" --filter="format=DOCKER" --project=$PROJECT_ID | head -1) - + if [ -n "$REPO_NAME" ]; then # Utiliser la région depuis les secrets REPO_LOCATION=$GCP_REGION - + # Construire l'URL complète pour Docker (format correct) DOCKER_REGISTRY_URL="${REPO_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}" - + echo "Debug - REPO_NAME: $REPO_NAME" echo "Debug - REPO_LOCATION: $REPO_LOCATION (depuis secret)" echo "Debug - PROJECT_ID: $PROJECT_ID" echo "Debug - URL construite: $DOCKER_REGISTRY_URL" - + echo "ARTIFACT_REGISTRY_URL=$DOCKER_REGISTRY_URL" >> $GITHUB_ENV echo "REGISTRY=$DOCKER_REGISTRY_URL" >> $GITHUB_ENV echo "✅ Artifact Registry trouvé: $DOCKER_REGISTRY_URL" @@ -257,11 +257,11 @@ jobs: - name: Configure Docker for Artifact Registry run: | echo "🔧 Configuration de Docker pour Artifact Registry..." - + # Extraire le domaine du registry (ex: europe-west1-docker.pkg.dev) REGISTRY_DOMAIN=$(echo $REGISTRY | cut -d'/' -f1) echo "Registry Domain: $REGISTRY_DOMAIN" - + # Configurer gcloud comme assistant d'identification pour le domaine gcloud auth configure-docker $REGISTRY_DOMAIN --quiet echo "✅ Docker configuré pour le domaine $REGISTRY_DOMAIN" @@ -273,107 +273,137 @@ jobs: echo "Registry: $REGISTRY" echo "Image: $IMAGE_NAME" echo "Tag: $GITHUB_SHA" - + # Construction de l'image avec le bon registry docker build -t $REGISTRY/$IMAGE_NAME:$GITHUB_SHA . docker tag $REGISTRY/$IMAGE_NAME:$GITHUB_SHA $REGISTRY/$IMAGE_NAME:prod-latest - + echo "📤 Push vers Artifact Registry..." docker push $REGISTRY/$IMAGE_NAME:$GITHUB_SHA docker push $REGISTRY/$IMAGE_NAME:prod-latest echo "✅ Images poussées avec succès vers $REGISTRY" - # ===== PHASE 4: KUBERNETES DEPLOYMENT ===== - #deploy-prod: - # name: Deploy to GKE - # runs-on: ubuntu-latest - # needs: [build-and-push] - # if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - # environment: - # name: production - # url: https://tasks-app-prod.example.com - # steps: - # - name: Checkout - # uses: actions/checkout@v4 -# - # - name: Auth to Google Cloud (WIF) - # uses: google-github-actions/auth@v2 - # with: - # workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} - # service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} -# - # - name: Set up Cloud SDK - # uses: google-github-actions/setup-gcloud@v1 -# - # - name: Get Artifact Registry URL - # run: | - # ARTIFACT_REGISTRY_URL=$(gcloud artifacts repositories list --format="value(name)" --filter="format=DOCKER" --project=$PROJECT_ID | head -1) - # echo "REGISTRY=$ARTIFACT_REGISTRY_URL" >> $GITHUB_ENV -# - # - name: Configure kubectl - # run: | - # echo "🔧 Configuration de kubectl..." - # - # # Récupérer le nom et la location du cluster depuis GCP - # CLUSTER_INFO=$(gcloud container clusters list \ - # --project=$PROJECT_ID \ - # --format="value(name,location)" | head -1) - # - # CLUSTER_NAME=$(echo $CLUSTER_INFO | cut -d' ' -f1) - # CLUSTER_LOCATION=$(echo $CLUSTER_INFO | cut -d' ' -f2) - # - # echo "📍 Cluster trouvé: $CLUSTER_NAME" - # echo "📍 Emplacement du cluster: $CLUSTER_LOCATION" - # - # # Vérifier si c'est une région ou une zone - # if [[ $CLUSTER_LOCATION == *-*-[a-z] ]]; then - # # Format comme europe-west9-c → cluster zoné - # echo "🔸 Cluster zoné détecté" - # gcloud container clusters get-credentials $CLUSTER_NAME \ - # --zone $CLUSTER_LOCATION \ - # --project $PROJECT_ID - # else - # # Format comme europe-west1 → cluster régional - # echo "🔹 Cluster régional détecté" - # gcloud container clusters get-credentials $CLUSTER_NAME \ - # --region $CLUSTER_LOCATION \ - # --project $PROJECT_ID - # fi - # - # echo "✅ kubectl configuré pour le cluster $CLUSTER_NAME en $CLUSTER_LOCATION" -# - # - name: Install Helm - # uses: azure/setup-helm@v3 - # with: - # version: '3.12.0' -# - # - name: Get database password from Secret Manager - # run: | - # echo "🔐 Récupération du mot de passe de la base de données..." - # - # # Récupérer le nom de l'instance Cloud SQL dynamiquement - # DB_INSTANCE_NAME=$(gcloud sql instances list --project=$PROJECT_ID --format="value(name)" | head -1) - # echo "Instance Cloud SQL trouvée: $DB_INSTANCE_NAME" - # - # # Récupérer le mot de passe depuis Secret Manager - # DB_PASSWORD=$(gcloud secrets versions access latest --secret="${DB_INSTANCE_NAME}-app-db-password" --project=$PROJECT_ID) - # echo "DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV - # echo "✅ Mot de passe récupéré depuis Secret Manager pour l'instance $DB_INSTANCE_NAME" -# - # - name: Deploy to GKE with Helm - # run: | - # helm upgrade --install tasks-app-prod ./helm/tasks-app \ - # --namespace tasks-prod \ - # --create-namespace \ - # --values ./helm/tasks-app/values-prod.yaml \ - # --set image.repository=$REGISTRY/$PROJECT_ID/$IMAGE_NAME \ - # --set image.tag=prod-latest \ - # --set secrets.dbPassword=$DB_PASSWORD \ - # --wait --timeout=5m -# - # - name: Verify deployment - # run: | - # kubectl get pods -n tasks-prod - # kubectl get services -n tasks-prod - # kubectl get ingress -n tasks-prod + deploy-prod: + name: Deploy to GKE + runs-on: ubuntu-latest + needs: [build-and-push] + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + environment: + name: Production + url: https://tasks-app-prod.example.com + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Auth to Google Cloud (WIF) + uses: google-github-actions/auth@v2 + with: + workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v1 + + - name: Install gke-gcloud-auth-plugin + run: | + echo "🔧 Installation du plugin gke-gcloud-auth-plugin..." + gcloud components install gke-gcloud-auth-plugin --quiet + echo "✅ Plugin gke-gcloud-auth-plugin installé" + + - name: Get Artifact Registry URL + run: | + echo "🔍 Récupération de l'URL Artifact Registry..." + REPO_NAME=$(gcloud artifacts repositories list --format="value(name)" --filter="format=DOCKER" --project=$PROJECT_ID | head -1) + if [ -n "$REPO_NAME" ]; then + DOCKER_REGISTRY_URL="${GCP_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}" + echo "REGISTRY=$DOCKER_REGISTRY_URL" >> $GITHUB_ENV + echo "✅ Artifact Registry URL: $DOCKER_REGISTRY_URL" + else + echo "❌ Aucun Artifact Registry trouvé !" + exit 1 + fi + + - name: Configure kubectl + run: | + echo "🔧 Configuration de kubectl..." + + # Récupérer la location du cluster depuis GCP + CLUSTER_LOCATION=$(gcloud container clusters list \ + --project=$PROJECT_ID \ + --filter="name=$GKE_CLUSTER" \ + --format="value(location)") + + echo "📍 Emplacement du cluster: $CLUSTER_LOCATION" + + # Vérifier si c'est une région ou une zone + if [[ $CLUSTER_LOCATION == *-*-[a-z] ]]; then + # Format comme europe-west9-c → cluster zoné + echo "🔸 Cluster zoné détecté" + gcloud container clusters get-credentials $GKE_CLUSTER \ + --zone $CLUSTER_LOCATION \ + --project $PROJECT_ID + else + # Format comme europe-west1 → cluster régional + echo "🔹 Cluster régional détecté" + gcloud container clusters get-credentials $GKE_CLUSTER \ + --region $CLUSTER_LOCATION \ + --project $PROJECT_ID + fi + + echo "✅ kubectl configuré pour le cluster $GKE_CLUSTER en $CLUSTER_LOCATION" + + - name: Install Helm + uses: azure/setup-helm@v3 + with: + version: "3.12.0" + + - name: Get database password from Secret Manager + run: | + echo "🔐 Récupération des informations de connexion à la base de données..." + + # Récupérer le nom de l'instance Cloud SQL dynamiquement + DB_INSTANCE_NAME=$(gcloud sql instances list --project=$PROJECT_ID --format="value(name)" | head -1) + echo "Instance Cloud SQL trouvée: $DB_INSTANCE_NAME" + + # Récupérer l'IP privée de l'instance Cloud SQL + DB_HOST=$(gcloud sql instances describe $DB_INSTANCE_NAME --project=$PROJECT_ID --format="value(ipAddresses[0].ipAddress)") + echo "DB_HOST=$DB_HOST" >> $GITHUB_ENV + echo "✅ IP de la base de données: $DB_HOST" + + # Récupérer le nom de la base de données + echo "DB_NAME=tasksdb" >> $GITHUB_ENV + + # Récupérer le mot de passe depuis Secret Manager + DB_PASSWORD=$(gcloud secrets versions access latest --secret="${DB_INSTANCE_NAME}-app-db-password" --project=$PROJECT_ID) + echo "DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV + echo "✅ Mot de passe récupéré depuis Secret Manager pour l'instance $DB_INSTANCE_NAME" + + - name: Deploy to GKE with Helm + run: | + echo "🚀 Déploiement sur GKE avec Helm..." + echo " 📦 Image: $REGISTRY/$IMAGE_NAME:prod-latest" + echo " 🗄️ DB Host: $DB_HOST" + echo " 📊 DB Name: $DB_NAME" + + helm upgrade --install tasks-app-prod ./helm/tasks-app \ + --namespace tasks-prod \ + --create-namespace \ + --values ./helm/tasks-app/values-prod.yaml \ + --set image.repository="$REGISTRY/$IMAGE_NAME" \ + --set-string image.tag=prod-latest \ + --set-string env.DB_HOST=$DB_HOST \ + --set-string env.DB_NAME=$DB_NAME \ + --set-string env.DB_USER=tasks_app \ + --set-string secrets.dbPassword="$DB_PASSWORD" \ + --wait --timeout=10m \ + --debug --atomic + echo "✅ Déploiement terminé" + + - name: Verify deployment + run: | + echo "🔍 Vérification du déploiement..." + kubectl get pods -n tasks-prod + kubectl get services -n tasks-prod + kubectl get ingress -n tasks-prod + echo "✅ Vérification terminée" diff --git a/environments/dev/terraform.tfvars b/environments/dev/terraform.tfvars index 4f47358..97978b8 100644 --- a/environments/dev/terraform.tfvars +++ b/environments/dev/terraform.tfvars @@ -67,6 +67,6 @@ team_member_emails = [ ] team_role = "roles/editor" instructor_email = "jeremie@jjaouen.com" -enable_instructor_binding = false +enable_instructor_binding = true auto_invite_missing_users = true billing_account_id = "0100E9-D328A7-35D6BE" diff --git a/environments/prd/terraform.tfvars b/environments/prd/terraform.tfvars index ea4e04c..92eecae 100644 --- a/environments/prd/terraform.tfvars +++ b/environments/prd/terraform.tfvars @@ -63,6 +63,6 @@ team_member_emails = [ ] team_role = "roles/editor" instructor_email = "jeremie@jjaouen.com" -enable_instructor_binding = false +enable_instructor_binding = true auto_invite_missing_users = true billing_account_id = "0100E9-D328A7-35D6BE" diff --git a/helm/tasks-app/values.yaml b/helm/tasks-app/values.yaml index dec5cda..09afb41 100644 --- a/helm/tasks-app/values.yaml +++ b/helm/tasks-app/values.yaml @@ -58,7 +58,7 @@ resources: memory: 256Mi autoscaling: - enabled: false + enabled: true minReplicas: 2 maxReplicas: 10 targetCPUUtilizationPercentage: 80 From 993effc18bc3ca3d964f102a2da7c6d48db859f4 Mon Sep 17 00:00:00 2001 From: math974 Date: Sat, 25 Oct 2025 00:03:56 +0200 Subject: [PATCH 11/13] feat(eazae): aeaz --- bootstrap-wif/main.tf | 8 ++++++++ bootstrap-wif/variables.tf | 6 ++++++ helm/tasks-app/values-dev.yaml | 17 +++++++---------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/bootstrap-wif/main.tf b/bootstrap-wif/main.tf index f7f05cb..19cd71f 100644 --- a/bootstrap-wif/main.tf +++ b/bootstrap-wif/main.tf @@ -63,4 +63,12 @@ resource "google_service_account_iam_member" "wif_binding" { service_account_id = google_service_account.sa.name role = "roles/iam.workloadIdentityUser" member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.pool.name}/attribute.repository/${var.github_owner}/${var.github_repo}" +} + +# Rôles de facturation (doivent être assignés au niveau du compte de facturation, pas du projet) +resource "google_billing_account_iam_member" "sa_billing_admin" { + count = var.billing_account_id != "" ? 1 : 0 + billing_account_id = var.billing_account_id + role = "roles/billing.admin" + member = "serviceAccount:${google_service_account.sa.email}" } \ No newline at end of file diff --git a/bootstrap-wif/variables.tf b/bootstrap-wif/variables.tf index ba4dec9..0b6027f 100644 --- a/bootstrap-wif/variables.tf +++ b/bootstrap-wif/variables.tf @@ -3,6 +3,12 @@ variable "project_id" { type = string } +variable "billing_account_id" { + description = "ID du compte de facturation GCP (optionnel, nécessaire pour les rôles billing)" + type = string + default = "" +} + variable "environment" { description = "Environnement (dev ou prd)" type = string diff --git a/helm/tasks-app/values-dev.yaml b/helm/tasks-app/values-dev.yaml index 65f9911..4697d52 100644 --- a/helm/tasks-app/values-dev.yaml +++ b/helm/tasks-app/values-dev.yaml @@ -4,6 +4,12 @@ replicaCount: 1 image: repository: gcr.io/PROJECT_ID/tasks-app tag: "dev-latest" + pullPolicy: IfNotPresent + +service: + type: LoadBalancer + port: 80 + targetPort: 8000 resources: limits: @@ -17,16 +23,7 @@ autoscaling: enabled: false ingress: - enabled: true - hosts: - - host: tasks-app-dev.example.com - paths: - - path: / - pathType: Prefix - tls: - - secretName: tasks-app-dev-tls - hosts: - - tasks-app-dev.example.com + enabled: false env: DB_HOST: "mysql-dev-service" From 3e9c0cb60feb37e3a70939b47e5c4b9aa57fb154 Mon Sep 17 00:00:00 2001 From: TomD <83023881+TomDesalmand@users.noreply.github.com> Date: Sat, 25 Oct 2025 00:06:33 +0200 Subject: [PATCH 12/13] feat(prod update) --- environments/dev/terraform.tfvars | 2 +- environments/prd/terraform.tfvars | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/environments/dev/terraform.tfvars b/environments/dev/terraform.tfvars index 97978b8..4f47358 100644 --- a/environments/dev/terraform.tfvars +++ b/environments/dev/terraform.tfvars @@ -67,6 +67,6 @@ team_member_emails = [ ] team_role = "roles/editor" instructor_email = "jeremie@jjaouen.com" -enable_instructor_binding = true +enable_instructor_binding = false auto_invite_missing_users = true billing_account_id = "0100E9-D328A7-35D6BE" diff --git a/environments/prd/terraform.tfvars b/environments/prd/terraform.tfvars index 92eecae..ea4e04c 100644 --- a/environments/prd/terraform.tfvars +++ b/environments/prd/terraform.tfvars @@ -63,6 +63,6 @@ team_member_emails = [ ] team_role = "roles/editor" instructor_email = "jeremie@jjaouen.com" -enable_instructor_binding = true +enable_instructor_binding = false auto_invite_missing_users = true billing_account_id = "0100E9-D328A7-35D6BE" From 58f140776496a2b2773c6a129bac4757efc86dca Mon Sep 17 00:00:00 2001 From: math974 Date: Sat, 25 Oct 2025 00:28:54 +0200 Subject: [PATCH 13/13] feat(kubernetes): manifest prod --- helm/tasks-app/values-prod.yaml | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 helm/tasks-app/values-prod.yaml diff --git a/helm/tasks-app/values-prod.yaml b/helm/tasks-app/values-prod.yaml new file mode 100644 index 0000000..5e1f7de --- /dev/null +++ b/helm/tasks-app/values-prod.yaml @@ -0,0 +1,42 @@ +# Production environment values +replicaCount: 2 + +image: + repository: gcr.io/PROJECT_ID/tasks-app + tag: "prod-latest" + pullPolicy: IfNotPresent + +service: + type: LoadBalancer + port: 80 + targetPort: 8000 + +resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + +autoscaling: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + +ingress: + enabled: false + +env: + DB_HOST: "10.0.0.3" # IP privée Cloud SQL (sera écrasée par le workflow) + DB_PORT: "3306" + DB_NAME: "tasksdb" # Sera écrasé par le workflow + DB_USER: "app_user" + +configMap: + data: + LOG_LEVEL: "INFO" + ENVIRONMENT: "production" +