diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index c853606c..45e4454c 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -55,5 +55,6 @@ jobs: gke-credentials: ${{ secrets.GKE_SA_KEY }} gke-project: ${{ secrets.GKE_PROJECT }} gh-key: ${{ secrets.GH_KEY }} + pgpasswd: ${{ secrets.PGPASSWORD }} # cloudflare-api-token: ${{ secrets.CF_API_TOKEN }} # cloudflare-zone-id: ${{ secrets.CF_ZONE_ID }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1e5ab055..57e41673 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -31,6 +31,9 @@ on: gh-key: description: Github authentication key required: true + pgpasswd: + description: PGPASSWORD + required: true # cloudflare-api-token: # description: Cloudflare API Token # required: true @@ -39,12 +42,68 @@ on: # required: true jobs: + meta: + name: Meta + runs-on: ubuntu-latest + outputs: + context: ${{ steps.meta.outputs.context }} + cors: ${{ steps.meta.outputs.cors }} + environment: ${{ steps.meta.outputs.environment }} + namespace: ${{ steps.meta.outputs.namespace }} + release_name: ${{ steps.meta.outputs.release_name }} + replica: ${{ steps.meta.outputs.replica }} + url: ${{ steps.meta.outputs.url }} + + steps: + - name: Generate metadata + id: meta + run: | + set -o pipefail + CORS_LOCALHOST="http://localhost|https://localhost|http://localhost:3000" + if [[ "${{ github.ref }}" == 'refs/heads/main' ]]; then + # Tags are deployed in prod + CONTEXT=prod + CORS=$(echo "^https://api-platform.com|$CORS_LOCALHOST$" | sed 's/\./\\./g' ) + ENVIRONMENT=prod + NAMESPACE=prod-website + RELEASE_NAME=website-prod + REPLICA=1 + URL=api-platform.com + else + CONTEXT=nonprod + if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then + ENVIRONMENT=preview + RELEASE_NAME=pr-$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH") + else + ENVIRONMENT=staging + RELEASE_NAME=${{ github.ref_name }} + fi + CORS=$(echo "^https://${RELEASE_NAME}.apip.preprod-tilleuls.ovh|$CORS_LOCALHOST$" | sed 's/\./\\./g' ) + NAMESPACE=nonprod-website + REPLICA=1 + URL=$RELEASE_NAME.apip.preprod-tilleuls.ovh + fi + echo "context=$CONTEXT" >> "$GITHUB_OUTPUT" + echo "cors=$CORS" >> "$GITHUB_OUTPUT" + echo "environment=$ENVIRONMENT" >> "$GITHUB_OUTPUT" + echo "namespace=$NAMESPACE" >> "$GITHUB_OUTPUT" + echo "release_name=$RELEASE_NAME" >> "$GITHUB_OUTPUT" + echo "replica=$REPLICA" >> "$GITHUB_OUTPUT" + echo "url=$URL" >> "$GITHUB_OUTPUT" + + cat $GITHUB_OUTPUT + + deploy: name: Deploy runs-on: ubuntu-latest permissions: contents: 'read' id-token: 'write' + pull-requests: 'write' + needs: ["meta"] + environment: + name: ${{ needs.meta.outputs.environment }} steps: - name: Checkout uses: actions/checkout@v3 @@ -71,106 +130,52 @@ jobs: helm repo add bitnami https://charts.bitnami.com/bitnami/ helm repo add stable https://charts.helm.sh/stable/ helm dependency build ./helm/api-platform - - name: Define namespace - run: | - set -o pipefail - if [[ "${{ github.ref }}" == 'refs/heads/main' ]]; then - # Tags are deployed in prod - echo "CONTEXT=prod" >> "$GITHUB_ENV" - echo "RELEASE_NAME=website-prod" >> "$GITHUB_ENV" - echo "URL=api-platform.com" >> "$GITHUB_ENV" - echo 'CORS=["https://api-platform.com", "http://localhost", "https://localhost", "http://localhost:3000"]' >> "$GITHUB_ENV" - echo "NAMESPACE=prod-website" >> "$GITHUB_ENV" - echo "REPLICA=1" >> "$GITHUB_ENV" - else - CONTEXT=nonprod - if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then - echo RELEASE_NAME=pr-$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH") >> "$GITHUB_ENV" - export RELEASE_NAME=pr-$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH") - else - echo "RELEASE_NAME=${{ github.ref_name }}" >> "$GITHUB_ENV" - export RELEASE_NAME=${{ github.ref_name }} - fi - echo "URL=$RELEASE_NAME.apip.preprod-tilleuls.ovh" >> "$GITHUB_ENV" - echo "REPLICA=1" >> "$GITHUB_ENV" - echo "NAMESPACE=nonprod-website" >> "$GITHUB_ENV" - echo 'CORS=["https://${{ env.RELEASE_NAME}}.apip.preprod-tilleuls.ovh", "http://localhost", "https://localhost", "http://localhost:3000"]' >> "$GITHUB_ENV" - fi - name: HELM Deploy run: | set -o pipefail - if ! helm -n ${{ env.NAMESPACE }} status ${{ env.RELEASE_NAME }} &>/dev/null; then - JWT_PASSPHRASE=$(openssl rand -base64 32) - JWT_SECRET_KEY=$(openssl genpkey -pass file:<(echo "$JWT_PASSPHRASE") -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096) - helm upgrade ${{ env.RELEASE_NAME }} ./helm/api-platform \ - --reuse-values \ - --install \ - --create-namespace \ - --debug \ - --wait \ - --atomic \ - --namespace=${{ env.NAMESPACE }} \ - --set=app.version=${{ github.sha }} \ - --set=php.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/php \ - --set=php.image.tag=${{ inputs.docker-images-version }} \ - --set=php.image.pullPolicy=Always \ - --set=caddy.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/caddy \ - --set=caddy.image.tag=${{ inputs.docker-images-version }} \ - --set=caddy.image.pullPolicy=Always \ - --set=pwa.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/pwa \ - --set=pwa.image.tag=${{ inputs.docker-images-version }} \ - --set=pwa.image.pullPolicy=Always \ - --set=postgresql.image.repository=bitnamilegacy/postgresql \ - --set=bucket.s3Upstream=storage.googleapis.com \ - --set=bucket.s3Name=api-platform-website-v3 \ - --set=service.type=NodePort \ - --set=ingress.enabled=true \ - --set=ingress.hosts[0].host=${{ env.URL }} \ - --set=ingress.hosts[0].paths[0].path=/ \ - --set=ingress.hosts[0].paths[0].pathType=ImplementationSpecific \ - --set=ingress.tls[0].hosts[0]=${{ env.URL }} \ - --set=ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt-production \ - --set=ingress.tls[0].secretName=${{ env.RELEASE_NAME }}-website-ssl \ - --set=php.jwt.secretKey="$JWT_SECRET_KEY" \ - --set=php.jwt.publicKey="$(openssl pkey -in <(echo "$JWT_SECRET_KEY") -passin file:<(echo "$JWT_PASSPHRASE") -pubout)" \ - --set=php.jwt.passphrase=$JWT_PASSPHRASE \ - --set=php.corsAllowOrigin="^$(echo "${{ join(fromJSON(env.CORS), '|') }}" | sed 's/\./\\./g')$" \ - --set=php.host=${{ env.URL }} \ - --set=next.rootUrl=${{ env.URL }} \ - --set=github.key=${{ secrets.gh-key }} \ - --set=postgresql.global.postgresql.auth.password=$(openssl rand -base64 32 | tr -d "=+/") \ - --set=postgresql.global.postgresql.auth.username=website \ - | sed --unbuffered '/USER-SUPPLIED VALUES/,$d' - else - helm upgrade ${{ env.RELEASE_NAME }} ./helm/api-platform \ - --reuse-values \ - --install \ - --create-namespace \ - --debug \ - --wait \ - --atomic \ - --namespace=${{ env.NAMESPACE }} \ - --set=app.version=${{ github.sha }} \ - --set=php.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/php \ - --set=php.image.tag=${{ inputs.docker-images-version }} \ - --set=php.image.pullPolicy=Always \ - --set=postgresql.image.repository=bitnamilegacy/postgresql \ - --set=caddy.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/caddy \ - --set=caddy.image.tag=${{ inputs.docker-images-version }} \ - --set=caddy.image.pullPolicy=Always \ - --set=pwa.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/pwa \ - --set=pwa.image.tag=${{ inputs.docker-images-version }} \ - --set=pwa.image.pullPolicy=Always \ - --set=php.corsAllowOrigin="^$(echo "${{ join(fromJSON(env.CORS), '|') }}" | sed 's/\./\\./g')$" \ - --set=github.key=${{ secrets.gh-key }} \ - --set=next.rootUrl=${{ env.URL }} \ - --set=bucket.s3Upstream=storage.googleapis.com \ - --set=bucket.s3Name=api-platform-website-v3 \ - | sed --unbuffered '/USER-SUPPLIED VALUES/,$d' - fi + JWT_PASSPHRASE=$(openssl rand -base64 32) + JWT_SECRET_KEY=$(openssl genpkey -pass file:<(echo "$JWT_PASSPHRASE") -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096) + echo ${{ secrets.pgpasswd }} | sed 's/./& /g' + helm upgrade ${{ needs.meta.outputs.release_name }} ./helm/api-platform \ + --install \ + --create-namespace \ + --debug \ + --wait \ + --atomic \ + --namespace ${{ needs.meta.outputs.namespace }} \ + --set=app.version=${{ github.sha }} \ + --set=php.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/php \ + --set=php.image.tag=${{ inputs.docker-images-version }} \ + --set=php.image.pullPolicy=Always \ + --set=caddy.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/caddy \ + --set=caddy.image.tag=${{ inputs.docker-images-version }} \ + --set=caddy.image.pullPolicy=Always \ + --set=pwa.image.repository=europe-west1-docker.pkg.dev/${{ secrets.gke-project }}/website/pwa \ + --set=pwa.image.tag=${{ inputs.docker-images-version }} \ + --set=pwa.image.pullPolicy=Always \ + --set=bucket.s3Upstream=storage.googleapis.com \ + --set=bucket.s3Name=api-platform-website-v3 \ + --set=service.type=NodePort \ + --set=ingress.enabled=true \ + --set=ingress.hosts[0].host=${{ needs.meta.outputs.url }} \ + --set=ingress.hosts[0].paths[0].path=/ \ + --set=ingress.hosts[0].paths[0].pathType=ImplementationSpecific \ + --set=ingress.tls[0].hosts[0]=${{ needs.meta.outputs.url }} \ + --set=ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt-production \ + --set=ingress.tls[0].secretName=${{ needs.meta.outputs.release_name }}-website-ssl \ + --set=php.jwt.secretKey="$JWT_SECRET_KEY" \ + --set=php.jwt.publicKey="$(openssl pkey -in <(echo "$JWT_SECRET_KEY") -passin file:<(echo "$JWT_PASSPHRASE") -pubout)" \ + --set=php.jwt.passphrase=$JWT_PASSPHRASE \ + --set=php.corsAllowOrigin="${{ needs.meta.outputs.cors }}" \ + --set=php.host=${{ needs.meta.outputs.url }} \ + --set=next.rootUrl=${{ needs.meta.outputs.url }} \ + --set=github.key=${{ secrets.gh-key }} \ + --set=postgresql.global.postgresql.auth.password="${{ secrets.pgpasswd }}" \ + --set=postgresql.global.postgresql.auth.username=website \ + | sed --unbuffered '/USER-SUPPLIED VALUES/,$d' - name: Debug kube events if: failure() - run: kubectl get events --namespace=${{ env.NAMESPACE }} --sort-by .metadata.creationTimestamp + run: kubectl get events --namespace=${{ needs.meta.outputs.namespace }} --sort-by .metadata.creationTimestamp links: name: Check for dead links diff --git a/helm/api-platform/Chart.yaml b/helm/api-platform/Chart.yaml index 2c13b318..8dce4b79 100644 --- a/helm/api-platform/Chart.yaml +++ b/helm/api-platform/Chart.yaml @@ -24,8 +24,8 @@ version: 0.1.0 # follow Semantic Versioning. They should reflect the version the application is using. appVersion: 0.1.0 -dependencies: - - name: postgresql - version: ~12.1.14 - repository: https://charts.bitnami.com/bitnami/ - condition: postgresql.enabled +#dependencies: +# - name: postgresql +# version: ~12.1.14 +# repository: https://charts.bitnami.com/bitnami/ +# condition: postgresql.enabled diff --git a/helm/api-platform/templates/postgresql.yaml b/helm/api-platform/templates/postgresql.yaml new file mode 100644 index 00000000..fb41b12c --- /dev/null +++ b/helm/api-platform/templates/postgresql.yaml @@ -0,0 +1,37 @@ +{{- if .Values.postgresql.enabled -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "api-platform.fullname" . }}-initdb + labels: + {{- include "api-platform.labels" . | nindent 4 }} +type: kubernetes.io/basic-auth +data: + username: {{ .Values.postgresql.global.postgresql.auth.username | b64enc }} + password: {{ .Values.postgresql.global.postgresql.auth.password | b64enc }} +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: {{ .Release.Name }}-postgresql +spec: + instances: 1 + imageName: ghcr.io/cloudnative-pg/postgresql:17 + superuserSecret: + name: {{ include "api-platform.fullname" . }}-initdb + bootstrap: + initdb: + database: {{ .Values.postgresql.global.postgresql.auth.database }} + owner: {{ .Values.postgresql.global.postgresql.auth.username }} + postInitSQL: + - {{ printf "ALTER USER %s CREATEDB;" .Values.postgresql.global.postgresql.auth.username | quote }} + secret: + name: {{ include "api-platform.fullname" . }}-initdb + managed: + services: + disabledDefaultServices: ["ro", "r"] + storage: + size: {{ .Values.postgresql.primary.persistence.size }} + resources: + {{- toYaml .Values.postgresql.primary.resources | nindent 4 }} +{{- end -}} diff --git a/helm/api-platform/templates/secrets.yaml b/helm/api-platform/templates/secrets.yaml index f197ea1a..078631fe 100644 --- a/helm/api-platform/templates/secrets.yaml +++ b/helm/api-platform/templates/secrets.yaml @@ -7,7 +7,7 @@ metadata: type: Opaque stringData: {{- if .Values.postgresql.enabled }} - database-url: {{ printf "pgsql://%s:%s@%s-postgresql/%s?serverVersion=13&charset=utf8" .Values.postgresql.global.postgresql.auth.username .Values.postgresql.global.postgresql.auth.password .Release.Name .Values.postgresql.global.postgresql.auth.database | quote }} + database-url: {{ printf "pgsql://%s:%s@%s-postgresql-rw/%s?serverVersion=13&charset=utf8" .Values.postgresql.global.postgresql.auth.username .Values.postgresql.global.postgresql.auth.password .Release.Name .Values.postgresql.global.postgresql.auth.database | quote }} {{- else }} database-url: {{ .Values.postgresql.url | quote }} {{- end }}