From 2dab0812c78963295e08ce854e24f83a21abee31 Mon Sep 17 00:00:00 2001 From: folland87 Date: Tue, 22 Oct 2024 16:53:19 +0200 Subject: [PATCH] test deployment --- .github/actions/k8s-deploy/action.yml | 166 ++++++++++++++++++++++++++ .github/workflows/deploy.yml | 27 +++++ .github/workflows/deployment.yml | 80 ------------- client/src/components/header.tsx | 4 + client/src/hooks/useVersion.ts | 12 ++ server/src/routes/healthy.ts | 14 ++- server/src/schemas/healthy.ts | 1 + 7 files changed, 223 insertions(+), 81 deletions(-) create mode 100644 .github/actions/k8s-deploy/action.yml create mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/deployment.yml create mode 100644 client/src/hooks/useVersion.ts diff --git a/.github/actions/k8s-deploy/action.yml b/.github/actions/k8s-deploy/action.yml new file mode 100644 index 0000000..48bd404 --- /dev/null +++ b/.github/actions/k8s-deploy/action.yml @@ -0,0 +1,166 @@ +name: "Kubernetes Deploy Action" +description: "Deploy an application to Kubernetes" +inputs: + app_port: + description: "The port on which the application runs" + required: false + default: "3000" + replicas: + description: "Number of replicas for the deployment" + required: false + default: "1" + cpu_request: + description: "CPU request for the container (e.g., 100m)" + required: false + default: "100m" + cpu_limit: + description: "CPU limit for the container (e.g., 200m)" + required: false + default: "200m" + memory_request: + description: "Memory request for the container (e.g., 128Mi)" + required: false + default: "128Mi" + memory_limit: + description: "Memory limit for the container (e.g., 256Mi)" + required: false + default: "256Mi" + kube_config: + description: "Base64 encoded kubeconfig file" + required: true + env_secrets: + description: "Base64 encoded .env file containing secrets" + required: false + default: "" + +outputs: + deployment_url: + description: "The URL of the deployed application" + value: ${{ steps.set-output.outputs.deployment_url }} + +runs: + using: "composite" + steps: + - name: Check out repository + uses: actions/checkout@v2 + + - name: Set up kubectl + uses: azure/setup-kubectl@v1 + + - name: Configure kubectl + shell: bash + run: | + echo "${{ inputs.kube_config }}" | base64 -d > kubeconfig.yaml + export KUBECONFIG=./kubeconfig.yaml + + - name: Install Kustomize + shell: bash + run: | + curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + sudo mv kustomize /usr/local/bin/ + + - name: Set environment variables + shell: bash + run: | + echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV + echo "IMAGE_TAG=$(echo $GITHUB_SHA | cut -c1-7)" >> $GITHUB_ENV + echo "REPO_NAME=$(echo ${GITHUB_REPOSITORY#*/} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV + echo "DEPLOYMENT_URL=$(echo ${GITHUB_REPOSITORY#*/} | tr '[:upper:]' '[:lower:]')-${GITHUB_REF#refs/heads/}.staging.dataesr.ovh" >> $GITHUB_ENV + + - name: Build and push Docker image + shell: bash + run: | + docker build -t ghcr.io/${{ github.repository_owner }}/${{ env.REPO_NAME }}:${{ env.IMAGE_TAG }} . + docker push ghcr.io/${{ github.repository_owner }}/${{ env.REPO_NAME }}:${{ env.IMAGE_TAG }} + + - name: Create Kustomize files + shell: bash + run: | + mkdir -p kustomize/base + cat < kustomize/base/deployment.yaml + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ${{ env.REPO_NAME }} + spec: + replicas: ${{ inputs.replicas }} + selector: + matchLabels: + app: ${{ env.REPO_NAME }} + template: + metadata: + labels: + app: ${{ env.REPO_NAME }} + spec: + containers: + - name: app + image: ghcr.io/${{ github.repository_owner }}/${{ env.REPO_NAME }}:${{ env.IMAGE_TAG }} + ports: + - containerPort: ${{ inputs.app_port }} + resources: + requests: + cpu: ${{ inputs.cpu_request }} + memory: ${{ inputs.memory_request }} + limits: + cpu: ${{ inputs.cpu_limit }} + memory: ${{ inputs.memory_limit }} + ${{ inputs.env_secrets != '' && 'envFrom:' || '' }} + ${{ inputs.env_secrets != '' && '- secretRef:' || '' }} + ${{ inputs.env_secrets != '' && ' name: ${{ env.REPO_NAME }}-secrets' || '' }} + EOF + + cat < kustomize/base/service.yaml + apiVersion: v1 + kind: Service + metadata: + name: ${{ env.REPO_NAME }}-svc + spec: + selector: + app: ${{ env.REPO_NAME }} + ports: + - port: 80 + targetPort: ${{ inputs.app_port }} + EOF + + cat < kustomize/base/ingress.yaml + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: ${{ env.REPO_NAME }} + spec: + entryPoints: + - websecure + routes: + - match: Host(${{ env.DEPLOYMENT_URL }}) + kind: Rule + services: + - name: ${{ env.REPO_NAME }}-svc + port: 80 + tls: {} + EOF + + cat < kustomize/base/kustomization.yaml + resources: + - deployment.yaml + - service.yaml + - ingress.yaml + ${{ inputs.env_secrets != '' && '- secrets.yaml' || '' }} + EOF + + - name: Create Kubernetes Secret + if: inputs.env_secrets != '' + shell: bash + run: | + echo "${{ inputs.env_secrets }}" | base64 -d > .env + kubectl create secret generic ${{ env.REPO_NAME }}-secrets --from-env-file=.env --dry-run=client -o yaml > kustomize/base/secrets.yaml + rm .env + + - name: Deploy to Kubernetes + shell: bash + run: | + kustomize build kustomize/base | kubectl apply -f - + + - name: Set output + id: set-output + shell: bash + run: echo "deployment_url=${{ env.DEPLOYMENT_URL }}" >> $GITHUB_OUTPUT diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..71e6794 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Deploy to Kubernetes + +on: + push: + branches: + - "main" + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Deploy to Kubernetes + uses: ./.github/actions/k8s-deploy + with: + app_port: 3000 + replicas: 2 + cpu_request: 100m + cpu_limit: 200m + memory_request: 128Mi + memory_limit: 256Mi + kube_config: ${{ secrets.KUBECONFIG }} + docker_username: ${{ secrets.DOCKER_USERNAME }} + docker_password: ${{ secrets.DOCKER_PASSWORD }} + env_secrets: ${{ secrets.MAIN_ENV_SECRETS }} + - name: Get deployment URL + run: echo "Deployed to ${{ steps.deploy.outputs.deployment_url }}" diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml deleted file mode 100644 index 15bdb61..0000000 --- a/.github/workflows/deployment.yml +++ /dev/null @@ -1,80 +0,0 @@ -# TODO. -name: Deploy - -on: - push: - branches: - # Add the branch name you want to deploy - - staging - tags: - - v[0-9]+.[0-9]+.[0-9]+ - -env: - GITHUB_REF_NAME: ${{ github.ref_name }} - URL: ${{ github.repository }}-${{ github.ref_name }} - PROD: ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '.') }} - IMAGE_ID: $(echo ghcr.io/${{ github.repository }} | tr '[A-Z]' '[a-z]') - -jobs: - test: - name: Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - - run: bun install --frozen-lockfile - - run: bun run test - - publish-ghcr: - name: Publish docker image - needs: - - test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Log into registry - run: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} - - - name: 🐋 Build Docker image - run: docker build -t $IMAGE_ID . - - - name: 🐋 Push Docker image with ref name - run: | - docker build -t $IMAGE_ID . - docker tag $IMAGE_ID $IMAGE_ID:${{ env.GITHUB_REF_NAME }} - docker push $IMAGE_ID:${{ env.GITHUB_REF_NAME }} - - - name: 🐋 Push latest Docker image if prod - if: ${{ env.PROD }} - run: | - docker tag $IMAGE_ID $IMAGE_ID:latest - docker push $IMAGE_ID:latest - - # deploy: - # name: Update deployment - # runs-on: ubuntu-latest - # needs: - # - publish-ghcr - # steps: - # - name: Deploy to Cluster - # id: kubectl-deploy - # uses: dataesr/kubectl-deploy@v1.1 - # env: - # KUBE_CONFIG: ${{ secrets.KUBE_CONFIG_DOAD_STAGING }} - # with: - # namespace: ${{ env.DEPLOYMENT_NAMESPACE }} - # restart: ${{ env.DEPLOYMENT }} - - notify: - needs: - - publish-ghcr # Changed from 'deploy' since deploy job is commented out - if: always() - runs-on: ubuntu-latest - steps: - - uses: dataesr/mm-notifier-action@v1.0.2 - with: - deployment_url: ${{ env.URL }} - github_token: ${{ secrets.GITHUB_TOKEN}} - mattermost_channel: bots - mattermost_webhook_url: ${{ secrets.MATTERMOST_WEBHOOK_URL }} diff --git a/client/src/components/header.tsx b/client/src/components/header.tsx index 017bef2..a61b01d 100644 --- a/client/src/components/header.tsx +++ b/client/src/components/header.tsx @@ -1,6 +1,9 @@ import { Link } from "react-router-dom"; +import useVersion from "@/hooks/useVersion"; + const Header: React.FC = () => { + const { data } = useVersion(); return (
@@ -24,6 +27,7 @@ const Header: React.FC = () => {

Nom du site / service + {data?.version && {data?.version}}

baseline - prĂ©cisions sur l‘organisation

diff --git a/client/src/hooks/useVersion.ts b/client/src/hooks/useVersion.ts new file mode 100644 index 0000000..d060cef --- /dev/null +++ b/client/src/hooks/useVersion.ts @@ -0,0 +1,12 @@ +import api from '@/api-client'; +import { useQuery } from '@tanstack/react-query'; + +export default function useVersion() { + return useQuery({ + queryKey: ['version'], + queryFn: async () => { + const { data } = await api.version.get(); + return data; + } + }) +} diff --git a/server/src/routes/healthy.ts b/server/src/routes/healthy.ts index 19180bc..580849d 100644 --- a/server/src/routes/healthy.ts +++ b/server/src/routes/healthy.ts @@ -1,6 +1,6 @@ import Elysia from 'elysia'; -import { HealthySchema, PingPongSchema } from '~/schemas/healthy'; +import { HealthySchema, PingPongSchema, VersionSchema } from '~/schemas/healthy'; export const healthyRoutes = new Elysia() @@ -21,3 +21,15 @@ export const healthyRoutes = new Elysia() summary: 'Check if the server is healthy', } ) + .get( + '/version', + () => { + return { version: Bun.env.VERSION ?? '0.0.0' } + }, + { + reponse: { + 200: VersionSchema + }, + summary: 'Returns the version of the application', + } + ) diff --git a/server/src/schemas/healthy.ts b/server/src/schemas/healthy.ts index fc072de..8046ee3 100644 --- a/server/src/schemas/healthy.ts +++ b/server/src/schemas/healthy.ts @@ -4,3 +4,4 @@ export const HealthySchema = t.Object({ healthy: t.Boolean() }); export const PingPongSchema = t.String(); +export const VersionSchema = t.String();