From 8039e6712f2dc4b5c47d8de1cdb05411f7b49e9c Mon Sep 17 00:00:00 2001
From: Frank Sauerburger <frank@sauerburger.com>
Date: Tue, 25 Jan 2022 17:58:20 +0100
Subject: [PATCH] Add draft chart

---
 app/Dockerfile                          |   2 +-
 app/kube/settings.py                    |   6 +-
 app/requirements.txt                    |   1 +
 app/run.sh                              |   1 -
 k8s-templates/.helmignore               |  23 +++
 k8s-templates/Chart.yaml                |  24 +++
 k8s-templates/templates/_helpers.tpl    |  62 +++++++
 k8s-templates/templates/deployment.yaml | 222 ++++++++++++++++++++++++
 k8s-templates/templates/ingress.yaml    |  25 +++
 k8s-templates/templates/pvc.yaml        |  27 +++
 k8s-templates/templates/secrets.yaml    |  35 ++++
 k8s-templates/templates/services.yaml   |  36 ++++
 k8s-templates/values.yaml               |  25 +++
 nginx/Dockerfile                        |   2 +-
 nginx/nginx.conf                        |   9 +-
 15 files changed, 490 insertions(+), 10 deletions(-)
 create mode 100644 k8s-templates/.helmignore
 create mode 100644 k8s-templates/Chart.yaml
 create mode 100644 k8s-templates/templates/_helpers.tpl
 create mode 100644 k8s-templates/templates/deployment.yaml
 create mode 100644 k8s-templates/templates/ingress.yaml
 create mode 100644 k8s-templates/templates/pvc.yaml
 create mode 100644 k8s-templates/templates/secrets.yaml
 create mode 100644 k8s-templates/templates/services.yaml
 create mode 100644 k8s-templates/values.yaml

diff --git a/app/Dockerfile b/app/Dockerfile
index 71fda42..8cbc882 100644
--- a/app/Dockerfile
+++ b/app/Dockerfile
@@ -1,6 +1,6 @@
 FROM docker.sauerburger.com/python:3.10.1
 
-RUN pip install uwsgi
+RUN pip install --upgrade pip
 COPY requirements.txt /app/
 RUN pip install -r /app/requirements.txt
 RUN rm /app/requirements.txt
diff --git a/app/kube/settings.py b/app/kube/settings.py
index bf89f73..d33bcd0 100644
--- a/app/kube/settings.py
+++ b/app/kube/settings.py
@@ -30,10 +30,10 @@ if is_production or is_staging:
     DATABASES = {
         'default': {
             'ENGINE': 'django.db.backends.postgresql_psycopg2',
-            'NAME': 'kubetemplates',
-            'USER': 'webapp',
+            'NAME': os.environ.get("DB_NAME"),
+            'USER': os.environ.get("DB_USERNAME"),
             'PASSWORD': os.environ["DB_PASSWORD"],
-            'HOST': 'database',
+            'HOST': os.environ["DB_HOST"],
             'PORT': '5432',
         }
     }
diff --git a/app/requirements.txt b/app/requirements.txt
index 4beb54a..fbbd890 100644
--- a/app/requirements.txt
+++ b/app/requirements.txt
@@ -5,3 +5,4 @@ django-libsass~=0.9.0
 psycopg2~=2.9.2
 django-pygmentify~=0.3.7
 crispy-bootstrap5~=0.6
+uwsgi~=2.0.20
diff --git a/app/run.sh b/app/run.sh
index a1ddb69..81d45a5 100755
--- a/app/run.sh
+++ b/app/run.sh
@@ -2,7 +2,6 @@
 set -e
 python3 manage.py collectstatic --no-input
 python3 manage.py compress
-chown app:app -R /app/static
 rm -rf /app/webcontent/static
 cp -a /app/static /app/webcontent/static
 
