diff --git a/karrot/app.yaml b/karrot/app.yaml new file mode 100644 index 0000000..9368b1a --- /dev/null +++ b/karrot/app.yaml @@ -0,0 +1,5 @@ +name: karrot +is: karrot +description: Karrot is a community platform for sharing resources, organizing food-saving activities, and coordinating volunteers. +icon: https://codeberg.org/karrot/karrot-frontend/raw/branch/main/src/logo/assets/carrot-logo.svg +latest: "1" diff --git a/karrot/versions/1/db-init-job.yaml b/karrot/versions/1/db-init-job.yaml new file mode 100644 index 0000000..2a07159 --- /dev/null +++ b/karrot/versions/1/db-init-job.yaml @@ -0,0 +1,58 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: karrot-db-init + labels: + component: db-init +spec: + template: + metadata: + labels: + component: db-init + spec: + restartPolicy: OnFailure + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + seccompProfile: + type: RuntimeDefault + containers: + - name: postgres-init + image: postgres:15 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + env: + - name: PGHOST + value: {{ .db.host }} + - name: PGUSER + value: postgres + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: karrot-secrets + key: postgres.password + - name: DB_NAME + value: {{ .db.name }} + - name: DB_USER + value: {{ .db.user }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: karrot-secrets + key: dbPassword + command: + - /bin/bash + - -c + - | + set -e + until pg_isready; do sleep 2; done + psql -c "CREATE DATABASE ${DB_NAME};" || echo "Database already exists" + psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" || echo "User already exists" + psql -c "ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" + psql -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};" + psql -d ${DB_NAME} -c "GRANT ALL ON SCHEMA public TO ${DB_USER};" + echo "Done" diff --git a/karrot/versions/1/deployment-backend.yaml b/karrot/versions/1/deployment-backend.yaml new file mode 100644 index 0000000..3213b24 --- /dev/null +++ b/karrot/versions/1/deployment-backend.yaml @@ -0,0 +1,174 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: karrot-backend + namespace: {{ .namespace }} +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + component: backend + template: + metadata: + labels: + component: backend + spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + initContainers: + - name: migrate + image: codeberg.org/karrot/karrot-backend:v17.4.1 + command: ["/entrypoint.sh", "migrate"] + env: + - name: MODE + value: prod + - name: BASE_URL + value: https://{{ .domain }} + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: karrot-secrets + key: secretKey + - name: DATABASE_NAME + value: {{ .db.name }} + - name: DATABASE_USER + value: {{ .db.user }} + - name: DATABASE_HOST + value: {{ .db.host }} + - name: DATABASE_PORT + value: "{{ .db.port }}" + - name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: karrot-secrets + key: dbPassword + - name: REDIS_HOST + value: {{ .redis.host }} + - name: EMAIL_BACKEND + value: smtp + - name: EMAIL_FROM + value: {{ .smtp.from }} + - name: SMTP_HOST + value: {{ .smtp.host }} + - name: SMTP_PORT + value: "{{ .smtp.port }}" + - name: SMTP_USER + value: {{ .smtp.user }} + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: karrot-secrets + key: smtp.password + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + volumeMounts: + - name: karrot-uploads + mountPath: /app/uploads + containers: + - name: karrot-backend + image: codeberg.org/karrot/karrot-backend:v17.4.1 + command: ["/entrypoint.sh", "server"] + ports: + - name: http + containerPort: 8000 + protocol: TCP + env: + - name: MODE + value: prod + - name: BASE_URL + value: https://{{ .domain }} + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: karrot-secrets + key: secretKey + - name: DATABASE_NAME + value: {{ .db.name }} + - name: DATABASE_USER + value: {{ .db.user }} + - name: DATABASE_HOST + value: {{ .db.host }} + - name: DATABASE_PORT + value: "{{ .db.port }}" + - name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: karrot-secrets + key: dbPassword + - name: REDIS_HOST + value: {{ .redis.host }} + - name: EMAIL_BACKEND + value: smtp + - name: EMAIL_FROM + value: {{ .smtp.from }} + - name: SMTP_HOST + value: {{ .smtp.host }} + - name: SMTP_PORT + value: "{{ .smtp.port }}" + - name: SMTP_USER + value: {{ .smtp.user }} + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: karrot-secrets + key: smtp.password + - name: LISTEN_HOST + value: "0.0.0.0:8000" + - name: ALLOWED_HOSTS + value: "{{ .domain }}" + - name: CSRF_TRUSTED_ORIGINS + value: "https://{{ .domain }}" + resources: + limits: + cpu: 1000m + ephemeral-storage: 1Gi + memory: 1Gi + requests: + cpu: 50m + ephemeral-storage: 50Mi + memory: 256Mi + volumeMounts: + - name: karrot-uploads + mountPath: /app/uploads + livenessProbe: + httpGet: + path: /api/ + port: 8000 + httpHeaders: + - name: Host + value: "{{ .domain }}" + initialDelaySeconds: 60 + timeoutSeconds: 5 + periodSeconds: 20 + failureThreshold: 6 + readinessProbe: + httpGet: + path: /api/ + port: 8000 + httpHeaders: + - name: Host + value: "{{ .domain }}" + initialDelaySeconds: 30 + timeoutSeconds: 3 + periodSeconds: 10 + failureThreshold: 3 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + volumes: + - name: karrot-uploads + persistentVolumeClaim: + claimName: karrot-uploads + restartPolicy: Always diff --git a/karrot/versions/1/deployment-frontend.yaml b/karrot/versions/1/deployment-frontend.yaml new file mode 100644 index 0000000..3b0455c --- /dev/null +++ b/karrot/versions/1/deployment-frontend.yaml @@ -0,0 +1,86 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: karrot-frontend + namespace: {{ .namespace }} +spec: + replicas: 1 + selector: + matchLabels: + component: frontend + template: + metadata: + labels: + component: frontend + spec: + securityContext: + runAsNonRoot: true + runAsUser: 101 + runAsGroup: 101 + seccompProfile: + type: RuntimeDefault + containers: + - name: karrot-frontend + image: codeberg.org/karrot/karrot-frontend:v17.4.1 + ports: + - name: http + containerPort: 8080 + protocol: TCP + env: + - name: BACKEND + value: karrot-backend:8000 + - name: FILE_UPLOAD_DIR + value: /app/uploads/ + - name: FILE_UPLOAD_MAX_SIZE + value: 10m + resources: + limits: + cpu: 200m + ephemeral-storage: 256Mi + memory: 128Mi + requests: + cpu: 10m + ephemeral-storage: 50Mi + memory: 32Mi + livenessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 10 + timeoutSeconds: 3 + periodSeconds: 15 + failureThreshold: 6 + readinessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 5 + timeoutSeconds: 3 + periodSeconds: 10 + failureThreshold: 3 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + volumeMounts: + - name: karrot-uploads + mountPath: /app/uploads + readOnly: true + - name: nginx-cache + mountPath: /var/cache/nginx + - name: nginx-conf + mountPath: /etc/nginx/conf.d + - name: nginx-run + mountPath: /var/run + volumes: + - name: karrot-uploads + persistentVolumeClaim: + claimName: karrot-uploads + - name: nginx-cache + emptyDir: {} + - name: nginx-conf + emptyDir: {} + - name: nginx-run + emptyDir: {} + restartPolicy: Always diff --git a/karrot/versions/1/deployment-worker.yaml b/karrot/versions/1/deployment-worker.yaml new file mode 100644 index 0000000..fcc93cf --- /dev/null +++ b/karrot/versions/1/deployment-worker.yaml @@ -0,0 +1,88 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: karrot-worker + namespace: {{ .namespace }} +spec: + replicas: 1 + selector: + matchLabels: + component: worker + template: + metadata: + labels: + component: worker + spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + containers: + - name: karrot-worker + image: codeberg.org/karrot/karrot-backend:v17.4.1 + command: ["/entrypoint.sh", "worker"] + env: + - name: MODE + value: prod + - name: BASE_URL + value: https://{{ .domain }} + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: karrot-secrets + key: secretKey + - name: DATABASE_NAME + value: {{ .db.name }} + - name: DATABASE_USER + value: {{ .db.user }} + - name: DATABASE_HOST + value: {{ .db.host }} + - name: DATABASE_PORT + value: "{{ .db.port }}" + - name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: karrot-secrets + key: dbPassword + - name: REDIS_HOST + value: {{ .redis.host }} + - name: EMAIL_BACKEND + value: smtp + - name: EMAIL_FROM + value: {{ .smtp.from }} + - name: SMTP_HOST + value: {{ .smtp.host }} + - name: SMTP_PORT + value: "{{ .smtp.port }}" + - name: SMTP_USER + value: {{ .smtp.user }} + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: karrot-secrets + key: smtp.password + resources: + limits: + cpu: 500m + ephemeral-storage: 512Mi + memory: 512Mi + requests: + cpu: 50m + ephemeral-storage: 50Mi + memory: 128Mi + volumeMounts: + - name: karrot-uploads + mountPath: /app/uploads + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + volumes: + - name: karrot-uploads + persistentVolumeClaim: + claimName: karrot-uploads + restartPolicy: Always diff --git a/karrot/versions/1/ingress.yaml b/karrot/versions/1/ingress.yaml new file mode 100644 index 0000000..58288b5 --- /dev/null +++ b/karrot/versions/1/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: karrot + namespace: {{ .namespace }} + annotations: + external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }} + external-dns.alpha.kubernetes.io/cloudflare-proxied: "false" + external-dns.alpha.kubernetes.io/ttl: "60" +spec: + ingressClassName: traefik + rules: + - host: {{ .domain }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: karrot-frontend + port: + number: 80 + tls: + - hosts: + - {{ .domain }} + secretName: {{ .tlsSecretName }} diff --git a/karrot/versions/1/kustomization.yaml b/karrot/versions/1/kustomization.yaml new file mode 100644 index 0000000..17d4459 --- /dev/null +++ b/karrot/versions/1/kustomization.yaml @@ -0,0 +1,19 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: {{ .namespace }} +labels: + - includeSelectors: true + pairs: + app: karrot + managedBy: kustomize + partOf: wild-cloud +resources: + - namespace.yaml + - db-init-job.yaml + - deployment-backend.yaml + - deployment-frontend.yaml + - deployment-worker.yaml + - redis.yaml + - service.yaml + - ingress.yaml + - pvc.yaml diff --git a/karrot/versions/1/manifest.yaml b/karrot/versions/1/manifest.yaml new file mode 100644 index 0000000..784c0bc --- /dev/null +++ b/karrot/versions/1/manifest.yaml @@ -0,0 +1,28 @@ +version: v17.4.1 +requires: + - name: postgres + - name: smtp +defaultConfig: + namespace: karrot + externalDnsDomain: '{{ .cloud.domain }}' + domain: karrot.{{ .cloud.domain }} + tlsSecretName: wildcard-wild-cloud-tls + storage: 2Gi + db: + host: '{{ .apps.postgres.host }}' + port: '{{ .apps.postgres.port }}' + name: karrot + user: karrot + redis: + host: karrot-redis + smtp: + host: '{{ .apps.smtp.host }}' + port: '{{ .apps.smtp.port }}' + from: '{{ .apps.smtp.from }}' + user: '{{ .apps.smtp.user }}' +defaultSecrets: + - key: secretKey + - key: dbPassword +requiredSecrets: + - postgres.password + - smtp.password diff --git a/karrot/versions/1/namespace.yaml b/karrot/versions/1/namespace.yaml new file mode 100644 index 0000000..054927e --- /dev/null +++ b/karrot/versions/1/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .namespace }} diff --git a/karrot/versions/1/pvc.yaml b/karrot/versions/1/pvc.yaml new file mode 100644 index 0000000..bb82973 --- /dev/null +++ b/karrot/versions/1/pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: karrot-uploads + namespace: {{ .namespace }} +spec: + accessModes: + - ReadWriteMany + storageClassName: nfs + resources: + requests: + storage: {{ .storage }} diff --git a/karrot/versions/1/redis.yaml b/karrot/versions/1/redis.yaml new file mode 100644 index 0000000..ed9c743 --- /dev/null +++ b/karrot/versions/1/redis.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: karrot-redis + namespace: karrot +spec: + replicas: 1 + selector: + matchLabels: + component: redis + template: + metadata: + labels: + component: redis + spec: + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + seccompProfile: + type: RuntimeDefault + containers: + - name: redis + image: redis:7-alpine + args: ["--save", ""] + ports: + - containerPort: 6379 + resources: + limits: + cpu: 200m + memory: 128Mi + requests: + cpu: 10m + memory: 32Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false +--- +apiVersion: v1 +kind: Service +metadata: + name: karrot-redis + namespace: karrot +spec: + selector: + component: redis + ports: + - port: 6379 + targetPort: 6379 diff --git a/karrot/versions/1/service.yaml b/karrot/versions/1/service.yaml new file mode 100644 index 0000000..32b149c --- /dev/null +++ b/karrot/versions/1/service.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: karrot-backend + namespace: {{ .namespace }} + labels: + component: backend +spec: + selector: + component: backend + ports: + - name: http + port: 8000 + targetPort: 8000 + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + name: karrot-frontend + namespace: {{ .namespace }} + labels: + component: frontend +spec: + selector: + component: frontend + ports: + - name: http + port: 80 + targetPort: 8080 + protocol: TCP