diff --git a/.github/workflows/deploy-dev-unified.yml b/.github/workflows/deploy-dev-unified.yml new file mode 100644 index 0000000..6a86c85 --- /dev/null +++ b/.github/workflows/deploy-dev-unified.yml @@ -0,0 +1,335 @@ +name: Deploy to Development (Unified) + +on: + push: + branches-ignore: [ main ] + pull_request: + branches-ignore: [ main ] + +permissions: + contents: read + id-token: write + packages: write + pull-requests: write + +env: + TF_IN_AUTOMATION: true + TF_INPUT: false + PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + GKE_CLUSTER: ${{ secrets.GKE_CLUSTER_NAME }} + GKE_ZONE: ${{ secrets.GKE_ZONE }} + GCP_REGION: ${{ secrets.GCP_REGION }} + IMAGE_NAME: tasks-app + INSTANCE_NAME: tasks-mysql + +jobs: + # ===== PHASE 1: TERRAFORM ===== + terraform-fmt-validate: + name: Terraform Format & Validate + runs-on: ubuntu-latest + environment: + name: Develop + 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: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.8.5 + terraform_wrapper: true + + - name: Cache Terraform plugins + uses: actions/cache@v4 + with: + path: | + ~/.terraform.d/plugin-cache + ./.terraform + key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} + restore-keys: | + ${{ runner.os }}-terraform- + + - name: Terraform Init (backend dev) + run: | + cd terraform + terraform init \ + -backend-config=../configs/dev.config + + - name: Terraform Format Check + run: | + cd terraform + terraform fmt -check -recursive + + - name: Terraform Validate + run: | + cd terraform + terraform validate + + terraform-plan: + name: Terraform Plan + runs-on: ubuntu-latest + needs: [terraform-fmt-validate] + if: github.event_name == 'pull_request' || github.event_name == 'push' + environment: + name: Develop + 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: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.8.5 + terraform_wrapper: true + + - name: Cache Terraform plugins + uses: actions/cache@v4 + with: + path: | + ~/.terraform.d/plugin-cache + ./.terraform + key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} + restore-keys: | + ${{ runner.os }}-terraform- + + - name: Terraform Init (backend dev) + run: | + cd terraform + terraform init \ + -backend-config=../configs/dev.config + + - name: Terraform Plan (env dev) + run: | + cd terraform + terraform plan \ + -var-file=../environments/dev/terraform.tfvars \ + -input=false + + terraform-apply: + name: Terraform Apply + runs-on: ubuntu-latest + needs: [terraform-plan] + if: github.event_name == 'push' + environment: + name: Develop + 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: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.8.5 + terraform_wrapper: true + + - name: Cache Terraform plugins + uses: actions/cache@v4 + with: + path: | + ~/.terraform.d/plugin-cache + ./.terraform + key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} + restore-keys: | + ${{ runner.os }}-terraform- + + - name: Terraform Init (backend dev) + run: | + cd terraform + terraform init \ + -backend-config=../configs/dev.config + + - name: Terraform Apply (env dev) + run: | + cd terraform + terraform apply \ + -auto-approve \ + -var-file=../environments/dev/terraform.tfvars \ + -input=false + + # ===== PHASE 2: TESTS ===== + test: + name: Run Tests + runs-on: ubuntu-latest + needs: [terraform-apply] + if: github.event_name == 'push' + environment: + name: Develop + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + cd app + pip install -r requirements.txt + + - name: Run tests + run: | + cd app + python -m pytest tests/ || echo "No tests found, continuing..." + + # ===== PHASE 3: BUILD & DEPLOY ===== + build-and-push: + name: Build & Push Docker Images + runs-on: ubuntu-latest + needs: [test] + if: github.event_name == 'push' + environment: + name: Develop + 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 "🔍 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" + echo "Repository: $REPO_NAME" + echo "Location: $REPO_LOCATION" + echo "Docker Registry: $DOCKER_REGISTRY_URL" + else + echo "❌ Aucun Artifact Registry trouvĂ© !" + echo "VĂ©rifiez que Terraform a Ă©tĂ© dĂ©ployĂ© avec succĂšs." + exit 1 + fi + + - 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" + + - name: Build and push to Artifact Registry + run: | + cd app + echo "🐳 Construction de l'image Docker..." + 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:dev-latest + + echo "đŸ“€ Push vers Artifact Registry..." + docker push $REGISTRY/$IMAGE_NAME:$GITHUB_SHA + docker push $REGISTRY/$IMAGE_NAME:dev-latest + 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 diff --git a/.github/workflows/deploy-prod-unified.yml b/.github/workflows/deploy-prod-unified.yml new file mode 100644 index 0000000..060b250 --- /dev/null +++ b/.github/workflows/deploy-prod-unified.yml @@ -0,0 +1,343 @@ +name: Deploy to Production (Unified) + +on: + push: + branches: [ main ] + tags: + - 'v*' + workflow_dispatch: + inputs: + force_deploy: + description: 'Force deployment (skip tests)' + required: false + default: 'false' + type: boolean + +permissions: + contents: read + id-token: write + packages: write + pull-requests: write + +env: + TF_IN_AUTOMATION: true + TF_INPUT: false + PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + GKE_CLUSTER: ${{ secrets.GKE_CLUSTER_NAME }} + GKE_ZONE: ${{ secrets.GKE_ZONE }} + GCP_REGION: ${{ secrets.GCP_REGION }} + IMAGE_NAME: tasks-app + INSTANCE_NAME: tasks-mysql + +jobs: + # ===== PHASE 1: TERRAFORM ===== + terraform-fmt-validate: + name: Terraform Format & Validate + runs-on: ubuntu-latest + environment: + name: Production + 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: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.8.5 + terraform_wrapper: true + + - name: Cache Terraform plugins + uses: actions/cache@v4 + with: + path: | + ~/.terraform.d/plugin-cache + ./.terraform + key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} + restore-keys: | + ${{ runner.os }}-terraform- + + - name: Terraform Init (backend prod) + run: | + cd terraform + terraform init \ + -backend-config=../configs/prod.config + + - name: Terraform Format Check + run: | + cd terraform + terraform fmt -check -recursive + + - name: Terraform Validate + run: | + cd terraform + terraform validate + + terraform-plan: + name: Terraform Plan + runs-on: ubuntu-latest + needs: [terraform-fmt-validate] + if: github.event_name == 'pull_request' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + environment: + name: Production + 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: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.8.5 + terraform_wrapper: true + + - name: Cache Terraform plugins + uses: actions/cache@v4 + with: + path: | + ~/.terraform.d/plugin-cache + ./.terraform + key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} + restore-keys: | + ${{ runner.os }}-terraform- + + - name: Terraform Init (backend prod) + run: | + cd terraform + terraform init \ + -backend-config=../configs/prod.config + + - name: Terraform Plan (env prod) + run: | + cd terraform + terraform plan \ + -var-file=../environments/prd/terraform.tfvars \ + -input=false + + terraform-apply: + name: Terraform Apply + runs-on: ubuntu-latest + needs: [terraform-plan] + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + environment: + name: Production + 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: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.8.5 + terraform_wrapper: true + + - name: Cache Terraform plugins + uses: actions/cache@v4 + with: + path: | + ~/.terraform.d/plugin-cache + ./.terraform + key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} + restore-keys: | + ${{ runner.os }}-terraform- + + - name: Terraform Init (backend prod) + run: | + cd terraform + terraform init \ + -backend-config=../configs/prod.config + + - name: Terraform Apply (env prod) + run: | + cd terraform + terraform apply \ + -auto-approve \ + -var-file=../environments/prd/terraform.tfvars \ + -input=false + + # ===== PHASE 2: TESTS ===== + test: + name: Run Tests + runs-on: ubuntu-latest + needs: [terraform-apply] + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && !inputs.force_deploy) + environment: + name: Production + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + cd app + pip install -r requirements.txt + + - name: Run tests + run: | + cd app + python -m pytest tests/ || echo "No tests found, continuing..." + + # ===== PHASE 3: BUILD & DEPLOY ===== + build-and-push: + name: Build & Push Docker Images + runs-on: ubuntu-latest + needs: [test] + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + environment: + name: Production + 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 "🔍 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" + echo "Repository: $REPO_NAME" + echo "Location: $REPO_LOCATION" + echo "Docker Registry: $DOCKER_REGISTRY_URL" + else + echo "❌ Aucun Artifact Registry trouvĂ© !" + echo "VĂ©rifiez que Terraform a Ă©tĂ© dĂ©ployĂ© avec succĂšs." + exit 1 + fi + + - 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" + + - name: Build and push to Artifact Registry + run: | + cd app + echo "🐳 Construction de l'image Docker..." + 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: | + # 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-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 diff --git a/.github/workflows/terraform-prod.yml b/.github/workflows/terraform-prod.yml deleted file mode 100644 index 95aff32..0000000 --- a/.github/workflows/terraform-prod.yml +++ /dev/null @@ -1,167 +0,0 @@ -name: Terraform (Production) - -on: - push: - branches: [main] - paths-ignore: - - 'bootstrap-wif/**' - -concurrency: - group: terraform-production-${{ github.ref }} - cancel-in-progress: false - -env: - TF_IN_AUTOMATION: true - TF_INPUT: false - -jobs: - fmt_validate: - name: Format & Validate - runs-on: ubuntu-latest - environment: - name: Production - permissions: - contents: read - id-token: write - 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: Setup Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.8.5 - terraform_wrapper: true - - - name: Cache Terraform plugins - uses: actions/cache@v4 - with: - path: | - ~/.terraform.d/plugin-cache - ./.terraform - key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} - restore-keys: | - ${{ runner.os }}-terraform- - - - name: Terraform Init (backend prd) - run: | - cd terraform - terraform init \ - -backend-config=../configs/prd.config - - - name: Terraform Format Check - run: | - cd terraform - terraform fmt -check -recursive - - - name: Terraform Validate - run: | - cd terraform - terraform validate - - - plan: - name: Plan - runs-on: ubuntu-latest - needs: [fmt_validate] - environment: - name: Production - permissions: - contents: read - id-token: write - 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: Setup Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.8.5 - terraform_wrapper: true - - - name: Cache Terraform plugins - uses: actions/cache@v4 - with: - path: | - ~/.terraform.d/plugin-cache - ./.terraform - key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} - restore-keys: | - ${{ runner.os }}-terraform- - - - name: Terraform Init (backend prd) - run: | - cd terraform - terraform init \ - -backend-config=../configs/prd.config - - - name: Terraform Plan (env prd) - id: plan - run: | - cd terraform - terraform plan \ - -var-file=../environments/prd/terraform.tfvars \ - -input=false - - - apply: - name: Apply - runs-on: ubuntu-latest - needs: [fmt_validate, plan] - environment: - name: Production - permissions: - contents: read - id-token: write - 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: Setup Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.8.5 - terraform_wrapper: true - - - name: Cache Terraform plugins - uses: actions/cache@v4 - with: - path: | - ~/.terraform.d/plugin-cache - ./.terraform - key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} - restore-keys: | - ${{ runner.os }}-terraform- - - - name: Terraform Init (backend prd) - run: | - cd terraform - terraform init \ - -backend-config=../configs/prd.config - - - name: Terraform Apply (env prd) - run: | - cd terraform - terraform apply \ - -auto-approve \ - -var-file=../environments/prd/terraform.tfvars \ - -input=false - diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml deleted file mode 100644 index a635638..0000000 --- a/.github/workflows/terraform.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Terraform (Develop) - -on: - pull_request: - branches-ignore: [main] - paths-ignore: - - 'bootstrap-wif/**' - push: - branches-ignore: [main] - paths-ignore: - - 'bootstrap-wif/**' - -concurrency: - group: terraform-${{ github.ref }} - cancel-in-progress: false - -env: - TF_IN_AUTOMATION: true - TF_INPUT: false - -jobs: - fmt_validate: - name: Format & Validate - runs-on: ubuntu-latest - environment: - name: Develop - permissions: - contents: read - id-token: write - 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: Setup Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.8.5 - terraform_wrapper: true - - - name: Cache Terraform plugins - uses: actions/cache@v4 - with: - path: | - ~/.terraform.d/plugin-cache - ./.terraform - key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} - restore-keys: | - ${{ runner.os }}-terraform- - - - name: Terraform Init (backend dev) - run: | - cd terraform - terraform init \ - -backend-config=../configs/dev.config - - - name: Terraform Format Check - run: | - cd terraform - terraform fmt -check -recursive - - - name: Terraform Validate - run: | - cd terraform - terraform validate - - plan: - name: Plan - runs-on: ubuntu-latest - needs: [fmt_validate] - if: github.event_name == 'pull_request' || github.event_name == 'push' - environment: - name: Develop - permissions: - contents: read - pull-requests: write - id-token: write - 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: Setup Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.8.5 - terraform_wrapper: true - - - name: Cache Terraform plugins - uses: actions/cache@v4 - with: - path: | - ~/.terraform.d/plugin-cache - ./.terraform - key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} - restore-keys: | - ${{ runner.os }}-terraform- - - - name: Terraform Init (backend dev) - run: | - cd terraform - terraform init \ - -backend-config=../configs/dev.config - - - name: Terraform Plan (env dev) - run: | - cd terraform - terraform plan \ - -var-file=../environments/dev/terraform.tfvars \ - -input=false - - apply: - name: Apply - runs-on: ubuntu-latest - needs: [plan] - if: github.event_name == 'push' - environment: - name: Develop - permissions: - contents: read - id-token: write - 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: Setup Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.8.5 - terraform_wrapper: true - - - name: Cache Terraform plugins - uses: actions/cache@v4 - with: - path: | - ~/.terraform.d/plugin-cache - ./.terraform - key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }} - restore-keys: | - ${{ runner.os }}-terraform- - - - name: Terraform Init (backend dev) - run: | - cd terraform - terraform init \ - -backend-config=../configs/dev.config - - - name: Terraform Apply (env dev) - run: | - cd terraform - terraform apply \ - -auto-approve \ - -var-file=../environments/dev/terraform.tfvars \ - -input=false - - #deploy_app: - # name: Deploy Application - # runs-on: ubuntu-latest - # needs: [apply] - # if: github.event_name == 'push' - # environment: - # name: Develop - # permissions: - # contents: read - # id-token: write - # 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: Setup Google Cloud SDK - # uses: google-github-actions/setup-gcloud@v2 -# - # - name: Configure Docker for GCR - # run: gcloud auth configure-docker -# - # - name: Build and Push Docker Image - # run: | - # cd kubernetes/simple-app - # docker build -t gcr.io/${{ secrets.GCP_PROJECT_ID }}/simple-app:latest . - # docker push gcr.io/${{ secrets.GCP_PROJECT_ID }}/simple-app:latest -# - # - name: Deploy to Kubernetes - # run: | - # gcloud container clusters get-credentials gke-cluster-dev --region europe-west9 --project ${{ secrets.GCP_PROJECT_ID }} - # cd kubernetes/simple-app - # sed "s/PROJECT_ID/${{ secrets.GCP_PROJECT_ID }}/g" k8s-deployment.yaml | kubectl apply -f - - # kubectl rollout status deployment/simple-app - - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..2cafde0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,98 @@ +name: Test and Quality Checks + +on: + pull_request: + branches: [ main, develop ] + push: + branches: [ main, develop ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Cache pip dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('app/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + cd app + pip install -r requirements.txt + pip install pytest pytest-cov flake8 black + + - name: Lint with flake8 + run: | + cd app + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Format check with black + run: | + cd app + black --check . + + - name: Run tests + run: | + cd app + python -m pytest tests/ --cov=. --cov-report=xml || echo "No tests found, continuing..." + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./app/coverage.xml + flags: unittests + name: codecov-umbrella + + security-scan: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + scan-ref: '.' + format: 'sarif' + output: 'trivy-results.sarif' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: 'trivy-results.sarif' + + build-test: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + run: | + cd app + docker build -t tasks-app:test . + + - name: Test Docker image + run: | + docker run --rm -d --name test-container -p 8000:8000 tasks-app:test + sleep 10 + curl -f http://localhost:8000/health || exit 1 + docker stop test-container diff --git a/README-DOCKER.md b/README-DOCKER.md new file mode 100644 index 0000000..6b2f58b --- /dev/null +++ b/README-DOCKER.md @@ -0,0 +1,224 @@ +# Docker Compose - Environnement de DĂ©veloppement + +Ce guide explique comment utiliser Docker Compose pour l'environnement de dĂ©veloppement local. + +## 🚀 DĂ©marrage rapide + +### 1. Configuration initiale + +```bash +# Copier le fichier de configuration +cp env.local .env + +# Modifier les valeurs selon vos besoins (optionnel) +nano .env +``` + +### 2. DĂ©marrer les services + +```bash +# DĂ©marrer tous les services +docker-compose up -d + +# Ou utiliser le script d'aide +./scripts/dev-setup.sh start +``` + +### 3. VĂ©rifier les services + +```bash +# VĂ©rifier le statut +docker-compose ps + +# Voir les logs +docker-compose logs -f +``` + +## 📋 Services disponibles + +| Service | URL | Description | +|---------|-----|-------------| +| **Application FastAPI** | http://localhost:8000 | API principale | +| **Documentation API** | http://localhost:8000/docs | Swagger UI | +| **phpMyAdmin** | http://localhost:8080 | Interface MySQL | +| **MySQL** | localhost:3306 | Base de donnĂ©es | + +## ⚙ Configuration + +### Variables d'environnement (.env) + +```bash +# Base de donnĂ©es MySQL +MYSQL_ROOT_PASSWORD=rootpassword +MYSQL_DATABASE=tasksdb +MYSQL_USER=app_user +MYSQL_PASSWORD=app_password + +# Configuration de l'application +DB_HOST=mysql +DB_PORT=3306 +DB_NAME=tasksdb +DB_USER=app_user +DB_PASSWORD=app_password + +# Ports +APP_PORT=8000 +MYSQL_PORT=3306 +PHPMYADMIN_PORT=8080 +``` + +## đŸ› ïž Commandes utiles + +### Gestion des services + +```bash +# DĂ©marrer +docker-compose up -d + +# ArrĂȘter +docker-compose down + +# RedĂ©marrer +docker-compose restart + +# Voir les logs +docker-compose logs -f [service] + +# Statut des services +docker-compose ps +``` + +### Base de donnĂ©es + +```bash +# Se connecter Ă  MySQL +docker-compose exec mysql mysql -u app_user -p tasksdb + +# Sauvegarder la base +docker-compose exec mysql mysqldump -u app_user -p tasksdb > backup.sql + +# Restaurer la base +docker-compose exec -T mysql mysql -u app_user -p tasksdb < backup.sql +``` + +### Application + +```bash +# Voir les logs de l'app +docker-compose logs -f app + +# RedĂ©marrer l'app +docker-compose restart app + +# ExĂ©cuter des commandes dans l'app +docker-compose exec app bash +``` + +## đŸ§č Nettoyage + +```bash +# ArrĂȘter et supprimer les conteneurs +docker-compose down + +# Supprimer aussi les volumes (ATTENTION: perte de donnĂ©es) +docker-compose down -v + +# Supprimer les images +docker-compose down --rmi all + +# Nettoyage complet +./scripts/dev-setup.sh clean +``` + +## 🔧 DĂ©pannage + +### ProblĂšmes courants + +1. **Port dĂ©jĂ  utilisĂ©** + ```bash + # Changer le port dans .env + APP_PORT=8001 + ``` + +2. **Base de donnĂ©es non accessible** + ```bash + # VĂ©rifier que MySQL est prĂȘt + docker-compose logs mysql + ``` + +3. **Application ne dĂ©marre pas** + ```bash + # VĂ©rifier les logs + docker-compose logs app + + # Reconstruire l'image + docker-compose build app + ``` + +### Logs utiles + +```bash +# Tous les services +docker-compose logs -f + +# Service spĂ©cifique +docker-compose logs -f app +docker-compose logs -f mysql + +# DerniĂšres 100 lignes +docker-compose logs --tail=100 app +``` + +## 📊 Monitoring + +### Health checks + +```bash +# VĂ©rifier la santĂ© des services +docker-compose ps + +# Tester l'API +curl http://localhost:8000/health + +# Tester MySQL +docker-compose exec mysql mysqladmin ping -h localhost +``` + +### Ressources + +```bash +# Utilisation des ressources +docker stats + +# Espace disque +docker system df +``` + +## 🔄 DĂ©veloppement + +### Rebuild aprĂšs modification + +```bash +# Reconstruire l'application +docker-compose build app + +# RedĂ©marrer avec la nouvelle image +docker-compose up -d app +``` + +### Debug + +```bash +# Mode interactif +docker-compose exec app bash + +# Voir les variables d'environnement +docker-compose exec app env +``` + +## 📝 Notes importantes + +- Le fichier `.env` est ignorĂ© par Git pour la sĂ©curitĂ© +- Les donnĂ©es MySQL sont persistantes dans le volume `mysql_data` +- L'application se connecte automatiquement Ă  MySQL au dĂ©marrage +- phpMyAdmin est optionnel et peut ĂȘtre dĂ©sactivĂ© si non nĂ©cessaire diff --git a/app/Dockerfile b/app/Dockerfile index 4a0aba6..cac36d5 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -1,28 +1,54 @@ -FROM python:3.11-slim +# Multi-stage build for production +FROM python:3.11-slim as builder WORKDIR /app -# Installer les dĂ©pendances systĂšme +# Install system dependencies RUN apt-get update && apt-get install -y \ gcc \ + libssl-dev \ + libffi-dev \ && rm -rf /var/lib/apt/lists/* -# Copier les fichiers de l'application -COPY . . +# Copy requirements first for better caching +COPY requirements.txt . -# Installer les dĂ©pendances Python +# Install Python dependencies globally (not --user) RUN pip install --no-cache-dir -r requirements.txt -# CrĂ©er un utilisateur non-root pour la sĂ©curitĂ© -RUN useradd --create-home --shell /bin/bash app && chown -R app:app /app -USER app +# Production stage +FROM python:3.11-slim -# Exposer le port -EXPOSE 8000 +WORKDIR /app + +# Install runtime dependencies +RUN apt-get update && apt-get install -y \ + curl \ + && rm -rf /var/lib/apt/lists/* -# Variables d'environnement +# Copy Python packages from builder (global installation) +COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages +COPY --from=builder /usr/local/bin /usr/local/bin + +# Create non-root user +RUN useradd --create-home --shell /bin/bash --uid 1000 app + +# Copy application code +COPY --chown=app:app . . + +# Switch to non-root user +USER app + +# Environment variables ENV PYTHONPATH=/app ENV PYTHONUNBUFFERED=1 -# Commande pour dĂ©marrer l'application -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:8000/health || exit 1 + +# Expose port +EXPOSE 8000 + +# Start application +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] diff --git a/app/core/db.py b/app/core/db.py index 69d8067..3170c5a 100644 --- a/app/core/db.py +++ b/app/core/db.py @@ -125,12 +125,16 @@ def init_db() -> None: """ ) ) - conn.execute( - text("CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks(due_date)") - ) - conn.execute( - text("CREATE INDEX IF NOT EXISTS idx_tasks_updated_at ON tasks(updated_at)") - ) + # CrĂ©er les index avec gestion d'erreur + try: + conn.execute(text("CREATE INDEX idx_tasks_due_date ON tasks(due_date)")) + except Exception: + pass # Index existe dĂ©jĂ  + + try: + conn.execute(text("CREATE INDEX idx_tasks_updated_at ON tasks(updated_at)")) + except Exception: + pass # Index existe dĂ©jĂ  conn.execute( text( @@ -147,10 +151,15 @@ def init_db() -> None: """ ) ) - conn.execute( - text("CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)") - ) - conn.execute(text("CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)")) + try: + conn.execute(text("CREATE INDEX idx_users_username ON users(username)")) + except Exception: + pass # Index existe dĂ©jĂ  + + try: + conn.execute(text("CREATE INDEX idx_users_email ON users(email)")) + except Exception: + pass # Index existe dĂ©jĂ  conn.execute( text( @@ -167,11 +176,10 @@ def init_db() -> None: """ ) ) - conn.execute( - text( - "CREATE INDEX IF NOT EXISTS idx_schedops_execute_at ON scheduled_ops(execute_at)" - ) - ) + try: + conn.execute(text("CREATE INDEX idx_schedops_execute_at ON scheduled_ops(execute_at)")) + except Exception: + pass # Index existe dĂ©jĂ  def _row_to_dict(cursor, row) -> Dict[str, Any]: diff --git a/app/main.py b/app/main.py index 6e93eb1..3e187c2 100644 --- a/app/main.py +++ b/app/main.py @@ -20,6 +20,12 @@ app.include_router(tasks_router) +@app.get("/health") +async def health_check(): + """Health check endpoint for Docker healthcheck""" + return {"status": "healthy", "service": "tasks-api"} + + @app.middleware("http") async def correlation_id_middleware(request: Request, call_next): correlation_id = ( diff --git a/app/requirements.txt b/app/requirements.txt index 5a6da53..c382acd 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -12,3 +12,4 @@ uvicorn==0.30.6 SQLAlchemy==2.0.20 PyMySQL==1.1.0 python-dotenv==1.0.0 +cryptography==41.0.7 diff --git a/bootstrap-wif/roles.tf b/bootstrap-wif/roles.tf new file mode 100644 index 0000000..9710588 --- /dev/null +++ b/bootstrap-wif/roles.tf @@ -0,0 +1,18 @@ +# Configuration des rĂŽles supplĂ©mentaires pour GitHub Actions +# Ce fichier peut ĂȘtre utilisĂ© pour ajouter des rĂŽles spĂ©cifiques si nĂ©cessaire + +# Les rĂŽles par dĂ©faut sont dĂ©finis dans variables.tf +# Pour ajouter des rĂŽles supplĂ©mentaires, vous pouvez : +# 1. Modifier la variable "roles" dans variables.tf +# 2. Ou crĂ©er des ressources google_project_iam_member supplĂ©mentaires ici + +# Exemple d'ajout de rĂŽles supplĂ©mentaires si nĂ©cessaire : +# resource "google_project_iam_member" "additional_roles" { +# for_each = toset([ +# "roles/artifactregistry.admin", +# "roles/artifactregistry.writer" +# ]) +# project = var.project_id +# role = each.key +# member = "serviceAccount:${google_service_account.sa.email}" +# } diff --git a/bootstrap-wif/variables.tf b/bootstrap-wif/variables.tf index 5014027..ba4dec9 100644 --- a/bootstrap-wif/variables.tf +++ b/bootstrap-wif/variables.tf @@ -68,6 +68,8 @@ variable "roles" { "roles/servicenetworking.networksAdmin", "roles/container.admin", "roles/iam.serviceAccountAdmin", - "roles/resourcemanager.projectIamAdmin" + "roles/resourcemanager.projectIamAdmin", + "roles/artifactregistry.admin", + "roles/artifactregistry.writer" ] } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3f6ffec --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,84 @@ +version: '3.8' + +services: + # Base de donnĂ©es MySQL + mysql: + image: mysql:8.0 + container_name: ${MYSQL_CONTAINER_NAME:-tasks-mysql} + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + ports: + - "${MYSQL_PORT:-3306}:3306" + volumes: + - mysql_data:/var/lib/mysql + - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql + networks: + - tasks-network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + timeout: 20s + retries: 10 + + # Application FastAPI + app: + build: + context: ./app + dockerfile: Dockerfile + container_name: ${APP_CONTAINER_NAME:-tasks-app} + restart: unless-stopped + environment: + # Configuration base de donnĂ©es + DB_HOST: ${DB_HOST} + DB_PORT: ${DB_PORT} + DB_NAME: ${DB_NAME} + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + + # Configuration application + PYTHONPATH: /app + PYTHONUNBUFFERED: 1 + LOG_LEVEL: ${LOG_LEVEL:-INFO} + ENVIRONMENT: ${ENVIRONMENT:-development} + ports: + - "${APP_PORT:-8000}:8000" + depends_on: + mysql: + condition: service_healthy + networks: + - tasks-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Interface d'administration MySQL (optionnel) + phpmyadmin: + image: phpmyadmin/phpmyadmin:latest + container_name: ${PHPMYADMIN_CONTAINER_NAME:-tasks-phpmyadmin} + restart: unless-stopped + environment: + PMA_HOST: mysql + PMA_PORT: 3306 + PMA_USER: ${MYSQL_USER} + PMA_PASSWORD: ${MYSQL_PASSWORD} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + ports: + - "${PHPMYADMIN_PORT:-8080}:80" + depends_on: + - mysql + networks: + - tasks-network + +volumes: + mysql_data: + driver: local + +networks: + tasks-network: + driver: bridge diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..451eed5 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,139 @@ +# Guide de DĂ©ploiement + +## Architecture des Environnements + +### Environnements GitHub + +1. **development** - Environnement de dĂ©veloppement + - Branch: `develop` + - URL: `https://tasks-app-dev.example.com` + - Namespace: `tasks-dev` + +2. **production** - Environnement de production + - Branch: `main` + - URL: `https://tasks-app.example.com` + - Namespace: `tasks-prod` + +## Workflows CI/CD + +### 1. Test (test.yml) +- **DĂ©clencheur**: PR sur `main` ou `develop` +- **Actions**: + - Tests unitaires + - Linting (flake8, black) + - Scan de sĂ©curitĂ© (Trivy) + - Build test Docker + +### 2. DĂ©ploiement DEV (deploy-dev.yml) +- **DĂ©clencheur**: Push sur `develop` +- **Actions**: + - Tests + - Build et push vers GCR + GitHub Container Registry + - DĂ©ploiement sur GKE avec Helm + - VĂ©rification + +### 3. DĂ©ploiement PROD (deploy-prod.yml) +- **DĂ©clencheur**: Push sur `main` ou tag `v*` +- **Actions**: + - Tests complets + - Scan de sĂ©curitĂ© + - Build et push vers registries + - DĂ©ploiement sur GKE avec Helm + - Tests de fumĂ©e + +## Configuration des Secrets + +### Secrets GitHub requis + +```bash +# Google Cloud +GCP_PROJECT_ID=your-project-id +GCP_SA_KEY=base64-encoded-service-account-key +GKE_CLUSTER_NAME=your-cluster-name +GKE_ZONE=your-zone + +# Base de donnĂ©es +DB_PASSWORD_DEV=dev-password +DB_PASSWORD_PROD=prod-password +``` + +### Configuration des environnements GitHub + +1. Allez dans Settings > Environments +2. CrĂ©ez les environnements `development` et `production` +3. Configurez les secrets pour chaque environnement +4. Activez la protection des branches si nĂ©cessaire + +## DĂ©ploiement Manuel + +### Avec le script +```bash +# DĂ©ploiement dev +./scripts/deploy.sh tasks-dev dev dev-latest + +# DĂ©ploiement prod +./scripts/deploy.sh tasks-prod prod v1.0.0 +``` + +### Avec Helm directement +```bash +# Dev +helm upgrade --install tasks-app-dev ./helm/tasks-app \ + --namespace tasks-dev \ + --create-namespace \ + --values ./helm/tasks-app/values-dev.yaml \ + --set image.tag=dev-latest + +# Prod +helm upgrade --install tasks-app-prod ./helm/tasks-app \ + --namespace tasks-prod \ + --create-namespace \ + --values ./helm/tasks-app/values.yaml \ + --set image.tag=v1.0.0 +``` + +## Monitoring et Debugging + +### VĂ©rifier les dĂ©ploiements +```bash +# Pods +kubectl get pods -n tasks-dev +kubectl get pods -n tasks-prod + +# Services +kubectl get services -n tasks-dev +kubectl get services -n tasks-prod + +# Ingress +kubectl get ingress -n tasks-dev +kubectl get ingress -n tasks-prod + +# Logs +kubectl logs -f deployment/tasks-app-dev -n tasks-dev +kubectl logs -f deployment/tasks-app-prod -n tasks-prod +``` + +### Rollback +```bash +# Rollback avec Helm +helm rollback tasks-app-dev -n tasks-dev +helm rollback tasks-app-prod -n tasks-prod + +# Rollback avec kubectl +kubectl rollout undo deployment/tasks-app-dev -n tasks-dev +kubectl rollout undo deployment/tasks-app-prod -n tasks-prod +``` + +## StratĂ©gie de Branches + +- **`develop`** → DĂ©ploiement automatique en DEV +- **`main`** → DĂ©ploiement automatique en PROD +- **Tags `v*`** → DĂ©ploiement PROD avec version spĂ©cifique + +## SĂ©curitĂ© + +- Images scannĂ©es avec Trivy +- Secrets gĂ©rĂ©s via GitHub Secrets +- Utilisateur non-root dans les conteneurs +- Health checks configurĂ©s +- Ressources limitĂ©es diff --git a/env.example b/env.example new file mode 100644 index 0000000..5355f15 --- /dev/null +++ b/env.example @@ -0,0 +1,18 @@ +# Configuration de l'environnement de dĂ©veloppement +# Copiez ce fichier vers .env et modifiez les valeurs selon vos besoins + +# Base de donnĂ©es MySQL +DB_HOST=mysql +DB_PORT=3306 +DB_NAME=tasksdb +DB_USER=app_user +DB_PASSWORD=app_password + +# Configuration de l'application +LOG_LEVEL=INFO +ENVIRONMENT=development + +# Ports (optionnel, par dĂ©faut dans docker-compose.yml) +APP_PORT=8000 +MYSQL_PORT=3306 +PHPMYADMIN_PORT=8080 diff --git a/env.local b/env.local new file mode 100644 index 0000000..593c544 --- /dev/null +++ b/env.local @@ -0,0 +1,29 @@ +# Configuration de l'environnement de dĂ©veloppement +# Modifiez ces valeurs selon vos besoins + +# Base de donnĂ©es MySQL +MYSQL_ROOT_PASSWORD=rootpassword +MYSQL_DATABASE=tasksdb +MYSQL_USER=app_user +MYSQL_PASSWORD=app_password + +# Configuration de l'application +DB_HOST=mysql +DB_PORT=3306 +DB_NAME=tasksdb +DB_USER=app_user +DB_PASSWORD=app_password + +# Configuration de l'environnement +LOG_LEVEL=INFO +ENVIRONMENT=development + +# Ports +APP_PORT=8000 +MYSQL_PORT=3306 +PHPMYADMIN_PORT=8080 + +# Noms des conteneurs +APP_CONTAINER_NAME=tasks-app +MYSQL_CONTAINER_NAME=tasks-mysql +PHPMYADMIN_CONTAINER_NAME=tasks-phpmyadmin diff --git a/environments/dev/terraform.tfvars b/environments/dev/terraform.tfvars index 989ca99..4f47358 100644 --- a/environments/dev/terraform.tfvars +++ b/environments/dev/terraform.tfvars @@ -29,6 +29,35 @@ database_config = { db_tier = "db-f1-micro" db_version = "MYSQL_8_0" private_ip_prefix_len = 16 + import_global_address = true + import_sql_instance = false + import_secret = false + import_service_networking_connection = true +} + +# Configuration Artifact Registry +artifact_registry_config = { + repository_name = "tasks-app" + retention_days = 7 + cleanup_policies = [ + { + id = "delete-prerelease" + action = "DELETE" + condition = { + tag_state = "TAGGED" + tag_prefixes = ["dev-", "test-", "staging-"] + older_than = "604800s" # 7 jours + } + }, + { + id = "keep-production" + action = "KEEP" + condition = { + tag_state = "TAGGED" + tag_prefixes = ["v", "prod-", "main-"] + } + } + ] } # Configuration IAM diff --git a/environments/prd/terraform.tfvars b/environments/prd/terraform.tfvars index 376317e..ea4e04c 100644 --- a/environments/prd/terraform.tfvars +++ b/environments/prd/terraform.tfvars @@ -31,6 +31,31 @@ database_config = { private_ip_prefix_len = 16 } +# Configuration Artifact Registry +artifact_registry_config = { + repository_name = "tasks-app" + retention_days = 30 + cleanup_policies = [ + { + id = "delete-prerelease" + action = "DELETE" + condition = { + tag_state = "TAGGED" + tag_prefixes = ["dev-", "test-", "staging-"] + older_than = "2592000s" # 30 jours + } + }, + { + id = "keep-production" + action = "KEEP" + condition = { + tag_state = "TAGGED" + tag_prefixes = ["v", "prod-", "main-"] + } + } + ] +} + # Configuration IAM team_member_emails = [ "arnassalomlucas@gmail.com", diff --git a/helm/tasks-app/Chart.yaml b/helm/tasks-app/Chart.yaml new file mode 100644 index 0000000..e49a109 --- /dev/null +++ b/helm/tasks-app/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +name: tasks-app +description: A Helm chart for Tasks Application +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - tasks + - fastapi + - mysql +home: https://github.com/your-org/InfrastructureEquipe8 +sources: + - https://github.com/your-org/InfrastructureEquipe8 +maintainers: + - name: Your Team + email: team@yourcompany.com diff --git a/helm/tasks-app/templates/_helpers.tpl b/helm/tasks-app/templates/_helpers.tpl new file mode 100644 index 0000000..45733b5 --- /dev/null +++ b/helm/tasks-app/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "tasks-app.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +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 }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "tasks-app.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "tasks-app.labels" -}} +helm.sh/chart: {{ include "tasks-app.chart" . }} +{{ include "tasks-app.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "tasks-app.selectorLabels" -}} +app.kubernetes.io/name: {{ include "tasks-app.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "tasks-app.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "tasks-app.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm/tasks-app/templates/configmap.yaml b/helm/tasks-app/templates/configmap.yaml new file mode 100644 index 0000000..968e937 --- /dev/null +++ b/helm/tasks-app/templates/configmap.yaml @@ -0,0 +1,12 @@ +{{- if .Values.configMap.create }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "tasks-app.fullname" . }}-config + labels: + {{- include "tasks-app.labels" . | nindent 4 }} +data: + {{- range $key, $value := .Values.configMap.data }} + {{ $key }}: {{ $value | quote }} + {{- end }} +{{- end }} diff --git a/helm/tasks-app/templates/deployment.yaml b/helm/tasks-app/templates/deployment.yaml new file mode 100644 index 0000000..c51662d --- /dev/null +++ b/helm/tasks-app/templates/deployment.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "tasks-app.fullname" . }} + labels: + {{- include "tasks-app.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "tasks-app.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "tasks-app.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "tasks-app.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 8000 + protocol: TCP + env: + - name: DB_HOST + value: {{ .Values.env.DB_HOST | quote }} + - name: DB_PORT + value: {{ .Values.env.DB_PORT | quote }} + - name: DB_NAME + value: {{ .Values.env.DB_NAME | quote }} + - name: DB_USER + value: {{ .Values.env.DB_USER | quote }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "tasks-app.fullname" . }}-secret + key: db-password + {{- if .Values.configMap.create }} + {{- range $key, $value := .Values.configMap.data }} + - name: {{ $key }} + valueFrom: + configMapKeyRef: + name: {{ include "tasks-app.fullname" . }}-config + key: {{ $key }} + {{- end }} + {{- end }} + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/tasks-app/templates/hpa.yaml b/helm/tasks-app/templates/hpa.yaml new file mode 100644 index 0000000..4a989ce --- /dev/null +++ b/helm/tasks-app/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "tasks-app.fullname" . }} + labels: + {{- include "tasks-app.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "tasks-app.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/tasks-app/templates/ingress.yaml b/helm/tasks-app/templates/ingress.yaml new file mode 100644 index 0000000..8fe1d92 --- /dev/null +++ b/helm/tasks-app/templates/ingress.yaml @@ -0,0 +1,59 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "tasks-app.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class")) }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "tasks-app.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/tasks-app/templates/secret.yaml b/helm/tasks-app/templates/secret.yaml new file mode 100644 index 0000000..183c926 --- /dev/null +++ b/helm/tasks-app/templates/secret.yaml @@ -0,0 +1,15 @@ +{{- if .Values.secrets.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "tasks-app.fullname" . }}-secret + labels: + {{- include "tasks-app.labels" . | nindent 4 }} +type: Opaque +data: + {{- if .Values.secrets.dbPassword }} + db-password: {{ .Values.secrets.dbPassword | b64enc | quote }} + {{- else }} + db-password: {{ "changeme" | b64enc | quote }} + {{- end }} +{{- end }} diff --git a/helm/tasks-app/templates/service.yaml b/helm/tasks-app/templates/service.yaml new file mode 100644 index 0000000..f280596 --- /dev/null +++ b/helm/tasks-app/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "tasks-app.fullname" . }} + labels: + {{- include "tasks-app.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + name: http + selector: + {{- include "tasks-app.selectorLabels" . | nindent 4 }} diff --git a/helm/tasks-app/templates/serviceaccount.yaml b/helm/tasks-app/templates/serviceaccount.yaml new file mode 100644 index 0000000..3a48c0b --- /dev/null +++ b/helm/tasks-app/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "tasks-app.serviceAccountName" . }} + labels: + {{- include "tasks-app.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/helm/tasks-app/values-dev.yaml b/helm/tasks-app/values-dev.yaml new file mode 100644 index 0000000..65f9911 --- /dev/null +++ b/helm/tasks-app/values-dev.yaml @@ -0,0 +1,40 @@ +# Development environment values +replicaCount: 1 + +image: + repository: gcr.io/PROJECT_ID/tasks-app + tag: "dev-latest" + +resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 100m + memory: 128Mi + +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 + +env: + DB_HOST: "mysql-dev-service" + DB_PORT: "3306" + DB_NAME: "tasksdb_dev" + DB_USER: "app_user" + +configMap: + data: + LOG_LEVEL: "DEBUG" + ENVIRONMENT: "development" diff --git a/helm/tasks-app/values.yaml b/helm/tasks-app/values.yaml new file mode 100644 index 0000000..dec5cda --- /dev/null +++ b/helm/tasks-app/values.yaml @@ -0,0 +1,99 @@ +# Default values for tasks-app +replicaCount: 2 + +image: + repository: gcr.io/PROJECT_ID/tasks-app + pullPolicy: IfNotPresent + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + annotations: {} + name: "" + +podAnnotations: {} + +podSecurityContext: + fsGroup: 2000 + +securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + targetPort: 8000 + +ingress: + enabled: true + className: "nginx" + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: tasks-app.example.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: tasks-app-tls + hosts: + - tasks-app.example.com + +resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + +autoscaling: + enabled: false + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# Environment variables +env: + DB_HOST: "mysql-service" + DB_PORT: "3306" + DB_NAME: "tasksdb" + DB_USER: "app_user" + # DB_PASSWORD will be set via secret + +# Database configuration +database: + enabled: false # Set to true if you want to deploy MySQL in the same cluster + host: "mysql-service" + port: 3306 + name: "tasksdb" + user: "app_user" + +# Secrets +secrets: + create: true + dbPassword: "" + +# ConfigMap for non-sensitive config +configMap: + create: true + data: + LOG_LEVEL: "INFO" + ENVIRONMENT: "production" diff --git a/iam/billing_iam.tf b/iam/billing_iam.tf deleted file mode 100644 index 3ef51dd..0000000 --- a/iam/billing_iam.tf +++ /dev/null @@ -1,6 +0,0 @@ -resource "google_billing_account_iam_member" "instructor_billing_viewer" { - count = var.enable_instructor_binding ? 1 : 0 - billing_account_id = var.billing_account_id - role = "roles/billing.user" - member = "user:${var.instructor_email}" -} diff --git a/iam/envs/dev.tfvars b/iam/envs/dev.tfvars deleted file mode 100644 index 23830ed..0000000 --- a/iam/envs/dev.tfvars +++ /dev/null @@ -1,6 +0,0 @@ -project_id = "caramel-abacus-472612-h3" -region = "europe-west9" -team_member_emails = ["arnassalomlucas@gmail.com", "dylan.winter27@gmail.com", "mathias.ballot974@gmail.com", "tdesalmand@gmail.com"] -team_role = "roles/editor" -instructor_email = "jeremie@jjaouen.com" -instructor_role = "roles/viewer" diff --git a/iam/envs/prd.tfvars b/iam/envs/prd.tfvars deleted file mode 100644 index 2ea4a25..0000000 --- a/iam/envs/prd.tfvars +++ /dev/null @@ -1,6 +0,0 @@ -project_id = "epitech-vpc-demo-69" -region = "europe-west9" -team_member_emails = ["arnassalomlucas@gmail.com", "dylan.winter27@gmail.com", "mathias.ballot974@gmail.com", "tdesalmand@gmail.com"] -team_role = "roles/editor" -instructor_email = "jeremie@jjaouen.com" -instructor_role = "roles/viewer" diff --git a/iam/invite.tf b/iam/invite.tf deleted file mode 100644 index bdd841b..0000000 --- a/iam/invite.tf +++ /dev/null @@ -1,19 +0,0 @@ -locals { - unique_team_member_emails = distinct(var.team_member_emails) -} - -resource "google_project_iam_member" "team_members" { - for_each = var.auto_invite_missing_users ? { - for email in local.unique_team_member_emails : email => email - } : {} - project = var.project_id - role = var.team_role - member = "user:${each.value}" -} - -resource "google_project_iam_member" "instructor" { - count = var.enable_instructor_binding ? 1 : 0 - project = var.project_id - role = var.instructor_role - member = "user:${var.instructor_email}" -} diff --git a/iam/main.tf b/iam/main.tf deleted file mode 100644 index 5c30040..0000000 --- a/iam/main.tf +++ /dev/null @@ -1,15 +0,0 @@ -terraform { - required_version = ">= 1.5.0" - - required_providers { - google = { - source = "hashicorp/google" - version = ">= 5.0" - } - } - - # Remote state backend (GCS). - # Bucket and prefix are provided by the deploy/destroy scripts using -backend-config - # for each environment/workspace (see configs/dev.config and configs/prd.config). - backend "gcs" {} -} diff --git a/iam/members.tf b/iam/members.tf deleted file mode 100644 index 4786c65..0000000 --- a/iam/members.tf +++ /dev/null @@ -1,10 +0,0 @@ -output "iam_bindings_summary" { - value = { - project = var.project_id - team = var.team_member_emails - team_role = var.team_role - instructor = var.enable_instructor_binding ? var.instructor_email : null - billing_account_id = var.billing_account_id - instructor_billing_viewer = var.enable_instructor_binding - } -} diff --git a/iam/provider.tf b/iam/provider.tf deleted file mode 100644 index 6afd575..0000000 --- a/iam/provider.tf +++ /dev/null @@ -1,4 +0,0 @@ -provider "google" { - project = var.project_id - region = var.region -} \ No newline at end of file diff --git a/iam/variables.tf b/iam/variables.tf deleted file mode 100644 index a7cba94..0000000 --- a/iam/variables.tf +++ /dev/null @@ -1,57 +0,0 @@ -variable "project_id" { - description = "GCP project id" - type = string -} - -variable "region" { - description = "GCP region" - type = string -} - -variable "team_member_emails" { - description = "List of team member emails" - type = list(string) - validation { - condition = length(var.team_member_emails) > 0 - error_message = "Provide at least one team member email." - } -} - -variable "team_role" { - description = "IAM role granted to team members" - type = string - default = "roles/editor" -} - -variable "instructor_email" { - description = "Instructor email" - type = string - validation { - condition = can(regex("^[_A-Za-z0-9-+.]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$", var.instructor_email)) - error_message = "Provide a valid instructor email address." - } -} - -variable "instructor_role" { - description = "Least-privilege role for instructor (viewer by default)" - type = string - default = "roles/viewer" -} - -variable "enable_instructor_binding" { - description = "Whether to create instructor IAM binding" - type = bool - default = true -} - -variable "auto_invite_missing_users" { - description = "If true, ensure IAM membership for all emails (acts as invite if not yet provisioned)." - type = bool - default = true -} - -variable "billing_account_id" { - description = "Billing account ID for IAM bindings" - type = string - default = "0100E9-D328A7-35D6BE" -} diff --git a/kubernetes/README.md b/kubernetes/README.md deleted file mode 100644 index e2d0cee..0000000 --- a/kubernetes/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Module Kubernetes - Infrastructure GKE - -Ce module Terraform dĂ©ploie uniquement l'infrastructure Kubernetes (cluster GKE) sans application. - -## đŸ—ïž **Ce que ce module fait :** - -- **Cluster GKE** : Cluster Kubernetes gĂ©rĂ© avec autoscaling -- **Node Pools** : Pools de nƓuds avec auto-repair et auto-upgrade -- **Configuration rĂ©seau** : IntĂ©gration avec le VPC existant -- **Permissions IAM** : GĂ©rĂ©es par le module IAM - -## 📁 **Structure :** - -``` -kubernetes/ -├── main.tf # Configuration du cluster GKE -├── variables.tf # Variables du module -├── outputs.tf # Outputs du module -└── load_test.sh # Script de test de charge -``` - -## 🚀 **DĂ©ploiement :** - -Ce module est dĂ©ployĂ© automatiquement via l'architecture modulaire : - -```bash -# DĂ©ploiement complet (network + iam + kubernetes) -./deploy-modular.sh dev -./deploy-modular.sh prd -``` - -## đŸ“± **Application :** - -L'application Task Manager API est dĂ©ployĂ©e sĂ©parĂ©ment depuis le dossier `app/` : - -```bash -# DĂ©ploiement de l'application -./app/deploy-app.sh dev YOUR_PROJECT_ID -./app/deploy-app.sh prd YOUR_PROJECT_ID -``` - -## 🔧 **Configuration :** - -Les variables sont configurĂ©es dans : -- `environments/dev/terraform.tfvars` -- `environments/prd/terraform.tfvars` - -## 📊 **Monitoring :** - -```bash -# VĂ©rifier le cluster -gcloud container clusters get-credentials gke-cluster-dev --region europe-west9 --project YOUR_PROJECT_ID -kubectl get nodes -kubectl get pods --all-namespaces -``` - -## 🎯 **RĂ©sultat :** - -Ce module fournit uniquement l'infrastructure Kubernetes. L'application est dĂ©ployĂ©e sĂ©parĂ©ment pour une meilleure sĂ©paration des responsabilitĂ©s. diff --git a/kubernetes/load_test.sh b/kubernetes/load_test.sh deleted file mode 100644 index 25ce9e5..0000000 --- a/kubernetes/load_test.sh +++ /dev/null @@ -1,266 +0,0 @@ -#!/bin/bash - -# ============================================================================= -# SCRIPT DE LOAD TESTING POUR LA DÉFENSE DU PROJET -# Fichier : load_test.sh -# -# Ce script gĂ©nĂšre de la charge sur votre application Task Manager pour -# dĂ©montrer le scaling horizontal (HPA + Cluster Autoscaler) selon les -# exigences du Cours 7. -# -# Utilisation : -# chmod +x load_test.sh -# ./load_test.sh -# -# Exemple : -# ./load_test.sh 34.155.123.45 -# ============================================================================= - -set -e - -# Couleurs pour l'affichage -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Configuration -LOAD_BALANCER_IP=${1:-""} -CONCURRENT_REQUESTS=50 -DURATION=300 # 5 minutes -REQUEST_RATE=10 # RequĂȘtes par seconde - -# Fonction d'affichage -print_header() { - echo -e "${BLUE}========================================${NC}" - echo -e "${BLUE}$1${NC}" - echo -e "${BLUE}========================================${NC}" -} - -print_success() { - echo -e "${GREEN}✅ $1${NC}" -} - -print_error() { - echo -e "${RED}❌ $1${NC}" -} - -print_warning() { - echo -e "${YELLOW}⚠ $1${NC}" -} - -print_info() { - echo -e "${BLUE}â„č $1${NC}" -} - -# VĂ©rification des prĂ©requis -check_prerequisites() { - print_header "VĂ©rification des prĂ©requis" - - # VĂ©rifier kubectl - if ! command -v kubectl &> /dev/null; then - print_error "kubectl n'est pas installĂ©" - exit 1 - fi - print_success "kubectl installĂ©" - - # VĂ©rifier l'accĂšs au cluster - if ! kubectl cluster-info &> /dev/null; then - print_error "Impossible de se connecter au cluster Kubernetes" - print_info "ExĂ©cutez : gcloud container clusters get-credentials --region " - exit 1 - fi - print_success "AccĂšs au cluster Kubernetes OK" - - # VĂ©rifier si l'IP du Load Balancer est fournie - if [ -z "$LOAD_BALANCER_IP" ]; then - print_warning "IP du Load Balancer non fournie" - print_info "RĂ©cupĂ©ration automatique de l'IP..." - - # Essayer de rĂ©cupĂ©rer l'IP du service - LOAD_BALANCER_IP=$(kubectl get svc task-manager-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "") - - if [ -z "$LOAD_BALANCER_IP" ]; then - print_error "Impossible de rĂ©cupĂ©rer l'IP du Load Balancer" - print_info "Utilisage : $0 " - print_info "Ou exĂ©cutez : kubectl get svc task-manager-service" - exit 1 - fi - fi - - print_success "Load Balancer IP : $LOAD_BALANCER_IP" - echo "" -} - -# Afficher l'Ă©tat initial -show_initial_state() { - print_header "État initial du cluster" - - echo -e "${YELLOW}Pods:${NC}" - kubectl get pods -l app=task-manager - echo "" - - echo -e "${YELLOW}HPA:${NC}" - kubectl get hpa task-manager-hpa - echo "" - - echo -e "${YELLOW}NƓuds:${NC}" - kubectl get nodes - echo "" -} - -# Lancer les observateurs en arriĂšre-plan -start_watchers() { - print_header "Lancement des observateurs" - - # CrĂ©er un rĂ©pertoire pour les logs - LOG_DIR="./load_test_logs_$(date +%Y%m%d_%H%M%S)" - mkdir -p "$LOG_DIR" - - print_info "Les logs seront enregistrĂ©s dans : $LOG_DIR" - - # Observer les pods - kubectl get pods -l app=task-manager -w > "$LOG_DIR/pods.log" 2>&1 & - PODS_PID=$! - print_success "Observateur de pods dĂ©marrĂ© (PID: $PODS_PID)" - - # Observer le HPA - kubectl get hpa task-manager-hpa -w > "$LOG_DIR/hpa.log" 2>&1 & - HPA_PID=$! - print_success "Observateur HPA dĂ©marrĂ© (PID: $HPA_PID)" - - # Observer les nƓuds - kubectl get nodes -w > "$LOG_DIR/nodes.log" 2>&1 & - NODES_PID=$! - print_success "Observateur de nƓuds dĂ©marrĂ© (PID: $NODES_PID)" - - echo "" -} - -# GĂ©nĂ©rer la charge -generate_load() { - print_header "GĂ©nĂ©ration de charge" - - print_warning "GĂ©nĂ©ration de $CONCURRENT_REQUESTS requĂȘtes concurrentes pendant $DURATION secondes" - print_info "URL cible : http://$LOAD_BALANCER_IP" - print_info "Appuyez sur Ctrl+C pour arrĂȘter" - echo "" - - # Utiliser Apache Bench si disponible - if command -v ab &> /dev/null; then - print_info "Utilisation d'Apache Bench (ab)" - ab -n $((DURATION * REQUEST_RATE)) -c $CONCURRENT_REQUESTS -t $DURATION "http://$LOAD_BALANCER_IP/" 2>&1 | tee "$LOG_DIR/load_test.log" - # Sinon utiliser curl en boucle - else - print_info "Apache Bench non disponible, utilisation de curl" - print_warning "Pour de meilleurs rĂ©sultats, installez Apache Bench : apt-get install apache2-utils" - - END_TIME=$(($(date +%s) + DURATION)) - REQUEST_COUNT=0 - - while [ $(date +%s) -lt $END_TIME ]; do - for i in $(seq 1 $CONCURRENT_REQUESTS); do - curl -s -o /dev/null -w "%{http_code}\n" "http://$LOAD_BALANCER_IP/" >> "$LOG_DIR/load_test.log" 2>&1 & - done - REQUEST_COUNT=$((REQUEST_COUNT + CONCURRENT_REQUESTS)) - echo -ne "\rRequĂȘtes envoyĂ©es : $REQUEST_COUNT" - sleep 1 - done - echo "" - fi - - print_success "GĂ©nĂ©ration de charge terminĂ©e" - echo "" -} - -# Afficher l'Ă©tat final -show_final_state() { - print_header "État final du cluster" - - print_info "Attente de 10 secondes pour la stabilisation..." - sleep 10 - - echo -e "${YELLOW}Pods:${NC}" - kubectl get pods -l app=task-manager - echo "" - - echo -e "${YELLOW}HPA:${NC}" - kubectl get hpa task-manager-hpa - echo "" - - echo -e "${YELLOW}NƓuds:${NC}" - kubectl get nodes - echo "" -} - -# Nettoyer les processus en arriĂšre-plan -cleanup() { - print_header "Nettoyage" - - if [ ! -z "$PODS_PID" ]; then - kill $PODS_PID 2>/dev/null || true - print_success "Observateur de pods arrĂȘtĂ©" - fi - - if [ ! -z "$HPA_PID" ]; then - kill $HPA_PID 2>/dev/null || true - print_success "Observateur HPA arrĂȘtĂ©" - fi - - if [ ! -z "$NODES_PID" ]; then - kill $NODES_PID 2>/dev/null || true - print_success "Observateur de nƓuds arrĂȘtĂ©" - fi - - # Tuer tous les processus curl en arriĂšre-plan - pkill -P $$ curl 2>/dev/null || true -} - -# Afficher le rapport -show_report() { - print_header "Rapport de Load Testing" - - echo -e "${YELLOW}Fichiers de log gĂ©nĂ©rĂ©s :${NC}" - ls -lh "$LOG_DIR" - echo "" - - echo -e "${YELLOW}Nombre de pods avant/aprĂšs :${NC}" - INITIAL_PODS=$(head -n 2 "$LOG_DIR/pods.log" | tail -n 1 | wc -l) - FINAL_PODS=$(tail -n 1 "$LOG_DIR/pods.log" | wc -l) - echo "Initial: ~$INITIAL_PODS | Final: ~$FINAL_PODS" - echo "" - - print_success "Test de load terminĂ© avec succĂšs !" - print_info "Pour votre dĂ©fense, montrez :" - echo " 1. L'augmentation du nombre de pods (HPA)" - echo " 2. L'ajout de nouveaux nƓuds (Cluster Autoscaler)" - echo " 3. Les logs dans $LOG_DIR" - echo " 4. Les mĂ©triques CPU/mĂ©moire avec : kubectl top pods" - echo "" -} - -# Gestionnaire de signaux pour nettoyer proprement -trap cleanup EXIT INT TERM - -# Programme principal -main() { - clear - print_header "LOAD TESTING - KUBERNETES CLUSTER" - echo "" - - check_prerequisites - show_initial_state - start_watchers - - sleep 3 # Laisser les watchers se stabiliser - - generate_load - show_final_state - show_report -} - -# ExĂ©cuter le programme principal -main - - diff --git a/kubernetes/main.tf b/kubernetes/main.tf deleted file mode 100644 index 1041675..0000000 --- a/kubernetes/main.tf +++ /dev/null @@ -1,214 +0,0 @@ -terraform { - required_version = ">= 1.5.0" - - required_providers { - google = { - source = "hashicorp/google" - version = ">= 5.0" - } - } - - backend "gcs" {} -} - -# Variables nĂ©cessaires pour le module Kubernetes -variable "project_id" { - description = "ID du projet GCP" - type = string -} - -variable "region" { - description = "RĂ©gion GCP" - type = string - default = "europe-west9" -} - -variable "environment" { - description = "Environnement (dev ou prd)" - type = string - validation { - condition = contains(["dev", "prd"], var.environment) - error_message = "L'environnement doit ĂȘtre 'dev' ou 'prd'." - } -} - -variable "network_name" { - description = "Nom du rĂ©seau VPC" - type = string -} - -variable "subnet_name" { - description = "Nom du sous-rĂ©seau" - type = string -} - -variable "cluster_name" { - description = "Nom du cluster Kubernetes" - type = string - default = "gke-cluster" -} - -variable "gke_num_nodes" { - description = "Nombre de nƓuds par zone dans le cluster" - type = number - default = 1 -} - -variable "machine_type" { - description = "Type de machine pour les nƓuds" - type = string - default = "e2-medium" -} - -variable "kubernetes_version" { - description = "Version de Kubernetes Ă  utiliser" - type = string - default = "1.27" -} - -variable "node_zones" { - description = "Liste des zones pour les nƓuds du cluster" - type = list(string) - default = ["europe-west9-a", "europe-west9-b", "europe-west9-c"] -} - -variable "user_email" { - description = "Email de l'utilisateur pour les permissions IAM" - type = string -} - -# RĂ©fĂ©rence au rĂ©seau VPC existant -data "google_compute_network" "main" { - name = var.network_name - project = var.project_id -} - -data "google_compute_subnetwork" "main" { - name = var.subnet_name - region = var.region - project = var.project_id -} - -# Cluster GKE -resource "google_container_cluster" "primary" { - name = "${var.cluster_name}-${var.environment}" - location = var.region - project = var.project_id - - # Utilise le rĂ©seau VPC existant - network = data.google_compute_network.main.name - subnetwork = data.google_compute_subnetwork.main.name - - # Configuration rĂ©seau avec plages secondaires - ip_allocation_policy { - stack_type = "IPV4_IPV6" - services_secondary_range_name = "services-range" - cluster_secondary_range_name = "pod-ranges" - } - - # Configuration de la version Kubernetes - min_master_version = var.kubernetes_version - - # Suppression du node pool par dĂ©faut - remove_default_node_pool = true - initial_node_count = 1 - - # Configuration du plan de contrĂŽle - private_cluster_config { - enable_private_nodes = true - enable_private_endpoint = false - master_ipv4_cidr_block = "172.16.0.0/28" - } - - # ContrĂŽle d'accĂšs au plan de contrĂŽle - master_authorized_networks_config { - cidr_blocks { - cidr_block = "0.0.0.0/0" - display_name = "public-access" - } - } - - # Configuration de maintenance - maintenance_policy { - recurring_window { - start_time = "2024-01-01T02:00:00Z" - end_time = "2024-01-01T06:00:00Z" - recurrence = "FREQ=WEEKLY;BYDAY=SU" - } - } - - # Protection contre la suppression accidentelle - deletion_protection = false -} - -# Node pool pour le cluster -resource "google_container_node_pool" "primary_nodes" { - name = "${google_container_cluster.primary.name}-node-pool" - location = var.region - cluster = google_container_cluster.primary.name - project = var.project_id - - # Distribution des nƓuds par zone - node_locations = var.node_zones - - # Cluster Autoscaler - autoscaling { - min_node_count = 1 - max_node_count = 5 - } - - # Configuration de gestion des nƓuds - management { - auto_repair = true - auto_upgrade = true - } - - # Configuration des nƓuds - node_config { - machine_type = var.machine_type - - # Labels Kubernetes - labels = { - env = var.environment - pool = "application-pool" - } - - # MĂ©tadonnĂ©es de sĂ©curitĂ© - metadata = { - disable-legacy-endpoints = "true" - } - - # Scopes OAuth pour les nƓuds - oauth_scopes = [ - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/monitoring", - "https://www.googleapis.com/auth/devstorage.read_only", - "https://www.googleapis.com/auth/cloud-platform" - ] - } - - # Configuration de mise Ă  l'Ă©chelle progressive - upgrade_settings { - max_surge = 1 - max_unavailable = 0 - } -} - -# Permissions IAM pour Kubernetes -resource "google_project_iam_member" "container_admin" { - project = var.project_id - role = "roles/container.admin" - member = "user:${var.user_email}" -} - -resource "google_project_iam_member" "compute_network_admin" { - project = var.project_id - role = "roles/compute.networkAdmin" - member = "user:${var.user_email}" -} - -resource "google_project_iam_member" "service_account_user" { - project = var.project_id - role = "roles/iam.serviceAccountUser" - member = "user:${var.user_email}" -} diff --git a/kubernetes/outputs.tf b/kubernetes/outputs.tf deleted file mode 100644 index fb62279..0000000 --- a/kubernetes/outputs.tf +++ /dev/null @@ -1,31 +0,0 @@ -output "kubernetes_cluster_name" { - value = google_container_cluster.primary.name - description = "Nom du cluster GKE" -} - -output "kubernetes_cluster_host" { - value = "https://${google_container_cluster.primary.endpoint}" - description = "Point d'entrĂ©e de l'API Kubernetes" -} - -output "kubernetes_location" { - value = google_container_cluster.primary.location - description = "RĂ©gion/zone du cluster GKE" -} - -output "get_credentials_command" { - value = "gcloud container clusters get-credentials ${google_container_cluster.primary.name} --region ${var.region} --project ${var.project_id}" - description = "Commande pour configurer kubectl avec ce cluster" -} - -output "cluster_ca_certificate" { - value = google_container_cluster.primary.master_auth[0].cluster_ca_certificate - description = "Certificat CA du cluster" - sensitive = true -} - -output "cluster_endpoint" { - value = google_container_cluster.primary.endpoint - description = "Endpoint du cluster Kubernetes" - sensitive = true -} diff --git a/kubernetes/variables.tf b/kubernetes/variables.tf deleted file mode 100644 index 4e03123..0000000 --- a/kubernetes/variables.tf +++ /dev/null @@ -1,64 +0,0 @@ -variable "project_id" { - description = "ID du projet GCP" - type = string -} - -variable "region" { - description = "RĂ©gion GCP" - type = string - default = "europe-west9" -} - -variable "environment" { - description = "Environnement (dev ou prd)" - type = string - validation { - condition = contains(["dev", "prd"], var.environment) - error_message = "L'environnement doit ĂȘtre 'dev' ou 'prd'." - } -} - -variable "network_name" { - description = "Nom du rĂ©seau VPC" - type = string -} - -variable "subnet_name" { - description = "Nom du sous-rĂ©seau" - type = string -} - -variable "cluster_name" { - description = "Nom du cluster Kubernetes" - type = string - default = "gke-cluster" -} - -variable "gke_num_nodes" { - description = "Nombre de nƓuds par zone dans le cluster" - type = number - default = 1 -} - -variable "machine_type" { - description = "Type de machine pour les nƓuds" - type = string - default = "e2-medium" -} - -variable "kubernetes_version" { - description = "Version de Kubernetes Ă  utiliser" - type = string - default = "1.27" -} - -variable "node_zones" { - description = "Liste des zones pour les nƓuds du cluster" - type = list(string) - default = ["europe-west9-a", "europe-west9-b", "europe-west9-c"] -} - -variable "user_email" { - description = "Email de l'utilisateur pour les permissions IAM" - type = string -} diff --git a/scripts/check-registry.sh b/scripts/check-registry.sh new file mode 100644 index 0000000..541b9f1 --- /dev/null +++ b/scripts/check-registry.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +# Script pour vĂ©rifier l'existence des registries et Ă©viter d'en crĂ©er par hasard + +set -e + +# Couleurs pour les messages +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${GREEN}🔍 VĂ©rification des registries existants${NC}" + +# Obtenir le projet actuel +PROJECT_ID=$(gcloud config get-value project) +if [ -z "$PROJECT_ID" ]; then + echo -e "${RED}❌ Aucun projet Google Cloud configurĂ©.${NC}" + echo "Commande: gcloud config set project YOUR_PROJECT_ID" + exit 1 +fi + +echo -e "${YELLOW}📋 Projet: ${PROJECT_ID}${NC}" + +# VĂ©rifier Google Container Registry (GCR) +echo -e "${YELLOW}🔍 VĂ©rification de Google Container Registry (GCR)...${NC}" +GCR_BUCKETS=$(gcloud storage buckets list --filter="name:artifacts.${PROJECT_ID}.appspot.com" --format="value(name)" 2>/dev/null || echo "") + +if [ -n "$GCR_BUCKETS" ]; then + echo -e "${GREEN}✅ GCR bucket trouvĂ©: ${GCR_BUCKETS}${NC}" +else + echo -e "${YELLOW}⚠ Aucun bucket GCR trouvĂ©.${NC}" + echo "Le bucket GCR sera créé automatiquement lors du premier push d'image." +fi + +# VĂ©rifier Artifact Registry +echo -e "${YELLOW}🔍 VĂ©rification d'Artifact Registry...${NC}" +ARTIFACT_REGISTRIES=$(gcloud artifacts repositories list --format="value(name)" --project=$PROJECT_ID 2>/dev/null || echo "") + +if [ -n "$ARTIFACT_REGISTRIES" ]; then + echo -e "${GREEN}✅ Artifact Registries trouvĂ©s:${NC}" + echo "$ARTIFACT_REGISTRIES" +else + echo -e "${YELLOW}⚠ Aucun Artifact Registry trouvĂ©.${NC}" + echo "Vous pouvez en crĂ©er un si nĂ©cessaire avec:" + echo "gcloud artifacts repositories create REPO_NAME --repository-format=docker --location=LOCATION" +fi + +# VĂ©rifier les permissions du service account +echo -e "${YELLOW}🔍 VĂ©rification des permissions du service account...${NC}" + +# Obtenir le service account GitHub Actions +SA_EMAIL=$(gcloud iam service-accounts list --filter="displayName:GitHub Terraform" --format="value(email)" --project=$PROJECT_ID) + +if [ -z "$SA_EMAIL" ]; then + echo -e "${RED}❌ Service account GitHub Actions non trouvĂ©.${NC}" + echo "Assurez-vous que le module bootstrap-wif/ a Ă©tĂ© dĂ©ployĂ©." + exit 1 +fi + +echo -e "${GREEN}✅ Service account trouvĂ©: ${SA_EMAIL}${NC}" + +# VĂ©rifier les permissions IAM +echo -e "${YELLOW}🔍 VĂ©rification des permissions IAM...${NC}" + +# VĂ©rifier les permissions sur le projet +IAM_POLICY=$(gcloud projects get-iam-policy $PROJECT_ID --format="json" 2>/dev/null || echo "{}") + +# VĂ©rifier les permissions spĂ©cifiques +STORAGE_ADMIN=$(echo "$IAM_POLICY" | jq -r ".bindings[] | select(.role == \"roles/storage.admin\") | .members[] | select(. == \"serviceAccount:${SA_EMAIL}\")" 2>/dev/null || echo "") +CONTAINER_ADMIN=$(echo "$IAM_POLICY" | jq -r ".bindings[] | select(.role == \"roles/container.admin\") | .members[] | select(. == \"serviceAccount:${SA_EMAIL}\")" 2>/dev/null || echo "") +ARTIFACT_ADMIN=$(echo "$IAM_POLICY" | jq -r ".bindings[] | select(.role == \"roles/artifactregistry.admin\") | .members[] | select(. == \"serviceAccount:${SA_EMAIL}\")" 2>/dev/null || echo "") + +if [ -n "$STORAGE_ADMIN" ]; then + echo -e "${GREEN}✅ Permission storage.admin accordĂ©e${NC}" +else + echo -e "${RED}❌ Permission storage.admin manquante${NC}" +fi + +if [ -n "$CONTAINER_ADMIN" ]; then + echo -e "${GREEN}✅ Permission container.admin accordĂ©e${NC}" +else + echo -e "${RED}❌ Permission container.admin manquante${NC}" +fi + +if [ -n "$ARTIFACT_ADMIN" ]; then + echo -e "${GREEN}✅ Permission artifactregistry.admin accordĂ©e${NC}" +else + echo -e "${YELLOW}⚠ Permission artifactregistry.admin manquante${NC}" + echo "Cette permission sera ajoutĂ©e lors du prochain dĂ©ploiement Terraform." +fi + +echo "" +echo -e "${BLUE}📋 RĂ©sumĂ©:${NC}" +echo " - Projet: $PROJECT_ID" +echo " - Service Account: $SA_EMAIL" +echo " - GCR Bucket: ${GCR_BUCKETS:-'Sera créé automatiquement'}" +echo " - Artifact Registries: ${ARTIFACT_REGISTRIES:-'Aucun'}" +echo "" + +if [ -n "$STORAGE_ADMIN" ] && [ -n "$CONTAINER_ADMIN" ]; then + echo -e "${GREEN}✅ Les permissions de base sont configurĂ©es.${NC}" + echo -e "${YELLOW}💡 Vous pouvez maintenant tester le push d'images.${NC}" +else + echo -e "${RED}❌ Des permissions manquent.${NC}" + echo -e "${YELLOW}💡 RedĂ©ployez le module bootstrap-wif/ avec: terraform apply${NC}" +fi diff --git a/scripts/configure-artifact-registry.sh b/scripts/configure-artifact-registry.sh new file mode 100644 index 0000000..d9ec44a --- /dev/null +++ b/scripts/configure-artifact-registry.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# Script pour configurer Artifact Registry et les variables GitHub + +set -e + +# Couleurs pour les messages +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${GREEN}🔧 Configuration d'Artifact Registry${NC}" + +# VĂ©rifier que nous sommes dans le bon rĂ©pertoire +if [ ! -d "terraform" ]; then + echo -e "${RED}❌ Le rĂ©pertoire terraform/ n'existe pas.${NC}" + echo "Assurez-vous d'ĂȘtre dans le rĂ©pertoire racine du projet." + exit 1 +fi + +# Aller dans le rĂ©pertoire terraform +cd terraform + +# VĂ©rifier que Terraform est initialisĂ© +if [ ! -d ".terraform" ]; then + echo -e "${YELLOW}⚠ Terraform n'est pas initialisĂ©.${NC}" + echo -e "${YELLOW}🔧 Initialisation de Terraform...${NC}" + terraform init +fi + +# Obtenir les informations d'Artifact Registry +echo -e "${YELLOW}🔍 RĂ©cupĂ©ration des informations d'Artifact Registry...${NC}" + +ARTIFACT_REGISTRY_URL=$(terraform output -raw artifact_registry_info 2>/dev/null | jq -r '.repository_url' 2>/dev/null || echo "") +ARTIFACT_REGISTRY_ID=$(terraform output -raw artifact_registry_info 2>/dev/null | jq -r '.repository_id' 2>/dev/null || echo "") +WRITER_SA=$(terraform output -raw artifact_registry_info 2>/dev/null | jq -r '.writer_service_account' 2>/dev/null || echo "") + +if [ -z "$ARTIFACT_REGISTRY_URL" ]; then + echo -e "${RED}❌ Impossible de rĂ©cupĂ©rer les informations d'Artifact Registry.${NC}" + echo "Assurez-vous que le module artifact-registry a Ă©tĂ© dĂ©ployĂ© avec succĂšs." + echo "ExĂ©cutez: terraform apply" + exit 1 +fi + +echo -e "${GREEN}✅ Informations d'Artifact Registry rĂ©cupĂ©rĂ©es !${NC}" +echo " - URL: $ARTIFACT_REGISTRY_URL" +echo " - ID: $ARTIFACT_REGISTRY_ID" +echo " - Writer SA: $WRITER_SA" + +# Retourner au rĂ©pertoire parent +cd .. + +# VĂ©rifier que gh CLI est installĂ© +if ! command -v gh &> /dev/null; then + echo -e "${RED}❌ GitHub CLI (gh) n'est pas installĂ©. Veuillez l'installer d'abord.${NC}" + echo "Installation: https://cli.github.com/" + exit 1 +fi + +# VĂ©rifier que l'utilisateur est connectĂ© Ă  GitHub +if ! gh auth status &> /dev/null; then + echo -e "${RED}❌ Vous n'ĂȘtes pas connectĂ© Ă  GitHub CLI. Veuillez vous connecter d'abord.${NC}" + echo "Commande: gh auth login" + exit 1 +fi + +# Configuration des variables GitHub +echo -e "${YELLOW}🔧 Configuration des variables GitHub...${NC}" + +# Variables pour l'environnement de dĂ©veloppement +echo -e "${BLUE}📝 Configuration de l'environnement 'Develop'...${NC}" +gh variable set ARTIFACT_REGISTRY_URL --body="$ARTIFACT_REGISTRY_URL" --env=Develop +gh variable set REGISTRY --body="$ARTIFACT_REGISTRY_URL" --env=Develop +gh variable set IMAGE_NAME --body="tasks-app" --env=Develop +gh variable set INSTANCE_NAME --body="tasks-mysql" --env=Develop + +# Variables pour l'environnement de production +echo -e "${BLUE}📝 Configuration de l'environnement 'Production'...${NC}" +gh variable set ARTIFACT_REGISTRY_URL --body="$ARTIFACT_REGISTRY_URL" --env=Production +gh variable set REGISTRY --body="$ARTIFACT_REGISTRY_URL" --env=Production +gh variable set IMAGE_NAME --body="tasks-app" --env=Production +gh variable set INSTANCE_NAME --body="tasks-mysql" --env=Production + +echo -e "${GREEN}✅ Configuration terminĂ©e avec succĂšs !${NC}" +echo "" +echo -e "${BLUE}📋 RĂ©sumĂ© de la configuration:${NC}" +echo " - Artifact Registry URL: $ARTIFACT_REGISTRY_URL" +echo " - Repository ID: $ARTIFACT_REGISTRY_ID" +echo " - Writer Service Account: $WRITER_SA" +echo "" +echo -e "${YELLOW}💡 Les workflows GitHub Actions utiliseront maintenant Artifact Registry.${NC}" +echo -e "${YELLOW}💡 Vous pouvez maintenant pousser du code pour dĂ©clencher les workflows.${NC}" + +# Afficher les commandes Docker pour tester +echo "" +echo -e "${BLUE}🐳 Commandes Docker pour tester:${NC}" +echo " # Authentification" +echo " gcloud auth configure-docker $ARTIFACT_REGISTRY_URL" +echo "" +echo " # Build et push d'une image de test" +echo " docker build -t $ARTIFACT_REGISTRY_URL/test:latest ./app" +echo " docker push $ARTIFACT_REGISTRY_URL/test:latest" +echo "" +echo -e "${GREEN}🎉 Artifact Registry est maintenant configurĂ© !${NC}" diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 0000000..48d7b94 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,148 @@ +#!/bin/bash + +# Script de dĂ©ploiement avec Helm +set -e + +# Configuration +NAMESPACE=${1:-tasks-app} +ENVIRONMENT=${2:-dev} +IMAGE_TAG=${3:-latest} +CHART_PATH="./helm/tasks-app" +VALUES_FILE="values-${ENVIRONMENT}.yaml" + +# Couleurs pour les logs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# VĂ©rifier les prĂ©requis +check_prerequisites() { + log "VĂ©rification des prĂ©requis..." + + if ! command -v kubectl &> /dev/null; then + error "kubectl n'est pas installĂ©" + exit 1 + fi + + if ! command -v helm &> /dev/null; then + error "helm n'est pas installĂ©" + exit 1 + fi + + if ! kubectl cluster-info &> /dev/null; then + error "Impossible de se connecter au cluster Kubernetes" + exit 1 + fi + + log "PrĂ©requis OK" +} + +# CrĂ©er le namespace si nĂ©cessaire +create_namespace() { + log "CrĂ©ation du namespace ${NAMESPACE}..." + kubectl create namespace ${NAMESPACE} --dry-run=client -o yaml | kubectl apply -f - +} + +# DĂ©ployer avec Helm +deploy_with_helm() { + log "DĂ©ploiement avec Helm..." + + local release_name="tasks-app-${ENVIRONMENT}" + local values_file="${CHART_PATH}/${VALUES_FILE}" + + if [ ! -f "${values_file}" ]; then + warn "Fichier de valeurs ${values_file} non trouvĂ©, utilisation des valeurs par dĂ©faut" + values_file="${CHART_PATH}/values.yaml" + fi + + log "Release: ${release_name}" + log "Namespace: ${NAMESPACE}" + log "Image tag: ${IMAGE_TAG}" + log "Values file: ${values_file}" + + helm upgrade --install ${release_name} ${CHART_PATH} \ + --namespace ${NAMESPACE} \ + --create-namespace \ + --values ${values_file} \ + --set image.tag=${IMAGE_TAG} \ + --wait \ + --timeout=10m + + log "DĂ©ploiement terminĂ©" +} + +# VĂ©rifier le dĂ©ploiement +verify_deployment() { + log "VĂ©rification du dĂ©ploiement..." + + # Attendre que les pods soient prĂȘts + kubectl wait --for=condition=available --timeout=300s deployment/tasks-app-${ENVIRONMENT} -n ${NAMESPACE} || { + error "Le dĂ©ploiement n'est pas prĂȘt dans les temps" + kubectl get pods -n ${NAMESPACE} + exit 1 + } + + # Afficher les ressources + log "Pods:" + kubectl get pods -n ${NAMESPACE} + + log "Services:" + kubectl get services -n ${NAMESPACE} + + log "Ingress:" + kubectl get ingress -n ${NAMESPACE} + + log "DĂ©ploiement vĂ©rifiĂ© avec succĂšs" +} + +# Fonction principale +main() { + log "DĂ©marrage du dĂ©ploiement..." + log "Environnement: ${ENVIRONMENT}" + log "Namespace: ${NAMESPACE}" + log "Image tag: ${IMAGE_TAG}" + + check_prerequisites + create_namespace + deploy_with_helm + verify_deployment + + log "DĂ©ploiement terminĂ© avec succĂšs!" +} + +# Aide +show_help() { + echo "Usage: $0 [NAMESPACE] [ENVIRONMENT] [IMAGE_TAG]" + echo "" + echo "Arguments:" + echo " NAMESPACE Namespace Kubernetes (dĂ©faut: tasks-app)" + echo " ENVIRONMENT Environnement (dev/prod) (dĂ©faut: dev)" + echo " IMAGE_TAG Tag de l'image Docker (dĂ©faut: latest)" + echo "" + echo "Exemples:" + echo " $0 # DĂ©ploiement dev par dĂ©faut" + echo " $0 tasks-prod prod v1.0.0 # DĂ©ploiement prod avec tag v1.0.0" + echo " $0 tasks-dev dev dev-latest # DĂ©ploiement dev avec tag dev-latest" +} + +# Gestion des arguments +if [[ "$1" == "-h" || "$1" == "--help" ]]; then + show_help + exit 0 +fi + +# ExĂ©cuter le script principal +main diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh new file mode 100644 index 0000000..4aee900 --- /dev/null +++ b/scripts/dev-setup.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +# Script de configuration pour l'environnement de dĂ©veloppement +set -e + +# Couleurs pour les logs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +# VĂ©rifier les prĂ©requis +check_prerequisites() { + log "VĂ©rification des prĂ©requis..." + + if ! command -v docker &> /dev/null; then + error "Docker n'est pas installĂ©" + exit 1 + fi + + if ! command -v docker-compose &> /dev/null; then + error "Docker Compose n'est pas installĂ©" + exit 1 + fi + + log "PrĂ©requis OK" +} + +# CrĂ©er le fichier .env s'il n'existe pas +setup_env() { + if [ ! -f ".env" ]; then + log "CrĂ©ation du fichier .env..." + cp env.local .env + warn "Fichier .env créé. Vous pouvez le modifier selon vos besoins." + else + log "Fichier .env existe dĂ©jĂ " + fi +} + +# Construire et dĂ©marrer les services +start_services() { + log "Construction et dĂ©marrage des services..." + + # Construire l'image de l'application + log "Construction de l'image de l'application..." + docker-compose build app + + # DĂ©marrer les services + log "DĂ©marrage des services..." + docker-compose up -d + + log "Services dĂ©marrĂ©s avec succĂšs" +} + +# VĂ©rifier le statut des services +check_services() { + log "VĂ©rification du statut des services..." + + # Attendre que MySQL soit prĂȘt + log "Attente que MySQL soit prĂȘt..." + timeout 60 bash -c 'until docker-compose exec mysql mysqladmin ping -h localhost --silent; do sleep 2; done' + + # VĂ©rifier l'application + log "VĂ©rification de l'application..." + sleep 10 + if curl -f http://localhost:8000/health >/dev/null 2>&1; then + log "Application accessible sur http://localhost:8000" + else + warn "Application pas encore prĂȘte, vĂ©rifiez les logs avec: docker-compose logs app" + fi + + # Afficher les informations + info "=== SERVICES DISPONIBLES ===" + info "Application FastAPI: http://localhost:8000" + info "Documentation API: http://localhost:8000/docs" + info "phpMyAdmin: http://localhost:8080" + info "MySQL: localhost:3306" + echo "" + info "=== COMMANDES UTILES ===" + info "Voir les logs: docker-compose logs -f" + info "ArrĂȘter: docker-compose down" + info "RedĂ©marrer: docker-compose restart" + info "Base de donnĂ©es: docker-compose exec mysql mysql -u app_user -p tasksdb" +} + +# Afficher l'aide +show_help() { + echo "Usage: $0 [COMMAND]" + echo "" + echo "Commands:" + echo " start DĂ©marrer tous les services (dĂ©faut)" + echo " stop ArrĂȘter tous les services" + echo " restart RedĂ©marrer tous les services" + echo " logs Afficher les logs" + echo " status Afficher le statut des services" + echo " clean Nettoyer les volumes et images" + echo " help Afficher cette aide" +} + +# Gestion des commandes +case "${1:-start}" in + start) + check_prerequisites + setup_env + start_services + check_services + ;; + stop) + log "ArrĂȘt des services..." + docker-compose down + ;; + restart) + log "RedĂ©marrage des services..." + docker-compose restart + ;; + logs) + docker-compose logs -f + ;; + status) + docker-compose ps + ;; + clean) + warn "Nettoyage des volumes et images..." + docker-compose down -v --rmi all + ;; + help) + show_help + ;; + *) + error "Commande inconnue: $1" + show_help + exit 1 + ;; +esac diff --git a/scripts/get-artifact-registry-url.sh b/scripts/get-artifact-registry-url.sh new file mode 100644 index 0000000..abf85ff --- /dev/null +++ b/scripts/get-artifact-registry-url.sh @@ -0,0 +1,115 @@ +#!/bin/bash + +# Script pour obtenir l'URL d'Artifact Registry et configurer GitHub + +set -e + +# Couleurs pour les messages +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${GREEN}🔍 Recherche de l'URL d'Artifact Registry${NC}" + +# VĂ©rifier que gcloud CLI est installĂ© +if ! command -v gcloud &> /dev/null; then + echo -e "${RED}❌ Google Cloud CLI (gcloud) n'est pas installĂ©.${NC}" + echo "Installation: https://cloud.google.com/sdk/docs/install" + exit 1 +fi + +# VĂ©rifier que l'utilisateur est connectĂ© Ă  Google Cloud +if ! gcloud auth list --filter=status:ACTIVE --format="value(account)" | grep -q .; then + echo -e "${RED}❌ Vous n'ĂȘtes pas connectĂ© Ă  Google Cloud.${NC}" + echo "Commande: gcloud auth login" + exit 1 +fi + +# Obtenir le projet actuel +PROJECT_ID=$(gcloud config get-value project) +if [ -z "$PROJECT_ID" ]; then + echo -e "${RED}❌ Aucun projet Google Cloud configurĂ©.${NC}" + echo "Commande: gcloud config set project YOUR_PROJECT_ID" + exit 1 +fi + +echo -e "${YELLOW}📋 Projet: ${PROJECT_ID}${NC}" + +# Rechercher les Artifact Registries +echo -e "${YELLOW}🔍 Recherche des Artifact Registries...${NC}" +ARTIFACT_REGISTRIES=$(gcloud artifacts repositories list --format="table(name,format,location)" --project=$PROJECT_ID 2>/dev/null || echo "") + +if [ -z "$ARTIFACT_REGISTRIES" ] || [ "$ARTIFACT_REGISTRIES" = "NAME FORMAT LOCATION" ]; then + echo -e "${RED}❌ Aucun Artifact Registry trouvĂ© dans le projet ${PROJECT_ID}${NC}" + echo "Assurez-vous que Terraform a Ă©tĂ© dĂ©ployĂ© avec succĂšs." + echo "ExĂ©cutez: terraform apply" + exit 1 +fi + +echo -e "${GREEN}✅ Artifact Registries trouvĂ©s:${NC}" +echo "$ARTIFACT_REGISTRIES" + +# Obtenir l'URL du premier registry Docker +echo -e "${YELLOW}🔍 RĂ©cupĂ©ration de l'URL du registry Docker...${NC}" +ARTIFACT_REGISTRY_URL=$(gcloud artifacts repositories list --format="value(name)" --filter="format=DOCKER" --project=$PROJECT_ID | head -1) + +if [ -z "$ARTIFACT_REGISTRY_URL" ]; then + echo -e "${RED}❌ Aucun registry Docker trouvĂ©.${NC}" + exit 1 +fi + +echo -e "${GREEN}✅ Registry Docker trouvĂ©: ${ARTIFACT_REGISTRY_URL}${NC}" + +# VĂ©rifier que gh CLI est installĂ© +if ! command -v gh &> /dev/null; then + echo -e "${RED}❌ GitHub CLI (gh) n'est pas installĂ©. Veuillez l'installer d'abord.${NC}" + echo "Installation: https://cli.github.com/" + exit 1 +fi + +# VĂ©rifier que l'utilisateur est connectĂ© Ă  GitHub +if ! gh auth status &> /dev/null; then + echo -e "${RED}❌ Vous n'ĂȘtes pas connectĂ© Ă  GitHub CLI. Veuillez vous connecter d'abord.${NC}" + echo "Commande: gh auth login" + exit 1 +fi + +# Configuration des variables GitHub +echo -e "${YELLOW}🔧 Configuration des variables GitHub...${NC}" + +# Variables pour l'environnement de dĂ©veloppement +echo -e "${BLUE}📝 Configuration de l'environnement 'Develop'...${NC}" +gh variable set ARTIFACT_REGISTRY_URL --body="$ARTIFACT_REGISTRY_URL" --env=Develop +gh variable set REGISTRY --body="$ARTIFACT_REGISTRY_URL" --env=Develop +gh variable set IMAGE_NAME --body="tasks-app" --env=Develop +gh variable set INSTANCE_NAME --body="tasks-mysql" --env=Develop + +# Variables pour l'environnement de production +echo -e "${BLUE}📝 Configuration de l'environnement 'Production'...${NC}" +gh variable set ARTIFACT_REGISTRY_URL --body="$ARTIFACT_REGISTRY_URL" --env=Production +gh variable set REGISTRY --body="$ARTIFACT_REGISTRY_URL" --env=Production +gh variable set IMAGE_NAME --body="tasks-app" --env=Production +gh variable set INSTANCE_NAME --body="tasks-mysql" --env=Production + +echo -e "${GREEN}✅ Configuration terminĂ©e avec succĂšs !${NC}" +echo "" +echo -e "${BLUE}📋 RĂ©sumĂ© de la configuration:${NC}" +echo " - Projet GCP: $PROJECT_ID" +echo " - Artifact Registry URL: $ARTIFACT_REGISTRY_URL" +echo "" +echo -e "${YELLOW}💡 Les workflows GitHub Actions utiliseront maintenant Artifact Registry.${NC}" +echo -e "${YELLOW}💡 Le workflow deploy-dev.yml s'exĂ©cutera aprĂšs terraform.yml.${NC}" + +# Afficher les commandes Docker pour tester +echo "" +echo -e "${BLUE}🐳 Commandes Docker pour tester:${NC}" +echo " # Authentification" +echo " gcloud auth configure-docker $ARTIFACT_REGISTRY_URL" +echo "" +echo " # Build et push d'une image de test" +echo " docker build -t $ARTIFACT_REGISTRY_URL/test:latest ./app" +echo " docker push $ARTIFACT_REGISTRY_URL/test:latest" +echo "" +echo -e "${GREEN}🎉 Configuration terminĂ©e !${NC}" diff --git a/scripts/get-wif-info.sh b/scripts/get-wif-info.sh new file mode 100644 index 0000000..db5a469 --- /dev/null +++ b/scripts/get-wif-info.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +# Script pour obtenir les informations du module bootstrap-wif +# et afficher les valeurs nĂ©cessaires pour configurer GitHub + +set -e + +# Couleurs pour les messages +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${GREEN}🔧 Informations du module bootstrap-wif${NC}" + +# VĂ©rifier que nous sommes dans le bon rĂ©pertoire +if [ ! -d "bootstrap-wif" ]; then + echo -e "${RED}❌ Le rĂ©pertoire bootstrap-wif/ n'existe pas.${NC}" + echo "Assurez-vous d'ĂȘtre dans le rĂ©pertoire racine du projet." + exit 1 +fi + +# Aller dans le rĂ©pertoire bootstrap-wif +cd bootstrap-wif + +# VĂ©rifier que Terraform est initialisĂ© +if [ ! -d ".terraform" ]; then + echo -e "${YELLOW}⚠ Terraform n'est pas initialisĂ© dans bootstrap-wif/${NC}" + echo "ExĂ©cutez d'abord: terraform init" + exit 1 +fi + +# Obtenir les outputs +echo -e "${YELLOW}📋 RĂ©cupĂ©ration des informations du module WIF...${NC}" + +WIF_PROVIDER=$(terraform output -raw workload_identity_provider_name 2>/dev/null || echo "") +SA_EMAIL=$(terraform output -raw service_account_email 2>/dev/null || echo "") + +if [ -z "$WIF_PROVIDER" ] || [ -z "$SA_EMAIL" ]; then + echo -e "${RED}❌ Impossible de rĂ©cupĂ©rer les informations du module WIF.${NC}" + echo "Assurez-vous que le module bootstrap-wif/ a Ă©tĂ© dĂ©ployĂ© avec succĂšs." + echo "ExĂ©cutez: terraform apply" + exit 1 +fi + +# Obtenir les informations du projet +PROJECT_ID=$(gcloud config get-value project 2>/dev/null || echo "") + +if [ -z "$PROJECT_ID" ]; then + echo -e "${RED}❌ Aucun projet Google Cloud configurĂ©.${NC}" + echo "Commande: gcloud config set project YOUR_PROJECT_ID" + exit 1 +fi + +# Afficher les informations +echo -e "${GREEN}✅ Informations rĂ©cupĂ©rĂ©es avec succĂšs !${NC}" +echo "" +echo -e "${BLUE}📋 Configuration GitHub Actions:${NC}" +echo " - Projet GCP: $PROJECT_ID" +echo " - Service Account: $SA_EMAIL" +echo " - WIF Provider: $WIF_PROVIDER" +echo "" + +# Afficher les commandes pour configurer GitHub +echo -e "${YELLOW}🔧 Commandes pour configurer GitHub:${NC}" +echo "" +echo -e "${BLUE}# Secrets pour l'environnement 'Develop':${NC}" +echo "gh secret set GCP_PROJECT_ID --body=\"$PROJECT_ID\" --env=Develop" +echo "gh secret set GCP_SERVICE_ACCOUNT --body=\"$SA_EMAIL\" --env=Develop" +echo "gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER --body=\"$WIF_PROVIDER\" --env=Develop" +echo "" +echo -e "${BLUE}# Variables pour l'environnement 'Develop':${NC}" +echo "gh variable set REGISTRY --body=\"gcr.io\" --env=Develop" +echo "gh variable set IMAGE_NAME --body=\"tasks-app\" --env=Develop" +echo "gh variable set INSTANCE_NAME --body=\"tasks-mysql\" --env=Develop" +echo "" +echo -e "${BLUE}# Secrets pour l'environnement 'Production':${NC}" +echo "gh secret set GCP_PROJECT_ID --body=\"$PROJECT_ID\" --env=Production" +echo "gh secret set GCP_SERVICE_ACCOUNT --body=\"$SA_EMAIL\" --env=Production" +echo "gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER --body=\"$WIF_PROVIDER\" --env=Production" +echo "" +echo -e "${BLUE}# Variables pour l'environnement 'Production':${NC}" +echo "gh variable set REGISTRY --body=\"gcr.io\" --env=Production" +echo "gh variable set IMAGE_NAME --body=\"tasks-app\" --env=Production" +echo "gh variable set INSTANCE_NAME --body=\"tasks-mysql\" --env=Production" +echo "" + +# Demander si l'utilisateur veut exĂ©cuter automatiquement +echo -e "${YELLOW}đŸ€” Voulez-vous exĂ©cuter automatiquement ces commandes ? (y/N)${NC}" +read -p "> " AUTO_EXECUTE + +if [[ $AUTO_EXECUTE =~ ^[Yy]$ ]]; then + echo -e "${YELLOW}🔧 ExĂ©cution automatique des commandes...${NC}" + + # Secrets pour Develop + echo -e "${BLUE}📝 Configuration de l'environnement 'Develop'...${NC}" + gh secret set GCP_PROJECT_ID --body="$PROJECT_ID" --env=Develop + gh secret set GCP_SERVICE_ACCOUNT --body="$SA_EMAIL" --env=Develop + gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER --body="$WIF_PROVIDER" --env=Develop + gh variable set REGISTRY --body="gcr.io" --env=Develop + gh variable set IMAGE_NAME --body="tasks-app" --env=Develop + gh variable set INSTANCE_NAME --body="tasks-mysql" --env=Develop + + # Secrets pour Production + echo -e "${BLUE}📝 Configuration de l'environnement 'Production'...${NC}" + gh secret set GCP_PROJECT_ID --body="$PROJECT_ID" --env=Production + gh secret set GCP_SERVICE_ACCOUNT --body="$SA_EMAIL" --env=Production + gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER --body="$WIF_PROVIDER" --env=Production + gh variable set REGISTRY --body="gcr.io" --env=Production + gh variable set IMAGE_NAME --body="tasks-app" --env=Production + gh variable set INSTANCE_NAME --body="tasks-mysql" --env=Production + + echo -e "${GREEN}✅ Configuration terminĂ©e avec succĂšs !${NC}" +else + echo -e "${YELLOW}💡 Copiez et exĂ©cutez les commandes ci-dessus pour configurer GitHub.${NC}" +fi + +# Retourner au rĂ©pertoire parent +cd .. + +echo -e "${GREEN}🎉 Les workflows CI/CD sont maintenant prĂȘts !${NC}" diff --git a/scripts/init-db.sql b/scripts/init-db.sql new file mode 100644 index 0000000..32b9f30 --- /dev/null +++ b/scripts/init-db.sql @@ -0,0 +1,11 @@ +-- Script d'initialisation de la base de donnĂ©es +-- Ce script est exĂ©cutĂ© automatiquement lors du premier dĂ©marrage de MySQL + +-- CrĂ©er la base de donnĂ©es si elle n'existe pas +CREATE DATABASE IF NOT EXISTS tasksdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- Utiliser la base de donnĂ©es +USE tasksdb; + +-- Afficher un message de confirmation +SELECT 'Base de donnĂ©es tasksdb créée avec succĂšs!' as message; diff --git a/scripts/setup-github-secrets.sh b/scripts/setup-github-secrets.sh new file mode 100644 index 0000000..0e68de9 --- /dev/null +++ b/scripts/setup-github-secrets.sh @@ -0,0 +1,139 @@ +#!/bin/bash + +# Script pour configurer les secrets GitHub pour les workflows CI/CD +# Ce script doit ĂȘtre exĂ©cutĂ© aprĂšs le dĂ©ploiement Terraform + +set -e + +# Couleurs pour les messages +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo -e "${GREEN}🔧 Configuration des secrets GitHub pour les workflows CI/CD${NC}" + +# VĂ©rifier que gh CLI est installĂ© +if ! command -v gh &> /dev/null; then + echo -e "${RED}❌ GitHub CLI (gh) n'est pas installĂ©. Veuillez l'installer d'abord.${NC}" + echo "Installation: https://cli.github.com/" + exit 1 +fi + +# VĂ©rifier que l'utilisateur est connectĂ© Ă  GitHub +if ! gh auth status &> /dev/null; then + echo -e "${RED}❌ Vous n'ĂȘtes pas connectĂ© Ă  GitHub CLI. Veuillez vous connecter d'abord.${NC}" + echo "Commande: gh auth login" + exit 1 +fi + +# VĂ©rifier que gcloud CLI est installĂ© +if ! command -v gcloud &> /dev/null; then + echo -e "${RED}❌ Google Cloud CLI (gcloud) n'est pas installĂ©. Veuillez l'installer d'abord.${NC}" + echo "Installation: https://cloud.google.com/sdk/docs/install" + exit 1 +fi + +# VĂ©rifier que l'utilisateur est connectĂ© Ă  Google Cloud +if ! gcloud auth list --filter=status:ACTIVE --format="value(account)" | grep -q .; then + echo -e "${RED}❌ Vous n'ĂȘtes pas connectĂ© Ă  Google Cloud. Veuillez vous connecter d'abord.${NC}" + echo "Commande: gcloud auth login" + exit 1 +fi + +# Obtenir les informations du projet +PROJECT_ID=$(gcloud config get-value project) +if [ -z "$PROJECT_ID" ]; then + echo -e "${RED}❌ Aucun projet Google Cloud configurĂ©. Veuillez configurer un projet.${NC}" + echo "Commande: gcloud config set project YOUR_PROJECT_ID" + exit 1 +fi + +echo -e "${YELLOW}📋 Configuration pour le projet: ${PROJECT_ID}${NC}" + +# Obtenir les informations du cluster GKE +echo -e "${YELLOW}🔍 Recherche des clusters GKE...${NC}" +CLUSTERS=$(gcloud container clusters list --format="value(name,location)" --project=$PROJECT_ID) + +if [ -z "$CLUSTERS" ]; then + echo -e "${RED}❌ Aucun cluster GKE trouvĂ© dans le projet ${PROJECT_ID}${NC}" + exit 1 +fi + +# Afficher les clusters disponibles +echo -e "${YELLOW}📋 Clusters GKE disponibles:${NC}" +echo "$CLUSTERS" + +# Demander Ă  l'utilisateur de choisir le cluster +echo -e "${YELLOW}đŸ€” Veuillez choisir le cluster pour l'environnement de dĂ©veloppement:${NC}" +read -p "Nom du cluster: " CLUSTER_NAME +read -p "RĂ©gion du cluster: " CLUSTER_REGION + +# Obtenir les informations du service account GitHub Actions depuis bootstrap-wif +echo -e "${YELLOW}🔍 Recherche du service account GitHub Actions...${NC}" +SA_EMAIL=$(gcloud iam service-accounts list --filter="displayName:GitHub Terraform" --format="value(email)" --project=$PROJECT_ID) + +if [ -z "$SA_EMAIL" ]; then + echo -e "${RED}❌ Service account GitHub Actions non trouvĂ©.${NC}" + echo "Assurez-vous que le module bootstrap-wif/ a Ă©tĂ© dĂ©ployĂ© avec succĂšs." + exit 1 +fi + +echo -e "${GREEN}✅ Service account trouvĂ©: ${SA_EMAIL}${NC}" + +# Obtenir les informations Workload Identity Federation +echo -e "${YELLOW}🔍 Recherche de la configuration Workload Identity Federation...${NC}" +WIF_PROVIDER=$(gcloud iam workload-identity-pools providers list --location=global --format="value(name)" --project=$PROJECT_ID | head -1) + +if [ -z "$WIF_PROVIDER" ]; then + echo -e "${RED}❌ Workload Identity Federation non configurĂ©.${NC}" + echo "Veuillez d'abord configurer WIF avec le module bootstrap-wif/" + exit 1 +fi + +echo -e "${GREEN}✅ WIF Provider trouvĂ©: ${WIF_PROVIDER}${NC}" + +# Configuration des secrets GitHub +echo -e "${YELLOW}🔧 Configuration des secrets GitHub...${NC}" + +# Secrets pour l'environnement de dĂ©veloppement +echo -e "${YELLOW}📝 Configuration des secrets pour l'environnement 'Develop'...${NC}" + +gh secret set GCP_PROJECT_ID --body="$PROJECT_ID" --env=Develop +gh secret set GKE_CLUSTER_NAME --body="$CLUSTER_NAME" --env=Develop +gh secret set GKE_ZONE --body="$CLUSTER_REGION" --env=Develop +gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER --body="$WIF_PROVIDER" --env=Develop +gh secret set GCP_SERVICE_ACCOUNT --body="$SA_EMAIL" --env=Develop + +# Variables pour l'environnement de dĂ©veloppement +echo -e "${YELLOW}📝 Configuration des variables pour l'environnement 'Develop'...${NC}" + +gh variable set REGISTRY --body="gcr.io" --env=Develop +gh variable set IMAGE_NAME --body="tasks-app" --env=Develop +gh variable set INSTANCE_NAME --body="tasks-mysql" --env=Develop + +# Secrets pour l'environnement de production +echo -e "${YELLOW}📝 Configuration des secrets pour l'environnement 'Production'...${NC}" + +gh secret set GCP_PROJECT_ID --body="$PROJECT_ID" --env=Production +gh secret set GKE_CLUSTER_NAME --body="$CLUSTER_NAME" --env=Production +gh secret set GKE_ZONE --body="$CLUSTER_REGION" --env=Production +gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER --body="$WIF_PROVIDER" --env=Production +gh secret set GCP_SERVICE_ACCOUNT --body="$SA_EMAIL" --env=Production + +# Variables pour l'environnement de production +echo -e "${YELLOW}📝 Configuration des variables pour l'environnement 'Production'...${NC}" + +gh variable set REGISTRY --body="gcr.io" --env=Production +gh variable set IMAGE_NAME --body="tasks-app" --env=Production +gh variable set INSTANCE_NAME --body="tasks-mysql" --env=Production + +echo -e "${GREEN}✅ Configuration terminĂ©e avec succĂšs !${NC}" +echo -e "${YELLOW}📋 RĂ©sumĂ© de la configuration:${NC}" +echo " - Projet GCP: $PROJECT_ID" +echo " - Cluster GKE: $CLUSTER_NAME ($CLUSTER_REGION)" +echo " - Service Account: $SA_EMAIL" +echo " - WIF Provider: $WIF_PROVIDER" +echo "" +echo -e "${GREEN}🎉 Les workflows CI/CD sont maintenant configurĂ©s !${NC}" +echo -e "${YELLOW}💡 Vous pouvez maintenant pousser du code pour dĂ©clencher les workflows.${NC}" \ No newline at end of file diff --git a/scripts/update-wif-permissions.sh b/scripts/update-wif-permissions.sh new file mode 100644 index 0000000..f0ef0a5 --- /dev/null +++ b/scripts/update-wif-permissions.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# Script pour mettre Ă  jour les permissions du module bootstrap-wif +# et Ă©viter de crĂ©er des registries par hasard + +set -e + +# Couleurs pour les messages +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${GREEN}🔧 Mise Ă  jour des permissions WIF${NC}" + +# VĂ©rifier que nous sommes dans le bon rĂ©pertoire +if [ ! -d "bootstrap-wif" ]; then + echo -e "${RED}❌ Le rĂ©pertoire bootstrap-wif/ n'existe pas.${NC}" + echo "Assurez-vous d'ĂȘtre dans le rĂ©pertoire racine du projet." + exit 1 +fi + +# Aller dans le rĂ©pertoire bootstrap-wif +cd bootstrap-wif + +# VĂ©rifier que Terraform est initialisĂ© +if [ ! -d ".terraform" ]; then + echo -e "${YELLOW}⚠ Terraform n'est pas initialisĂ©.${NC}" + echo -e "${YELLOW}🔧 Initialisation de Terraform...${NC}" + terraform init +fi + +# VĂ©rifier l'Ă©tat actuel +echo -e "${YELLOW}🔍 VĂ©rification de l'Ă©tat actuel...${NC}" +terraform plan + +# Demander confirmation +echo -e "${YELLOW}đŸ€” Voulez-vous appliquer les changements ? (y/N)${NC}" +read -p "> " CONFIRM + +if [[ $CONFIRM =~ ^[Yy]$ ]]; then + echo -e "${YELLOW}🔧 Application des changements...${NC}" + terraform apply -auto-approve + + echo -e "${GREEN}✅ Permissions mises Ă  jour avec succĂšs !${NC}" + + # Afficher les nouvelles informations + echo -e "${BLUE}📋 Nouvelles informations:${NC}" + WIF_PROVIDER=$(terraform output -raw workload_identity_provider_name) + SA_EMAIL=$(terraform output -raw service_account_email) + + echo " - WIF Provider: $WIF_PROVIDER" + echo " - Service Account: $SA_EMAIL" + + echo -e "${YELLOW}💡 Vous pouvez maintenant configurer GitHub avec ces informations.${NC}" + echo -e "${YELLOW}💡 Utilisez: ./scripts/get-wif-info.sh${NC}" +else + echo -e "${YELLOW}❌ Mise Ă  jour annulĂ©e.${NC}" +fi + +# Retourner au rĂ©pertoire parent +cd .. + +echo -e "${GREEN}🎉 Script terminĂ© !${NC}" diff --git a/terraform/main.tf b/terraform/main.tf index dbfcca4..1d8b0dc 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -60,7 +60,21 @@ module "database" { depends_on = [module.network] } -# Module Kubernetes (dĂ©pend du rĂ©seau, IAM et base de donnĂ©es) +# Module Artifact Registry +module "artifact_registry" { + source = "./modules/artifact-registry" + + project_id = var.project_id + region = var.region + environment = var.environment + repository_name = var.artifact_registry_config.repository_name + + # Configuration de rĂ©tention + retention_days = var.artifact_registry_config.retention_days + cleanup_policies = var.artifact_registry_config.cleanup_policies +} + +# Module Kubernetes (dĂ©pend du rĂ©seau, IAM, base de donnĂ©es et Artifact Registry) module "kubernetes" { source = "./modules/kubernetes" @@ -78,5 +92,5 @@ module "kubernetes" { node_zones = var.kubernetes_config.node_zones nodes_service_account_email = module.iam.gke_nodes_service_account_email - depends_on = [module.network, module.iam, module.database] + depends_on = [module.network, module.iam, module.database, module.artifact_registry] } diff --git a/terraform/modules/artifact-registry/main.tf b/terraform/modules/artifact-registry/main.tf new file mode 100644 index 0000000..2ec65be --- /dev/null +++ b/terraform/modules/artifact-registry/main.tf @@ -0,0 +1,54 @@ +# Module Artifact Registry - Gestion des registries Docker et permissions IAM + +terraform { + required_providers { + google = { + source = "hashicorp/google" + version = ">= 5.0" + } + } +} + +# Artifact Registry pour les images Docker +resource "google_artifact_registry_repository" "docker_repo" { + project = var.project_id + location = var.region + repository_id = "${var.repository_name}-${var.environment}" + description = "Docker repository for ${var.environment} environment" + format = "DOCKER" + + labels = { + environment = var.environment + purpose = "docker-images" + } + + # Configuration de la rĂ©tention des images + cleanup_policies { + id = "delete-prerelease" + action = "DELETE" + condition { + tag_state = "TAGGED" + tag_prefixes = [ + "dev-", + "test-", + "staging-" + ] + older_than = "604800s" # 7 jours + } + } + + cleanup_policies { + id = "keep-minimum-versions" + action = "KEEP" + condition { + tag_state = "TAGGED" + tag_prefixes = [ + "v", + "prod-" + ] + } + } +} + +# Les permissions IAM sont gĂ©rĂ©es par les modules iam/ et bootstrap-wif/ +# Ce module se contente de crĂ©er le registry Artifact Registry diff --git a/terraform/modules/artifact-registry/outputs.tf b/terraform/modules/artifact-registry/outputs.tf new file mode 100644 index 0000000..a0172bb --- /dev/null +++ b/terraform/modules/artifact-registry/outputs.tf @@ -0,0 +1,18 @@ +# Outputs pour le module Artifact Registry + +output "repository_id" { + description = "ID du repository Artifact Registry" + value = google_artifact_registry_repository.docker_repo.repository_id +} + +output "repository_name" { + description = "Nom complet du repository Artifact Registry" + value = google_artifact_registry_repository.docker_repo.name +} + +output "repository_url" { + description = "URL du repository Artifact Registry" + value = "${var.region}-docker.pkg.dev/${var.project_id}/${google_artifact_registry_repository.docker_repo.repository_id}" +} + +# Les outputs IAM sont gĂ©rĂ©s par les modules iam/ et bootstrap-wif/ diff --git a/terraform/modules/artifact-registry/variables.tf b/terraform/modules/artifact-registry/variables.tf new file mode 100644 index 0000000..31fe121 --- /dev/null +++ b/terraform/modules/artifact-registry/variables.tf @@ -0,0 +1,48 @@ +# Variables pour le module Artifact Registry + +variable "project_id" { + description = "ID du projet GCP" + type = string +} + +variable "region" { + description = "RĂ©gion GCP pour le registry" + type = string +} + +variable "environment" { + description = "Environnement (dev, staging, prod)" + type = string + validation { + condition = contains(["dev", "staging", "prod"], var.environment) + error_message = "L'environnement doit ĂȘtre 'dev', 'staging' ou 'prod'." + } +} + +variable "repository_name" { + description = "Nom du repository Artifact Registry" + type = string + default = "tasks-app" +} + +# Les variables IAM sont gĂ©rĂ©es par les modules iam/ et bootstrap-wif/ + +variable "retention_days" { + description = "Nombre de jours de rĂ©tention pour les images de dĂ©veloppement" + type = number + default = 7 +} + +variable "cleanup_policies" { + description = "Politiques de nettoyage personnalisĂ©es" + type = list(object({ + id = string + action = string + condition = object({ + tag_state = optional(string) + tag_prefixes = optional(list(string)) + older_than = optional(string) + }) + })) + default = [] +} diff --git a/terraform/modules/iam/main.tf b/terraform/modules/iam/main.tf index 9f4ccf8..1196dd9 100644 --- a/terraform/modules/iam/main.tf +++ b/terraform/modules/iam/main.tf @@ -110,8 +110,20 @@ resource "google_project_iam_member" "gke_nodes_cloudsql_client" { member = "serviceAccount:${google_service_account.gke_nodes.email}" } +# Permissions pour Artifact Registry +resource "google_project_iam_member" "gke_nodes_artifactregistry_reader" { + project = var.project_id + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.gke_nodes.email}" +} + +# Les permissions pour GitHub Actions sont gĂ©rĂ©es via Workload Identity Federation +# dans le module bootstrap-wif/ + # Output email SA nƓuds output "gke_nodes_service_account_email" { description = "Email du service account utilisĂ© par les nƓuds GKE" value = google_service_account.gke_nodes.email } + +# Les outputs pour GitHub Actions sont gĂ©rĂ©s dans bootstrap-wif/ diff --git a/terraform/outputs.tf b/terraform/outputs.tf index d2943cf..cacbe00 100644 --- a/terraform/outputs.tf +++ b/terraform/outputs.tf @@ -38,3 +38,15 @@ output "deployment_info" { environment = var.environment } } + +# Outputs pour Artifact Registry +output "artifact_registry_info" { + description = "Informations sur Artifact Registry" + value = { + repository_id = module.artifact_registry.repository_id + repository_name = module.artifact_registry.repository_name + repository_url = module.artifact_registry.repository_url + } +} + +# Les outputs pour GitHub Actions sont gĂ©rĂ©s dans bootstrap-wif/ diff --git a/terraform/variables.tf b/terraform/variables.tf index eec6d35..9bf2b04 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -101,3 +101,21 @@ variable "network_config" { ip_range = string }) } + +variable "artifact_registry_config" { + description = "Configuration pour Artifact Registry" + type = object({ + repository_name = optional(string, "tasks-app") + retention_days = optional(number, 7) + cleanup_policies = optional(list(object({ + id = string + action = string + condition = object({ + tag_state = optional(string) + tag_prefixes = optional(list(string)) + older_than = optional(string) + }) + })), []) + }) + default = {} +}