diff --git a/k8s-templates/.helmignore b/k8s-templates/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/k8s-templates/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/k8s-templates/Chart.yaml b/k8s-templates/Chart.yaml
new file mode 100644
index 0000000..1af45cd
--- /dev/null
+++ b/k8s-templates/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: k8s-templates
+description: A helm chart for the kubernetes template app
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.6
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "0.1.5"
diff --git a/k8s-templates/templates/_helpers.tpl b/k8s-templates/templates/_helpers.tpl
new file mode 100644
index 0000000..b628fc0
--- /dev/null
+++ b/k8s-templates/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "k8s-templates.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "k8s-templates.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 "k8s-templates.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "k8s-templates.labels" -}}
+helm.sh/chart: {{ include "k8s-templates.chart" . }}
+{{ include "k8s-templates.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "k8s-templates.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "k8s-templates.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "k8s-templates.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "k8s-templates.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/k8s-templates/templates/deployment.yaml b/k8s-templates/templates/deployment.yaml
new file mode 100644
index 0000000..1d277a6
--- /dev/null
+++ b/k8s-templates/templates/deployment.yaml
@@ -0,0 +1,222 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Release.Name }}-backend
+  labels:
+    app.kubernetes.io/part-of: {{ .Release.Name }}
+    app.kubernetes.io/name: k8s-templates
+    app.kubernetes.io/instance: k8s-templates-main
+    app.kubernetes.io/version: "0.1.5"
+    app.kubernetes.io/component: backend
+spec:
+  replicas: 2
+  selector:
+    matchLabels:
+      app.kubernetes.io/part-of: {{ .Release.Name }}
+      app.kubernetes.io/instance: k8s-templates-main
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/part-of: {{ .Release.Name }}
+        app.kubernetes.io/name: k8s-templates
+        app.kubernetes.io/instance: k8s-templates-main
+        app.kubernetes.io/version: "0.1.5"
+        app.kubernetes.io/component: backend
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      containers:
+       - name: backend
+         image: gitlab.sauerburger.com:5049/frank/k8s-templates/k8s-templates-backend:0.1.5
+         ports:
+          - containerPort: 8080
+         env:
+          - name: DB_PASSWORD
+            valueFrom:
+             secretKeyRef:
+               name: {{ .Release.Name }}-db-secret
+               key: password
+          - name: DB_USERNAME
+            value: {{ .Values.database.user | quote }}
+          - name: DB_NAME
+            value: {{ .Values.database.name | quote }}
+          - name: DB_HOST
+            value: {{ .Release.Name }}-database-service
+          - name: PRODUCTION
+            value: {{ .Values.production | quote }}
+          - name: STAGING
+            value: {{ .Values.staging | quote }}
+          - name: SECRET_KEY
+            valueFrom:
+             secretKeyRef:
+               name: {{ .Release.Name }}-app-secret
+               key: password
+          - name: SUPER_EMAIL
+            value: {{ .Values.super.email | quote }}
+          - name: SUPER_USERNAME
+            value: {{ .Values.super.username | quote }}
+          - name: SUPER_PASSWORD
+            valueFrom:
+             secretKeyRef:
+               name: {{ .Release.Name }}-super-secret
+               key: password
+         resources:
+           requests:
+             memory: 100M
+             cpu: 100m
+           limits:
+             memory: 2000M
+             cpu: 2000m
+         readinessProbe:
+           tcpSocket:
+             port: 8080
+           initialDelaySeconds: 5
+           periodSeconds: 30
+
+         livenessProbe:
+           tcpSocket:
+             port: 8080
+           initialDelaySeconds: 120
+           periodSeconds: 60
+         volumeMounts:
+          - mountPath: /app/webcontent/static
+            name: static-files
+      volumes:
+        - name: static-files
+          persistentVolumeClaim:
+            claimName: {{ .Release.Name }}-static-files-pvc
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Release.Name }}-postgres
+  labels:
+    app.kubernetes.io/part-of: {{ .Release.Name }}
+    app.kubernetes.io/name: postgres
+    app.kubernetes.io/instance: postgres-main
+    app.kubernetes.io/version: "14.0"
+    app.kubernetes.io/component: datbase
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/part-of: {{ .Release.Name }}
+      app.kubernetes.io/instance: postgres-main
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/part-of: {{ .Release.Name }}
+        app.kubernetes.io/name: postgres
+        app.kubernetes.io/instance: postgres-main
+        app.kubernetes.io/version: "14.0"
+        app.kubernetes.io/component: datbase
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      containers:
+       - name: postgres
+         image: docker.sauerburger.com/postgres:14.0
+         ports:
+          - containerPort: 5432
+         env:
+          - name: POSTGRES_PASSWORD
+            valueFrom:
+             secretKeyRef:
+               name: {{ .Release.Name }}-db-secret
+               key: password
+          - name: POSTGRES_USER
+            value: {{ .Values.database.user | quote }}
+          - name: POSTGRES_DB
+            value: {{ .Values.database.name | quote}}
+          - name: PGDATA
+            value: /var/lib/postgresql/data/pgdata
+         resources:
+           requests:
+             memory: 500M
+             cpu: 100m
+           limits:
+             memory: 2000M
+             cpu: 2000m
+         readinessProbe:
+           tcpSocket:
+             port: 5432
+           initialDelaySeconds: 30
+           periodSeconds: 30
+
+         livenessProbe:
+           tcpSocket:
+             port: 5432
+           initialDelaySeconds: 120
+           periodSeconds: 30
+         volumeMounts:
+          - mountPath: /var/lib/postgresql/data
+            name: vol
+      volumes:
+        - name: vol
+          persistentVolumeClaim:
+            claimName: {{ .Release.Name }}-database-pvc
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Release.Name }}-webserver
+  labels:
+    app.kubernetes.io/part-of: {{ .Release.Name }}
+    app.kubernetes.io/name: nginx
+    app.kubernetes.io/instance: nginx-main
+    app.kubernetes.io/version: "0.1.0"
+    app.kubernetes.io/component: webserver
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/part-of: {{ .Release.Name }}
+      app.kubernetes.io/instance: nginx-main
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/part-of: {{ .Release.Name }}
+        app.kubernetes.io/name: nginx
+        app.kubernetes.io/instance: nginx-main
+        app.kubernetes.io/version: "1.21.5"
+        app.kubernetes.io/component: backend
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      containers:
+       - name: backend
+         image: docker.sauerburger.com/nginx:1.21.5
+         ports:
+          - containerPort: 8080
+         resources:
+           requests:
+             memory: 100M
+             cpu: 100m
+           limits:
+             memory: 2000M
+             cpu: 2000m
+         readinessProbe:
+           httpGet:
+             path: "/--healthz"
+             port: 8080
+           initialDelaySeconds: 5
+           periodSeconds: 30
+         livenessProbe:
+           httpGet:
+             path: "/--healthz"
+             port: 8080
+           initialDelaySeconds: 120
+           periodSeconds: 60
+         volumeMounts:
+          - mountPath: /app/webcontent/static
+            name: static-files
+      volumes:
+        - name: static-files
+          persistentVolumeClaim:
+            claimName: {{ .Release.Name }}-static-files-pvc
diff --git a/k8s-templates/templates/ingress.yaml b/k8s-templates/templates/ingress.yaml
new file mode 100644
index 0000000..badcf07
--- /dev/null
+++ b/k8s-templates/templates/ingress.yaml
@@ -0,0 +1,25 @@
+{{- if .Values.ingress.host -}}
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    {{ .Values.ingress.tlsIssuerType}}: {{ .Values.ingress.tlsIssuer | quote }}
+    kubernetes.io/ingress.class: nginx
+  name: {{ .Release.Name }}-k8s-templates-ingress
+spec:
+  rules:
+  - host: {{ .Values.ingress.host | quote }}
+    http:
+      paths:
+      - backend:
+          service:
+            name: {{ .Release.Name }}-k8s-templates-service
+            port:
+              number: 80
+        path: /
+        pathType: Prefix
+  tls:
+   - hosts:
+      - {{ .Values.ingress.host | quote }}
+     secretName: {{ .Release.Name }}-k8s-templates-tls-secret
+{{- end -}}
diff --git a/k8s-templates/templates/pvc.yaml b/k8s-templates/templates/pvc.yaml
new file mode 100644
index 0000000..410e9bb
--- /dev/null
+++ b/k8s-templates/templates/pvc.yaml
@@ -0,0 +1,27 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  name: {{ .Release.Name }}-database-pvc
+spec:
+  accessModes:
+    - ReadWriteMany
+  {{- if .Values.storage.database.persistentStorageClass }}
+  storageClassName: {{ .Values.storage.database.persistentStorageClass | quote }}
+  {{- end}}
+  resources:
+    requests:
+      storage: {{ .Values.storage.database.requestSize | quote }}
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  name: {{ .Release.Name }}-static-files-pvc
+spec:
+  accessModes:
+    - ReadWriteMany
+  {{- if .Values.storage.staticFiles.persistentStorageClass }}
+  storageClassName: {{ .Values.storage.staticFiles.persistentStorageClass | quote }}
+  {{- end }}
+  resources:
+    requests:
+      storage: 100Mi
diff --git a/k8s-templates/templates/secrets.yaml b/k8s-templates/templates/secrets.yaml
new file mode 100644
index 0000000..1a8445a
--- /dev/null
+++ b/k8s-templates/templates/secrets.yaml
@@ -0,0 +1,35 @@
+apiVersion: v1
+kind: Secret
+type: Opaque
+metadata:
+  name: {{ .Release.Name }}-app-secret
+data: 
+  {{- if .Release.IsInstall }}
+  password: {{ randAlphaNum 20 | b64enc }}
+  {{- else }}
+  password:  {{ (lookup "v1" "Secret" .Release.Namespace (print .Release.Name "-app-secret")).data.password }}
+  {{- end }}
+---
+apiVersion: v1
+kind: Secret
+type: Opaque
+metadata:
+  name: {{ .Release.Name }}-db-secret
+data: 
+  {{- if .Release.IsInstall }}
+  password: {{ randAlphaNum 20 | b64enc }}
+  {{- else }}
+  password:  {{ (lookup "v1" "Secret" .Release.Namespace (print .Release.Name "-db-secret")).data.password }}
+  {{- end }}
+---
+apiVersion: v1
+kind: Secret
+type: Opaque
+metadata:
+  name: {{ .Release.Name }}-super-secret
+data: 
+  {{- if .Release.IsInstall }}
+  password: {{ randAlphaNum 20 | b64enc }}
+  {{- else }}
+  password:  {{ (lookup "v1" "Secret" .Release.Namespace (print .Release.Name "-super-secret")).data.password }}
+  {{- end }}
diff --git a/k8s-templates/templates/services.yaml b/k8s-templates/templates/services.yaml
new file mode 100644
index 0000000..8ce3cd0
--- /dev/null
+++ b/k8s-templates/templates/services.yaml
@@ -0,0 +1,36 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ .Release.Name }}-database-service
+spec:
+  selector:
+    app.kubernetes.io/part-of: {{ .Release.Name }}
+    app.kubernetes.io/instance: postgres-main
+  ports:
+    - protocol: TCP
+      port: 5432
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: k8s-templates-backend-service
+spec:
+  selector:
+    app.kubernetes.io/part-of: {{ .Release.Name }}
+    app.kubernetes.io/instance: k8s-templates-main
+  ports:
+    - protocol: TCP
+      port: 80
+      targetPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ .Release.Name }}-web-service
+spec:
+  selector:
+    app.kubernetes.io/part-of: {{ .Release.Name }}
+    app.kubernetes.io/instance: nginx-main
+  ports:
+    - protocol: TCP
+      port: 80
diff --git a/k8s-templates/values.yaml b/k8s-templates/values.yaml
new file mode 100644
index 0000000..d792995
--- /dev/null
+++ b/k8s-templates/values.yaml
@@ -0,0 +1,25 @@
+ingress:
+  host: ""  # Leave empty to disable ingress
+  tlsIssuer: "letsencrypt-production"
+  tlsIssuerType: "cert-manager.io/cluster-issuer"
+
+super:
+  email: "admin@example.com"
+  username: "admin"
+
+database:
+  user: webapp
+  name: kubetemplates
+
+production: 1
+staging: ""
+
+imagePullSecrets: []
+
+storage:
+  database:
+    persistentStorageClass: ""  # Leave empty to use default
+    requestSize: 100Mi  
+  
+  staticFiles:
+    persistentStorageClass: ""  # Leave empty to use default
diff --git a/nginx/Dockerfile b/nginx/Dockerfile
index d2d5946..e896a29 100644
--- a/nginx/Dockerfile
+++ b/nginx/Dockerfile
@@ -1,2 +1,2 @@
-FROM docker.sauerburger.com/nginx:1.21
+FROM docker.sauerburger.com/nginx:1.21.5
 COPY nginx.conf /etc/nginx/nginx.conf
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
index 002115f..ce6962c 100644
--- a/nginx/nginx.conf
+++ b/nginx/nginx.conf
@@ -5,13 +5,10 @@ worker_processes  2;
 error_log  /var/log/nginx/error.log warn;
 pid        /var/run/nginx.pid;
 
-
 events {
     worker_connections  1024;
 }
 
-
-
 http {
     include       /etc/nginx/mime.types;
     default_type  application/octet-stream;
@@ -36,9 +33,13 @@ http {
         location /static {
             root /var/webcontent;
         }
+        location /--healthz {
+          add_header Content-Type text/plain;
+          return 200 'All systems go!';
+        }
         location / {
             include uwsgi_params;
-            uwsgi_pass kubetemplates:8080;
+            uwsgi_pass k8s-templates-backend-service:8080;
             uwsgi_param HTTP_X_FORWARDED_PROTO https;
         }
     }
-- 
GitLab