diff --git a/argocd-apps/invy.yaml b/argocd-apps/invy.yaml new file mode 100644 index 0000000..7278867 --- /dev/null +++ b/argocd-apps/invy.yaml @@ -0,0 +1,21 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: ipify + namespace: argocd +spec: + project: my-apps + source: + repoURL: https://git.dvirlabs.com/dvirlabs/my-apps.git + targetRevision: HEAD + path: charts/invy-chart + helm: + valueFiles: + - ../../manifests/invy/values.yaml + destination: + server: https://kubernetes.default.svc + namespace: my-apps + syncPolicy: + automated: + prune: true + selfHeal: true diff --git a/charts/invy-chart/Chart.yaml b/charts/invy-chart/Chart.yaml new file mode 100644 index 0000000..7d4b40c --- /dev/null +++ b/charts/invy-chart/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v2 +name: invy +description: A Helm chart for Invy - Wedding Guest List Management Application +type: application +version: 1.0.0 +appVersion: "1.0.0" +keywords: + - invy + - wedding + - guest-management +maintainers: + - name: dvir diff --git a/charts/invy-chart/README.md b/charts/invy-chart/README.md new file mode 100644 index 0000000..0d4e930 --- /dev/null +++ b/charts/invy-chart/README.md @@ -0,0 +1,183 @@ +# Invy Helm Chart + +This Helm chart deploys the Invy wedding guest list management application on Kubernetes. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.0+ +- Persistent Volume provisioner support in the underlying infrastructure (for PostgreSQL) +- Ingress controller (Traefik recommended) +- Cert-manager (for TLS certificates) + +## Components + +This chart deploys the following components: + +- **Frontend**: React + Vite application (Nginx) +- **Backend**: FastAPI application +- **Database**: PostgreSQL 16 + +## Installation + +### Add the chart repository (if applicable) + +```bash +helm repo add invy https://your-helm-repo.com +helm repo update +``` + +### Install the chart + +```bash +# Install with default values +helm install invy ./invy-chart -n my-apps --create-namespace + +# Install with custom values +helm install invy ./invy-chart -n my-apps --create-namespace -f custom-values.yaml +``` + +## Configuration + +The following table lists the configurable parameters and their default values. + +### Global Parameters + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `global.namespace` | Namespace for all resources | `my-apps` | +| `global.imagePullSecrets` | Image pull secrets | `[]` | + +### Backend Parameters + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `backend.image.repository` | Backend image repository | `harbor.dvirlabs.com/my-apps/invy-backend` | +| `backend.image.tag` | Backend image tag | `latest` | +| `backend.replicaCount` | Number of backend replicas | `1` | +| `backend.service.port` | Backend service port | `8000` | +| `backend.ingress.enabled` | Enable backend ingress | `true` | +| `backend.ingress.hosts[0].host` | Backend ingress hostname | `api-invy.dvirlabs.com` | + +### Frontend Parameters + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `frontend.image.repository` | Frontend image repository | `harbor.dvirlabs.com/my-apps/invy-frontend` | +| `frontend.image.tag` | Frontend image tag | `latest` | +| `frontend.replicaCount` | Number of frontend replicas | `1` | +| `frontend.service.port` | Frontend service port | `80` | +| `frontend.env.VITE_API_URL` | Backend API URL | `https://api-invy.dvirlabs.com` | +| `frontend.ingress.enabled` | Enable frontend ingress | `true` | +| `frontend.ingress.hosts[0].host` | Frontend ingress hostname | `invy.dvirlabs.com` | + +### PostgreSQL Parameters + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `postgres.user` | PostgreSQL user | `invy_user` | +| `postgres.password` | PostgreSQL password | `invy_password` | +| `postgres.database` | PostgreSQL database name | `invy_db` | +| `postgres.persistence.enabled` | Enable persistence | `true` | +| `postgres.persistence.size` | Persistent volume size | `10Gi` | +| `postgres.persistence.storageClass` | Storage class | `nfs-client` | + +## Building and Pushing Images + +### Backend Image + +```bash +cd backend +docker build -t harbor.dvirlabs.com/my-apps/invy-backend:latest . +docker push harbor.dvirlabs.com/my-apps/invy-backend:latest +``` + +### Frontend Image + +```bash +cd frontend +docker build --build-arg VITE_API_URL=https://api-invy.dvirlabs.com -t harbor.dvirlabs.com/my-apps/invy-frontend:latest . +docker push harbor.dvirlabs.com/my-apps/invy-frontend:latest +``` + +## Upgrading + +```bash +helm upgrade invy ./invy-chart -n my-apps +``` + +## Uninstalling + +```bash +helm uninstall invy -n my-apps +``` + +## Customization + +Create a `custom-values.yaml` file to override default values: + +```yaml +backend: + image: + tag: "v1.0.0" + ingress: + hosts: + - host: api.mycompany.com + paths: + - path: / + pathType: Prefix + +frontend: + image: + tag: "v1.0.0" + env: + VITE_API_URL: "https://api.mycompany.com" + ingress: + hosts: + - host: invy.mycompany.com + paths: + - path: / + pathType: Prefix + +postgres: + password: "your-secure-password" + persistence: + storageClass: "your-storage-class" +``` + +Then install with: + +```bash +helm install invy ./invy-chart -n my-apps -f custom-values.yaml +``` + +## Troubleshooting + +### Check pod status + +```bash +kubectl get pods -n my-apps +``` + +### View pod logs + +```bash +# Backend logs +kubectl logs -n my-apps -l app.kubernetes.io/component=backend + +# Frontend logs +kubectl logs -n my-apps -l app.kubernetes.io/component=frontend + +# Database logs +kubectl logs -n my-apps -l app.kubernetes.io/component=database +``` + +### Access the database + +```bash +kubectl exec -it -n my-apps invy-db-0 -- psql -U invy_user -d invy_db +``` + +## Support + +For issues and feature requests, please open an issue in the repository. diff --git a/charts/invy-chart/templates/NOTES.txt b/charts/invy-chart/templates/NOTES.txt new file mode 100644 index 0000000..8ddf3ec --- /dev/null +++ b/charts/invy-chart/templates/NOTES.txt @@ -0,0 +1,38 @@ +Thank you for installing {{ .Chart.Name }}! + +Your release is named {{ .Release.Name }}. + +To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get all {{ .Release.Name }} + +{{- if .Values.frontend.ingress.enabled }} + +Application URLs: +{{- range .Values.frontend.ingress.hosts }} + Frontend: https://{{ .host }} +{{- end }} +{{- range .Values.backend.ingress.hosts }} + Backend API: https://{{ .host }} +{{- end }} +{{- else }} + +To access your application: + +1. Get the frontend URL: + export POD_NAME=$(kubectl get pods --namespace {{ .Values.global.namespace }} -l "app.kubernetes.io/name={{ include "invy.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=frontend" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ .Values.global.namespace }} port-forward $POD_NAME 8080:80 + echo "Visit http://127.0.0.1:8080 to use your application" + +2. Get the backend URL: + export POD_NAME=$(kubectl get pods --namespace {{ .Values.global.namespace }} -l "app.kubernetes.io/name={{ include "invy.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=backend" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ .Values.global.namespace }} port-forward $POD_NAME 8000:8000 + echo "Visit http://127.0.0.1:8000 to access the API" +{{- end }} + +Database connection: + Host: {{ include "invy.fullname" . }}-db + Port: {{ .Values.postgres.port }} + Database: {{ .Values.postgres.database }} + User: {{ .Values.postgres.user }} diff --git a/charts/invy-chart/templates/_helpers.tpl b/charts/invy-chart/templates/_helpers.tpl new file mode 100644 index 0000000..16eb99d --- /dev/null +++ b/charts/invy-chart/templates/_helpers.tpl @@ -0,0 +1,60 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "invy.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "invy.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 "invy.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "invy.labels" -}} +helm.sh/chart: {{ include "invy.chart" . }} +{{ include "invy.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "invy.selectorLabels" -}} +app.kubernetes.io/name: {{ include "invy.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "invy.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "invy.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/invy-chart/templates/backend-deployment.yaml b/charts/invy-chart/templates/backend-deployment.yaml new file mode 100644 index 0000000..95066e4 --- /dev/null +++ b/charts/invy-chart/templates/backend-deployment.yaml @@ -0,0 +1,80 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "invy.fullname" . }}-backend + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: backend +spec: + replicas: {{ .Values.backend.replicaCount }} + selector: + matchLabels: + {{- include "invy.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: backend + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "invy.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: backend + spec: + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "invy.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + initContainers: + - name: wait-for-postgres + image: busybox:1.35 + command: ['sh', '-c', 'until nc -z {{ include "invy.fullname" . }}-db-headless {{ .Values.postgres.port | default 5432 }}; do echo waiting for postgres; sleep 2; done;'] + containers: + - name: backend + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.backend.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.backend.service.targetPort }} + protocol: TCP + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: {{ include "invy.fullname" . }}-secrets + key: database-url + {{- range $key, $value := .Values.backend.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 10 + periodSeconds: 5 + resources: + {{- toYaml .Values.backend.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/charts/invy-chart/templates/backend-ingress.yaml b/charts/invy-chart/templates/backend-ingress.yaml new file mode 100644 index 0000000..8c9e935 --- /dev/null +++ b/charts/invy-chart/templates/backend-ingress.yaml @@ -0,0 +1,42 @@ +{{- if .Values.backend.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "invy.fullname" . }}-backend + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: backend + {{- with .Values.backend.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.backend.ingress.className }} + ingressClassName: {{ .Values.backend.ingress.className }} + {{- end }} + {{- if .Values.backend.ingress.tls }} + tls: + {{- range .Values.backend.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.backend.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "invy.fullname" $ }}-backend + port: + number: {{ $.Values.backend.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/invy-chart/templates/backend-service.yaml b/charts/invy-chart/templates/backend-service.yaml new file mode 100644 index 0000000..41ab487 --- /dev/null +++ b/charts/invy-chart/templates/backend-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "invy.fullname" . }}-backend + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: backend +spec: + type: {{ .Values.backend.service.type }} + ports: + - port: {{ .Values.backend.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "invy.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: backend diff --git a/charts/invy-chart/templates/db-schema-configmap.yaml b/charts/invy-chart/templates/db-schema-configmap.yaml new file mode 100644 index 0000000..554d8c7 --- /dev/null +++ b/charts/invy-chart/templates/db-schema-configmap.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "invy.fullname" . }}-db-schema + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: database +data: + init.sql: | + -- Wedding Guest List Database Schema + + CREATE TABLE IF NOT EXISTS guests ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + phone VARCHAR(50), + email VARCHAR(255), + rsvp_status VARCHAR(50) DEFAULT 'pending', + meal_preference VARCHAR(100), + plus_one BOOLEAN DEFAULT FALSE, + plus_one_name VARCHAR(255), + notes TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + + -- Create index for faster searches + CREATE INDEX IF NOT EXISTS idx_guests_name ON guests(name); + CREATE INDEX IF NOT EXISTS idx_guests_email ON guests(email); + CREATE INDEX IF NOT EXISTS idx_guests_rsvp_status ON guests(rsvp_status); + + -- Create trigger to update updated_at timestamp + CREATE OR REPLACE FUNCTION update_updated_at_column() + RETURNS TRIGGER AS $$ + BEGIN + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; + END; + $$ language 'plpgsql'; + + CREATE TRIGGER update_guests_updated_at BEFORE UPDATE ON guests + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/charts/invy-chart/templates/db-service.yaml b/charts/invy-chart/templates/db-service.yaml new file mode 100644 index 0000000..0724125 --- /dev/null +++ b/charts/invy-chart/templates/db-service.yaml @@ -0,0 +1,36 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "invy.fullname" . }}-db + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: database +spec: + type: {{ .Values.postgres.service.type }} + ports: + - port: {{ .Values.postgres.service.port }} + targetPort: postgres + protocol: TCP + name: postgres + selector: + {{- include "invy.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: database +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "invy.fullname" . }}-db-headless + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: database +spec: + type: ClusterIP + clusterIP: None + ports: + - port: {{ .Values.postgres.service.port }} + targetPort: postgres + protocol: TCP + name: postgres + selector: + {{- include "invy.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: database diff --git a/charts/invy-chart/templates/db-statefulset.yaml b/charts/invy-chart/templates/db-statefulset.yaml new file mode 100644 index 0000000..6321063 --- /dev/null +++ b/charts/invy-chart/templates/db-statefulset.yaml @@ -0,0 +1,90 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "invy.fullname" . }}-db + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: database +spec: + serviceName: {{ include "invy.fullname" . }}-db-headless + replicas: 1 + selector: + matchLabels: + {{- include "invy.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: database + template: + metadata: + labels: + {{- include "invy.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: database + spec: + containers: + - name: postgres + image: "{{ .Values.postgres.image.repository }}:{{ .Values.postgres.image.tag }}" + imagePullPolicy: {{ .Values.postgres.image.pullPolicy }} + ports: + - name: postgres + containerPort: {{ .Values.postgres.port }} + protocol: TCP + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: {{ include "invy.fullname" . }}-secrets + key: postgres-user + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "invy.fullname" . }}-secrets + key: postgres-password + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: {{ include "invy.fullname" . }}-secrets + key: postgres-database + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + - name: init-script + mountPath: /docker-entrypoint-initdb.d + resources: + {{- toYaml .Values.postgres.resources | nindent 12 }} + livenessProbe: + exec: + command: + - pg_isready + - -U + - $(POSTGRES_USER) + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + exec: + command: + - pg_isready + - -U + - $(POSTGRES_USER) + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: init-script + configMap: + name: {{ include "invy.fullname" . }}-db-schema + {{- if .Values.postgres.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: postgres-data + spec: + accessModes: + - {{ .Values.postgres.persistence.accessMode }} + {{- if .Values.postgres.persistence.storageClass }} + storageClassName: {{ .Values.postgres.persistence.storageClass }} + {{- end }} + resources: + requests: + storage: {{ .Values.postgres.persistence.size }} + {{- else }} + - name: postgres-data + emptyDir: {} + {{- end }} diff --git a/charts/invy-chart/templates/frontend-deployment.yaml b/charts/invy-chart/templates/frontend-deployment.yaml new file mode 100644 index 0000000..50ef9e4 --- /dev/null +++ b/charts/invy-chart/templates/frontend-deployment.yaml @@ -0,0 +1,73 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "invy.fullname" . }}-frontend + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: frontend +spec: + replicas: {{ .Values.frontend.replicaCount }} + selector: + matchLabels: + {{- include "invy.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: frontend + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "invy.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: frontend + spec: + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "invy.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: frontend + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.frontend.service.targetPort }} + protocol: TCP + {{- if .Values.frontend.env }} + env: + {{- range $key, $value := .Values.frontend.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- end }} + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 10 + periodSeconds: 5 + resources: + {{- toYaml .Values.frontend.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/charts/invy-chart/templates/frontend-ingress.yaml b/charts/invy-chart/templates/frontend-ingress.yaml new file mode 100644 index 0000000..abbbc6e --- /dev/null +++ b/charts/invy-chart/templates/frontend-ingress.yaml @@ -0,0 +1,42 @@ +{{- if .Values.frontend.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "invy.fullname" . }}-frontend + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: frontend + {{- with .Values.frontend.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.frontend.ingress.className }} + ingressClassName: {{ .Values.frontend.ingress.className }} + {{- end }} + {{- if .Values.frontend.ingress.tls }} + tls: + {{- range .Values.frontend.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.frontend.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "invy.fullname" $ }}-frontend + port: + number: {{ $.Values.frontend.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/invy-chart/templates/frontend-service.yaml b/charts/invy-chart/templates/frontend-service.yaml new file mode 100644 index 0000000..5564e4a --- /dev/null +++ b/charts/invy-chart/templates/frontend-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "invy.fullname" . }}-frontend + labels: + {{- include "invy.labels" . | nindent 4 }} + app.kubernetes.io/component: frontend +spec: + type: {{ .Values.frontend.service.type }} + ports: + - port: {{ .Values.frontend.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "invy.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: frontend diff --git a/charts/invy-chart/templates/secret.yaml b/charts/invy-chart/templates/secret.yaml new file mode 100644 index 0000000..89776ef --- /dev/null +++ b/charts/invy-chart/templates/secret.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "invy.fullname" . }}-secrets + labels: + {{- include "invy.labels" . | nindent 4 }} +type: Opaque +stringData: + postgres-user: {{ .Values.postgres.user | quote }} + postgres-password: {{ .Values.postgres.password | quote }} + postgres-database: {{ .Values.postgres.database | quote }} + database-url: "postgresql://{{ .Values.postgres.user }}:{{ .Values.postgres.password }}@{{ include "invy.fullname" . }}-db:{{ .Values.postgres.port }}/{{ .Values.postgres.database }}" diff --git a/charts/invy-chart/templates/serviceaccount.yaml b/charts/invy-chart/templates/serviceaccount.yaml new file mode 100644 index 0000000..3b78f3c --- /dev/null +++ b/charts/invy-chart/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "invy.serviceAccountName" . }} + labels: + {{- include "invy.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/invy-chart/values.yaml b/charts/invy-chart/values.yaml new file mode 100644 index 0000000..5f45339 --- /dev/null +++ b/charts/invy-chart/values.yaml @@ -0,0 +1,136 @@ +global: + namespace: my-apps + imagePullSecrets: [] + +# Backend configuration +backend: + name: backend + replicaCount: 1 + image: + repository: harbor.dvirlabs.com/my-apps/invy-backend + pullPolicy: IfNotPresent + tag: "latest" + + service: + type: ClusterIP + port: 8000 + targetPort: 8000 + + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + + env: + PYTHONUNBUFFERED: "1" + + ingress: + enabled: true + className: "traefik" + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: api-invy.dvirlabs.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: api-invy-tls + hosts: + - api-invy.dvirlabs.com + +# Frontend configuration +frontend: + name: frontend + replicaCount: 1 + image: + repository: harbor.dvirlabs.com/my-apps/invy-frontend + pullPolicy: IfNotPresent + tag: "latest" + + service: + type: ClusterIP + port: 80 + targetPort: 80 + + env: + VITE_API_URL: "https://api-invy.dvirlabs.com" + + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 256Mi + + ingress: + enabled: true + className: "traefik" + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: invy.dvirlabs.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: invy-tls + hosts: + - invy.dvirlabs.com + +# PostgreSQL configuration +postgres: + name: db + image: + repository: postgres + tag: "16-alpine" + pullPolicy: IfNotPresent + + user: invy_user + password: invy_password + database: invy_db + port: 5432 + + service: + type: ClusterIP + port: 5432 + targetPort: 5432 + + persistence: + enabled: true + accessMode: ReadWriteOnce + storageClass: "nfs-client" + size: 10Gi + + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# Ingress configuration +ingress: + enabled: false # Individual frontend/backend ingress resources handle routing instead + className: "traefik" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: invy.dvirlabs.com + paths: + - path: / + pathType: Prefix + backend: frontend + tls: + - secretName: invy-tls + hosts: + - invy.dvirlabs.com diff --git a/manifests/invy/cname.yaml b/manifests/invy/cname.yaml new file mode 100644 index 0000000..bb333be --- /dev/null +++ b/manifests/invy/cname.yaml @@ -0,0 +1,2 @@ +enabled: true +hostname: ipify.dvirlabs.com \ No newline at end of file diff --git a/manifests/invy/values.yaml b/manifests/invy/values.yaml new file mode 100644 index 0000000..5f45339 --- /dev/null +++ b/manifests/invy/values.yaml @@ -0,0 +1,136 @@ +global: + namespace: my-apps + imagePullSecrets: [] + +# Backend configuration +backend: + name: backend + replicaCount: 1 + image: + repository: harbor.dvirlabs.com/my-apps/invy-backend + pullPolicy: IfNotPresent + tag: "latest" + + service: + type: ClusterIP + port: 8000 + targetPort: 8000 + + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + + env: + PYTHONUNBUFFERED: "1" + + ingress: + enabled: true + className: "traefik" + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: api-invy.dvirlabs.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: api-invy-tls + hosts: + - api-invy.dvirlabs.com + +# Frontend configuration +frontend: + name: frontend + replicaCount: 1 + image: + repository: harbor.dvirlabs.com/my-apps/invy-frontend + pullPolicy: IfNotPresent + tag: "latest" + + service: + type: ClusterIP + port: 80 + targetPort: 80 + + env: + VITE_API_URL: "https://api-invy.dvirlabs.com" + + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 256Mi + + ingress: + enabled: true + className: "traefik" + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: invy.dvirlabs.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: invy-tls + hosts: + - invy.dvirlabs.com + +# PostgreSQL configuration +postgres: + name: db + image: + repository: postgres + tag: "16-alpine" + pullPolicy: IfNotPresent + + user: invy_user + password: invy_password + database: invy_db + port: 5432 + + service: + type: ClusterIP + port: 5432 + targetPort: 5432 + + persistence: + enabled: true + accessMode: ReadWriteOnce + storageClass: "nfs-client" + size: 10Gi + + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# Ingress configuration +ingress: + enabled: false # Individual frontend/backend ingress resources handle routing instead + className: "traefik" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: invy.dvirlabs.com + paths: + - path: / + pathType: Prefix + backend: frontend + tls: + - secretName: invy-tls + hosts: + - invy.dvirlabs.com