diff --git a/.env.example b/.env.example index ed6ed73..3cf29da 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ OPENAI_API_KEY=sk-... +LOGOS_KEY=lg-... \ No newline at end of file diff --git a/.github/workflows/deploy-k8s.yml b/.github/workflows/deploy-k8s.yml index 763970d..84c544f 100644 --- a/.github/workflows/deploy-k8s.yml +++ b/.github/workflows/deploy-k8s.yml @@ -61,8 +61,10 @@ jobs: --set userService.image.tag=${{ steps.vars.outputs.image_tag }} \ --set groceryService.image.tag=${{ steps.vars.outputs.image_tag }} \ --set genai.image.tag=${{ steps.vars.outputs.image_tag }} \ + --set userDb.image.tag=${{ steps.vars.outputs.image_tag }} \ + --set groceryDb.image.tag=${{ steps.vars.outputs.image_tag }} \ --set genai.logosKey="${{ secrets.LOGOS_KEY }}" \ --set genai.openaiApiKey="${{ secrets.OPENAI_API_KEY }}" \ --set userDb.password="${{ secrets.USER_DB_PASSWORD }}" \ --set groceryDb.password="${{ secrets.GROCERY_DB_PASSWORD }}" \ - --atomic + --set jwt.secret="${{ secrets.JWT_SECRET }}" diff --git a/.github/workflows/test-build-push.yml b/.github/workflows/test-build-push.yml index eeb4f32..9ed9313 100644 --- a/.github/workflows/test-build-push.yml +++ b/.github/workflows/test-build-push.yml @@ -1,6 +1,7 @@ name: Test, Build and Push Images -# Runs tests and builds images on every branch. Only pushes to the registry on main. +# Runs tests and builds images on every branch. +# Pushes to the registry only on main, or on a manual workflow_dispatch run. on: push: branches: @@ -93,7 +94,7 @@ jobs: with: context: ${{ matrix.context }} file: ${{ matrix.context }}/Dockerfile - push: ${{ github.ref == 'refs/heads/main' }} + push: ${{ github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha diff --git a/helm/bytebite/templates/api-gateway-deployment.yaml b/helm/bytebite/templates/api-gateway-deployment.yaml index 84d2f67..134abfe 100644 --- a/helm/bytebite/templates/api-gateway-deployment.yaml +++ b/helm/bytebite/templates/api-gateway-deployment.yaml @@ -31,3 +31,12 @@ spec: value: "http://grocery-service:{{ .Values.groceryService.service.port }}" - name: USER_SERVICE_BASE_URL value: "http://user-service:{{ .Values.userService.service.port }}" + {{- if .Values.jwt.secret }} + # Shared JWT secret (verifies tokens). Must match user-service. Only + # injected when set; otherwise the app's built-in default applies. + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: jwt-secret + key: jwt-secret + {{- end }} diff --git a/helm/bytebite/templates/grocery-db-statefulset.yaml b/helm/bytebite/templates/grocery-db-deployment.yaml similarity index 65% rename from helm/bytebite/templates/grocery-db-statefulset.yaml rename to helm/bytebite/templates/grocery-db-deployment.yaml index 50ac2e8..9e2097b 100644 --- a/helm/bytebite/templates/grocery-db-statefulset.yaml +++ b/helm/bytebite/templates/grocery-db-deployment.yaml @@ -1,11 +1,14 @@ apiVersion: apps/v1 -kind: StatefulSet +kind: Deployment metadata: name: bytebite-grocery-db namespace: {{ .Values.namespace }} spec: - serviceName: grocery-db replicas: 1 + # Ephemeral DB (emptyDir): recreate the pod rather than rolling, so two + # instances never run at once and each start gets a clean database. + strategy: + type: Recreate selector: matchLabels: app: bytebite-grocery-db @@ -18,6 +21,13 @@ spec: - name: grocery-db image: "{{ .Values.groceryDb.image.repository }}:{{ .Values.groceryDb.image.tag }}" imagePullPolicy: {{ .Values.groceryDb.image.pullPolicy }} + resources: + limits: + cpu: "250m" + memory: "256Mi" + requests: + cpu: "100m" + memory: "128Mi" ports: - containerPort: 5432 env: @@ -33,11 +43,7 @@ spec: volumeMounts: - name: grocery-db-data mountPath: /var/lib/postgresql/data - volumeClaimTemplates: - - metadata: - name: grocery-db-data - spec: - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 1Gi \ No newline at end of file + volumes: + # Ephemeral storage: the database is recreated on every pod start. + - name: grocery-db-data + emptyDir: {} \ No newline at end of file diff --git a/helm/bytebite/templates/grocery-service-deployment.yaml b/helm/bytebite/templates/grocery-service-deployment.yaml index cbab032..2099d0c 100644 --- a/helm/bytebite/templates/grocery-service-deployment.yaml +++ b/helm/bytebite/templates/grocery-service-deployment.yaml @@ -27,6 +27,10 @@ spec: ports: - containerPort: {{ .Values.groceryService.service.targetPort }} env: + # The app's dev default port is 8082; override it so it listens on + # the container/Service port (8080), matching the api-gateway route. + - name: SERVER_PORT + value: "{{ .Values.groceryService.service.targetPort }}" - name: GENAI_BASE_URL value: "http://genai-service:{{ .Values.genai.service.port }}" - name: SPRING_DATASOURCE_URL diff --git a/helm/bytebite/templates/secret.yaml b/helm/bytebite/templates/secret.yaml index 76976eb..12692a2 100644 --- a/helm/bytebite/templates/secret.yaml +++ b/helm/bytebite/templates/secret.yaml @@ -16,4 +16,15 @@ metadata: type: Opaque data: user-db-password: {{ .Values.userDb.password | b64enc | quote }} - grocery-db-password: {{ .Values.groceryDb.password | b64enc | quote }} \ No newline at end of file + grocery-db-password: {{ .Values.groceryDb.password | b64enc | quote }} +{{- if .Values.jwt.secret }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: jwt-secret + namespace: {{ .Values.namespace }} +type: Opaque +data: + jwt-secret: {{ .Values.jwt.secret | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/helm/bytebite/templates/user-db-statefulset.yaml b/helm/bytebite/templates/user-db-deployment.yaml similarity index 65% rename from helm/bytebite/templates/user-db-statefulset.yaml rename to helm/bytebite/templates/user-db-deployment.yaml index e8f21c2..5824b49 100644 --- a/helm/bytebite/templates/user-db-statefulset.yaml +++ b/helm/bytebite/templates/user-db-deployment.yaml @@ -1,11 +1,14 @@ apiVersion: apps/v1 -kind: StatefulSet +kind: Deployment metadata: name: bytebite-user-db namespace: {{ .Values.namespace }} spec: - serviceName: user-db replicas: 1 + # Ephemeral DB (emptyDir): recreate the pod rather than rolling, so two + # instances never run at once and each start gets a clean database. + strategy: + type: Recreate selector: matchLabels: app: bytebite-user-db @@ -18,6 +21,13 @@ spec: - name: user-db image: "{{ .Values.userDb.image.repository }}:{{ .Values.userDb.image.tag }}" imagePullPolicy: {{ .Values.userDb.image.pullPolicy }} + resources: + limits: + cpu: "250m" + memory: "256Mi" + requests: + cpu: "100m" + memory: "128Mi" ports: - containerPort: 5432 env: @@ -33,11 +43,7 @@ spec: volumeMounts: - name: user-db-data mountPath: /var/lib/postgresql/data - volumeClaimTemplates: - - metadata: - name: user-db-data - spec: - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 1Gi \ No newline at end of file + volumes: + # Ephemeral storage: the database is recreated on every pod start. + - name: user-db-data + emptyDir: {} \ No newline at end of file diff --git a/helm/bytebite/templates/user-service-deployment.yaml b/helm/bytebite/templates/user-service-deployment.yaml index 34561db..674b646 100644 --- a/helm/bytebite/templates/user-service-deployment.yaml +++ b/helm/bytebite/templates/user-service-deployment.yaml @@ -27,6 +27,19 @@ spec: ports: - containerPort: {{ .Values.userService.service.targetPort }} env: + # The app's dev default port is 8083; override it so it listens on + # the container/Service port (8080), matching the api-gateway route. + - name: SERVER_PORT + value: "{{ .Values.userService.service.targetPort }}" + {{- if .Values.jwt.secret }} + # Shared JWT secret (signs tokens). Must match the api-gateway. Only + # injected when set; otherwise the app's built-in default applies. + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: jwt-secret + key: jwt-secret + {{- end }} - name: SPRING_DATASOURCE_URL value: "jdbc:postgresql://user-db:5432/{{ .Values.userDb.name }}" - name: SPRING_DATASOURCE_USERNAME diff --git a/helm/bytebite/values.yaml b/helm/bytebite/values.yaml index 7cc5417..ae5c85e 100644 --- a/helm/bytebite/values.yaml +++ b/helm/bytebite/values.yaml @@ -76,6 +76,12 @@ groceryDb: user: bytebite_grocery password: "" +# Shared JWT signing/verification secret for user-service (signs) and +# api-gateway (verifies). Must be >=32 chars. Leave empty to fall back to the +# apps' built-in dev default; set via --set jwt.secret / a GitHub secret for real deploys. +jwt: + secret: "" + ingress: enabled: true className: "nginx"