diff --git a/deploy/helm/movienight/Chart.yaml b/deploy/helm/movienight/Chart.yaml new file mode 100644 index 0000000..6280bbb --- /dev/null +++ b/deploy/helm/movienight/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: movienight +description: MovieNight backend +type: application +version: 0.1.0 +appVersion: "0.0.1" diff --git a/deploy/helm/movienight/templates/NOTES.txt b/deploy/helm/movienight/templates/NOTES.txt new file mode 100644 index 0000000..9eb3212 --- /dev/null +++ b/deploy/helm/movienight/templates/NOTES.txt @@ -0,0 +1,25 @@ +MovieNight backend has been deployed. + +Backend: + Service: {{ include "movienight.fullname" . }}-backend + Port: {{ .Values.backend.service.port }} + +Postgres: +{{- if .Values.postgres.url }} + Using explicit SPRING_DATASOURCE_URL. +{{- else if .Values.postgres.existingSecret.name }} + Using secret {{ .Values.postgres.existingSecret.name }}. +{{- else if .Values.postgres.cluster.enabled }} + CNPG Cluster: {{ include "movienight.postgresClusterName" . }} + JDBC URL: {{ include "movienight.postgresJdbcUrl" . }} +{{- else }} + No Postgres values provided. The app will fall back to its embedded H2 defaults. +{{- end }} + +Gateway: +{{- if .Values.gateway.enabled }} + Gateway: {{ include "movienight.gatewayName" . }} + GatewayClass: {{ .Values.gateway.className }} +{{- else }} + Disabled. +{{- end }} diff --git a/deploy/helm/movienight/templates/_helpers.tpl b/deploy/helm/movienight/templates/_helpers.tpl new file mode 100644 index 0000000..5479b62 --- /dev/null +++ b/deploy/helm/movienight/templates/_helpers.tpl @@ -0,0 +1,125 @@ +{{- define "movienight.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "movienight.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := include "movienight.name" . -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "movienight.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" -}} +{{- end -}} + +{{- define "movienight.labels" -}} +helm.sh/chart: {{ include "movienight.chart" . }} +{{ include "movienight.selectorLabels" . }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with .Values.global.labels }} +{{ toYaml . }} +{{- end }} +{{- end -}} + +{{- define "movienight.selectorLabels" -}} +app.kubernetes.io/name: {{ include "movienight.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{- define "movienight.componentLabels" -}} +{{- $root := .root -}} +{{- $component := .component -}} +{{ include "movienight.labels" $root }} +app.kubernetes.io/component: {{ $component }} +{{- end -}} + +{{- define "movienight.componentSelectorLabels" -}} +{{- $root := .root -}} +{{- $component := .component -}} +{{ include "movienight.selectorLabels" $root }} +app.kubernetes.io/component: {{ $component }} +{{- end -}} + +{{- define "movienight.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} +{{- default (include "movienight.fullname" .) .Values.serviceAccount.name -}} +{{- else -}} +{{- default "default" .Values.serviceAccount.name -}} +{{- end -}} +{{- end -}} + +{{- define "movienight.gatewayName" -}} +{{- if .Values.gateway.name -}} +{{- .Values.gateway.name -}} +{{- else -}} +{{- printf "%s-gateway" (include "movienight.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{- define "movienight.postgresClusterName" -}} +{{- if .Values.postgres.cluster.name -}} +{{- .Values.postgres.cluster.name -}} +{{- else -}} +{{- printf "%s-postgres" (include "movienight.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{- define "movienight.postgresHost" -}} +{{- default (printf "%s-rw" (include "movienight.postgresClusterName" .)) .Values.postgres.cluster.host -}} +{{- end -}} + +{{- define "movienight.postgresJdbcUrl" -}} +{{- printf "jdbc:postgresql://%s:%v/%s" (include "movienight.postgresHost" .) (default 5432 .Values.postgres.cluster.port) .Values.postgres.cluster.database -}} +{{- end -}} + +{{- define "movienight.postgresEnv" -}} +{{- if .Values.postgres.url }} +- name: SPRING_DATASOURCE_URL + value: {{ .Values.postgres.url | quote }} +{{- if .Values.postgres.username }} +- name: SPRING_DATASOURCE_USERNAME + value: {{ .Values.postgres.username | quote }} +{{- end }} +{{- if .Values.postgres.password }} +- name: SPRING_DATASOURCE_PASSWORD + value: {{ .Values.postgres.password | quote }} +{{- end }} +{{- else if .Values.postgres.existingSecret.name }} +- name: SPRING_DATASOURCE_URL + valueFrom: + secretKeyRef: + name: {{ .Values.postgres.existingSecret.name }} + key: {{ .Values.postgres.existingSecret.urlKey }} +- name: SPRING_DATASOURCE_USERNAME + valueFrom: + secretKeyRef: + name: {{ .Values.postgres.existingSecret.name }} + key: {{ .Values.postgres.existingSecret.usernameKey }} +- name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.postgres.existingSecret.name }} + key: {{ .Values.postgres.existingSecret.passwordKey }} +{{- else if .Values.postgres.cluster.enabled }} +- name: SPRING_DATASOURCE_URL + value: {{ include "movienight.postgresJdbcUrl" . | quote }} +- name: SPRING_DATASOURCE_USERNAME + valueFrom: + secretKeyRef: + name: {{ required "postgres.cluster.bootstrapSecretName is required when postgres.cluster.enabled=true" .Values.postgres.cluster.bootstrapSecretName }} + key: username +- name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ required "postgres.cluster.bootstrapSecretName is required when postgres.cluster.enabled=true" .Values.postgres.cluster.bootstrapSecretName }} + key: password +{{- end -}} +{{- end -}} diff --git a/deploy/helm/movienight/templates/backend/deployment.yaml b/deploy/helm/movienight/templates/backend/deployment.yaml new file mode 100644 index 0000000..2a03d9d --- /dev/null +++ b/deploy/helm/movienight/templates/backend/deployment.yaml @@ -0,0 +1,92 @@ +{{- if .Values.backend.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "movienight.fullname" . }}-backend + labels: + {{- include "movienight.componentLabels" (dict "root" . "component" "backend") | nindent 4 }} + {{- with .Values.global.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.backend.replicaCount }} + selector: + matchLabels: + {{- include "movienight.componentSelectorLabels" (dict "root" . "component" "backend") | nindent 6 }} + template: + metadata: + labels: + {{- include "movienight.componentSelectorLabels" (dict "root" . "component" "backend") | nindent 8 }} + {{- with .Values.backend.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.backend.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "movienight.serviceAccountName" . }} + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.backend.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- $postgresEnv := include "movienight.postgresEnv" . | trim }} + containers: + - name: backend + image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}" + imagePullPolicy: {{ .Values.backend.image.pullPolicy }} + {{- with .Values.backend.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.backend.service.port }} + protocol: TCP + {{- if or $postgresEnv .Values.backend.env }} + env: +{{- if $postgresEnv }} +{{- $postgresEnv | nindent 12 }} +{{- end }} +{{- with .Values.backend.env }} +{{- toYaml . | nindent 12 }} +{{- end }} + {{- end }} + {{- with .Values.backend.envFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.backend.startupProbe }} + startupProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.backend.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.backend.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.backend.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.backend.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.backend.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.backend.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/movienight/templates/backend/service.yaml b/deploy/helm/movienight/templates/backend/service.yaml new file mode 100644 index 0000000..1a4ca83 --- /dev/null +++ b/deploy/helm/movienight/templates/backend/service.yaml @@ -0,0 +1,21 @@ +{{- if .Values.backend.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "movienight.fullname" . }}-backend + labels: + {{- include "movienight.componentLabels" (dict "root" . "component" "backend") | nindent 4 }} + {{- with .Values.global.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.backend.service.type }} + ports: + - name: http + port: {{ .Values.backend.service.port }} + targetPort: http + protocol: TCP + selector: + {{- include "movienight.componentSelectorLabels" (dict "root" . "component" "backend") | nindent 4 }} +{{- end }} diff --git a/deploy/helm/movienight/templates/gateway/gateway.yaml b/deploy/helm/movienight/templates/gateway/gateway.yaml new file mode 100644 index 0000000..f2a2f4c --- /dev/null +++ b/deploy/helm/movienight/templates/gateway/gateway.yaml @@ -0,0 +1,50 @@ +{{- if .Values.gateway.enabled }} +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: {{ include "movienight.gatewayName" . }} + labels: + {{- include "movienight.componentLabels" (dict "root" . "component" "gateway") | nindent 4 }} + {{- with .Values.gateway.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.global.annotations .Values.gateway.annotations }} + annotations: + {{- with .Values.global.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.gateway.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +spec: + gatewayClassName: {{ required "gateway.className is required when gateway.enabled=true" .Values.gateway.className | quote }} + listeners: + {{- if .Values.gateway.http.enabled }} + - name: http + protocol: HTTP + port: {{ .Values.gateway.http.port }} + {{- if .Values.gateway.listenerHostname }} + hostname: {{ .Values.gateway.listenerHostname | quote }} + {{- end }} + allowedRoutes: + namespaces: + from: Same + {{- end }} + {{- if .Values.gateway.https.enabled }} + - name: https + protocol: HTTPS + port: {{ .Values.gateway.https.port }} + {{- if .Values.gateway.listenerHostname }} + hostname: {{ .Values.gateway.listenerHostname | quote }} + {{- end }} + tls: + mode: Terminate + certificateRefs: + - kind: Secret + name: {{ required "gateway.https.secretName is required when gateway.https.enabled=true" .Values.gateway.https.secretName }} + allowedRoutes: + namespaces: + from: Same + {{- end }} +{{- end }} diff --git a/deploy/helm/movienight/templates/gateway/httproute.yaml b/deploy/helm/movienight/templates/gateway/httproute.yaml new file mode 100644 index 0000000..5449768 --- /dev/null +++ b/deploy/helm/movienight/templates/gateway/httproute.yaml @@ -0,0 +1,29 @@ +{{- if and .Values.routes.enabled .Values.gateway.enabled }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "movienight.fullname" . }} + labels: + {{- include "movienight.componentLabels" (dict "root" . "component" "route") | nindent 4 }} + {{- with .Values.global.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + parentRefs: + - name: {{ include "movienight.gatewayName" . }} + {{- if .Values.gateway.hostnames }} + hostnames: + {{- toYaml .Values.gateway.hostnames | nindent 4 }} + {{- end }} + rules: + {{- if and .Values.routes.backend.enabled .Values.backend.enabled }} + - matches: + - path: + type: PathPrefix + value: {{ .Values.routes.backend.pathPrefix | quote }} + backendRefs: + - name: {{ include "movienight.fullname" . }}-backend + port: {{ .Values.backend.service.port }} + {{- end }} +{{- end }} diff --git a/deploy/helm/movienight/templates/postgres/cluster.yaml b/deploy/helm/movienight/templates/postgres/cluster.yaml new file mode 100644 index 0000000..57212c1 --- /dev/null +++ b/deploy/helm/movienight/templates/postgres/cluster.yaml @@ -0,0 +1,36 @@ +{{- if .Values.postgres.cluster.enabled }} +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: {{ include "movienight.postgresClusterName" . }} + labels: + {{- include "movienight.componentLabels" (dict "root" . "component" "postgres") | nindent 4 }} + {{- with .Values.postgres.cluster.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.global.annotations .Values.postgres.cluster.annotations }} + annotations: + {{- with .Values.global.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.postgres.cluster.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +spec: + instances: {{ .Values.postgres.cluster.instances }} + storage: + size: {{ .Values.postgres.cluster.storage.size | quote }} + {{- if .Values.postgres.cluster.storage.storageClass }} + storageClass: {{ .Values.postgres.cluster.storage.storageClass | quote }} + {{- end }} + bootstrap: + initdb: + database: {{ .Values.postgres.cluster.database | quote }} + owner: {{ .Values.postgres.cluster.owner | quote }} + secret: + name: {{ required "postgres.cluster.bootstrapSecretName is required when postgres.cluster.enabled=true" .Values.postgres.cluster.bootstrapSecretName }} + {{- with .Values.postgres.cluster.extraSpec }} + {{- toYaml . | nindent 2 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/movienight/templates/rbac/serviceaccount.yaml b/deploy/helm/movienight/templates/rbac/serviceaccount.yaml new file mode 100644 index 0000000..9930827 --- /dev/null +++ b/deploy/helm/movienight/templates/rbac/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "movienight.serviceAccountName" . }} + labels: + {{- include "movienight.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/movienight/values.schema.json b/deploy/helm/movienight/values.schema.json new file mode 100644 index 0000000..395fdcc --- /dev/null +++ b/deploy/helm/movienight/values.schema.json @@ -0,0 +1,246 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "definitions": { + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "envVar": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "valueFrom": { + "type": "object", + "additionalProperties": true + } + }, + "required": ["name"], + "additionalProperties": true + }, + "image": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "pullPolicy": { + "type": "string" + } + }, + "additionalProperties": true + }, + "probe": { + "type": "object", + "additionalProperties": true + } + }, + "properties": { + "nameOverride": { + "type": "string" + }, + "fullnameOverride": { + "type": "string" + }, + "global": { + "type": "object", + "properties": { + "imagePullSecrets": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "labels": { + "$ref": "#/definitions/labels" + }, + "annotations": { + "$ref": "#/definitions/annotations" + } + }, + "additionalProperties": true + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "annotations": { + "$ref": "#/definitions/annotations" + } + }, + "additionalProperties": true + }, + "postgres": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "existingSecret": { + "type": "object", + "additionalProperties": true + }, + "cluster": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "instances": { + "type": "integer" + }, + "database": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "bootstrapSecretName": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "storage": { + "type": "object", + "additionalProperties": true + }, + "labels": { + "$ref": "#/definitions/labels" + }, + "annotations": { + "$ref": "#/definitions/annotations" + }, + "extraSpec": { + "type": "object", + "additionalProperties": true + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true + }, + "backend": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "replicaCount": { + "type": "integer" + }, + "image": { + "$ref": "#/definitions/image" + }, + "service": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "port": { + "type": "integer" + } + }, + "additionalProperties": true + }, + "env": { + "type": "array", + "items": { + "$ref": "#/definitions/envVar" + } + }, + "envFrom": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + }, + "podAnnotations": { + "$ref": "#/definitions/annotations" + }, + "podLabels": { + "$ref": "#/definitions/labels" + }, + "resources": { + "type": "object", + "additionalProperties": true + }, + "securityContext": { + "type": "object", + "additionalProperties": true + }, + "podSecurityContext": { + "type": "object", + "additionalProperties": true + }, + "nodeSelector": { + "type": "object", + "additionalProperties": true + }, + "tolerations": { + "type": "array" + }, + "affinity": { + "type": "object", + "additionalProperties": true + }, + "livenessProbe": { + "$ref": "#/definitions/probe" + }, + "readinessProbe": { + "$ref": "#/definitions/probe" + }, + "startupProbe": { + "$ref": "#/definitions/probe" + } + }, + "additionalProperties": true + }, + "gateway": { + "type": "object", + "additionalProperties": true + }, + "routes": { + "type": "object", + "additionalProperties": true + } + } +} diff --git a/deploy/helm/movienight/values.yaml b/deploy/helm/movienight/values.yaml new file mode 100644 index 0000000..b787c63 --- /dev/null +++ b/deploy/helm/movienight/values.yaml @@ -0,0 +1,118 @@ +nameOverride: "" +fullnameOverride: "" + +global: + imagePullSecrets: [] + labels: {} + annotations: {} + +serviceAccount: + create: true + name: "" + annotations: {} + +postgres: + # Set url/username/password for a fixed database, or use existingSecret. + url: "" + username: "" + password: "" + existingSecret: + name: "" + urlKey: url + usernameKey: username + passwordKey: password + cluster: + enabled: false + name: "" + instances: 1 + database: postgres + owner: postgres + # Secret containing CNPG initdb owner credentials (username/password). + bootstrapSecretName: "" + host: "" + port: 5432 + storage: + size: 10Gi + storageClass: "" + labels: {} + annotations: {} + extraSpec: {} + +backend: + enabled: true + replicaCount: 1 + image: + repository: ghcr.io/devitq/movienight-backend + tag: latest + pullPolicy: IfNotPresent + service: + type: ClusterIP + port: 8080 + env: + - name: SERVER_PORT + value: "8080" + - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME + value: org.postgresql.Driver + - name: SPRING_FLYWAY_ENABLED + value: "true" + - name: SPRING_FLYWAY_LOCATIONS + value: classpath:db/migration + - name: SPRING_FLYWAY_BASELINE_ON_MIGRATE + value: "true" + - name: SPRING_H2_CONSOLE_ENABLED + value: "false" + envFrom: [] + podAnnotations: {} + podLabels: {} + resources: {} + securityContext: {} + podSecurityContext: {} + nodeSelector: {} + tolerations: [] + affinity: {} + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: http + initialDelaySeconds: 20 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + startupProbe: + httpGet: + path: /actuator/health + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + failureThreshold: 24 + +gateway: + enabled: false + name: "" + className: "" + labels: {} + annotations: {} + listenerHostname: "" + hostnames: [] + http: + enabled: true + port: 80 + https: + enabled: false + port: 443 + secretName: "" + +routes: + enabled: true + backend: + enabled: true + pathPrefix: /