Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,21 @@ jobs:
with:
fetch-depth: 0
- run: |
echo "MEISTERPLAN_SERVICE_CHART_VERSION=$(grep -Po '(?<=version: )[0-9.]+' charts/meisterplan-service/Chart.yaml)" >> "$GITHUB_ENV"
echo "SPRING_CHART_VERSION=$(grep -Po '(?<=version: )[0-9.]+' charts/spring-service/Chart.yaml)" >> "$GITHUB_ENV"
echo "CRONJOB_CHART_VERSION=$(grep -Po '(?<=version: )[0-9.]+' charts/cronjob/Chart.yaml)" >> "$GITHUB_ENV"
- run: |
git show-ref --tags --verify --quiet "refs/tags/spring-service-${SPRING_CHART_VERSION}" && \
echo "SPRING_EXISTS=true" || echo "SPRING_EXISTS=false" >> "$GITHUB_ENV"
git show-ref --tags --verify --quiet "refs/tags/spring-service-${MEISTERPLAN_SERVICE_CHART_VERSION}" && \
echo "MEISTERPLAN_SERVICE_EXISTS=true" || echo "MEISTERPLAN_SERVICE_EXISTS=false" >> "$GITHUB_ENV"
git show-ref --tags --verify --quiet "refs/tags/cronjob-${CRONJOB_CHART_VERSION}" && \
echo "CRONJOB_EXISTS=true" || echo "CRONJOB_EXISTS=false" >> "$GITHUB_ENV"
- uses: marocchino/sticky-pull-request-comment@v2
with:
message: |
Merging this PR will attempt to deploy these Charts (existing versions will be skipped):
${{ env.MEISTERPLAN_SERVICE_EXISTS == 'false' && format('* meisterplan-service **{0}**', env.MEISTERPLAN_SERVICE_CHART_VERSION) || '' }}
${{ env.SPRING_EXISTS == 'false' && format('* spring-service **{0}**', env.SPRING_CHART_VERSION) || '' }}
${{ env.CRONJOB_EXISTS == 'false' && format('* cronjob **{0}**', env.CRONJOB_CHART_VERSION) || '' }}

Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
# meisterplan-service

## 1.0.0

- Fork spring-service chart and introduce framework/language-agnostic features
- `serviceName` is now Helm-native `.Release.Name`
- `namespace` is now Helm-native `.Release.Namespace`
- Framework/language specifics are controlled by `platform` map, e.g. `platform.spring.enabled: true`
- Removed unnecessary image secrets outdated remnants
- Drop support for tracing before Spring Boot 3.4
- Restructure `values.yaml` to list common things first and then increase into more and more mad options
- Restructure `prometheusScraping`, `alertingRules` under `prometheus` and make path/port configurable
- Restructure all things specific to the [pod specification](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-template-v1/#PodTemplateSpec)
in a `podConfiguration` key (`env`, `resources`, `startupProbe`, `livenessProbe`, `readinessProbe`, `timeouts`, `enableServiceLinks`, `securityContext`).
- Restructure deployment and service-specific options under `deployment` and `service` keys.
- Cleanup zombie `basicAuthSecretParameterName` setting
- Document all values for ingress (no in-detail fixes because will be replaced by Gateway API soon)
- Introduce simple start for platform support for `nodejs`
- Service port 3000
- Sets `NODE_ENV` to `production`
- Expects an image with `check` utility and Liveness/Readiness probes via `check` utility and env vars `LIVENESS_CHECK`/`READINESS_CHECK` set
- Simple tracing toggle `TRACING_ENABLED`

# spring-service

## 5.5.0
Expand Down
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@ test:
$(MAKE) test-case CHART=spring-service CASE=complex-service
$(MAKE) test-case CHART=cronjob CASE=simple-cronjob
$(MAKE) test-case CHART=cronjob CASE=service-account-cronjob
$(MAKE) test-case-v2 CHART=meisterplan-service CASE=simple-spring-service
$(MAKE) test-case-v2 CHART=meisterplan-service CASE=complex-spring-service
$(MAKE) test-case-v2 CHART=meisterplan-service CASE=simple-nodejs-service
$(MAKE) test-version-in-changelog CHART=spring-service
$(MAKE) test-version-in-changelog CHART=cronjob
$(MAKE) test-version-in-changelog CHART=meisterplan-service

test-case:
rm -rf .test-output && mkdir -p .test-output
helm template --output-dir .test-output ./charts/$(CHART) -f tests/$(CHART)/$(CASE)/values.yaml -f tests/$(CHART)/$(CASE)/values.staging.yaml
diff -r tests/$(CHART)/$(CASE)/expected/ .test-output/
@echo "Test passed for $(CHART) - $(CASE)"

test-case-v2:
rm -rf .test-output && mkdir -p .test-output
helm template --namespace team-supercool --output-dir .test-output myservice ./charts/$(CHART) -f tests/$(CHART)/$(CASE)/values.yaml -f tests/$(CHART)/$(CASE)/values.staging.yaml
diff -r tests/$(CHART)/$(CASE)/expected/ .test-output/
@echo "Test passed for $(CHART) - $(CASE)"

test-case-promote-to-expected:
@test -n "$(CHART)" || (echo "Error: CHART must be set, e.g. spring-service."; exit 1)
@test -n "$(CASE)" || (echo "Error: CASE must be set, e.g. simple-service."; exit 1)
Expand Down
4 changes: 4 additions & 0 deletions charts/meisterplan-service/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
name: meisterplan-service
description: A generalized deployment for Meisterplan services in Kubernetes.
version: 1.0.0
44 changes: 44 additions & 0 deletions charts/meisterplan-service/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{{- define "is-ingress-public-defined" -}}
{{ and $.Values.ingress.public.subDomain
(or $.Values.ingress.public.paths.allowAll
(not (and (empty $.Values.ingress.public.paths.prefixes) (empty $.Values.ingress.public.paths.exact)))
)
}}
{{- end }}

{{- define "get-ingress-primary-public-host" }}
{{- if eq $.Values.ingress.public.subDomain "." -}}
{{ required "ingress.clusterDomain must be set!" $.Values.ingress.clusterDomain }}
{{ else -}}
{{ $.Values.ingress.public.subDomain }}.{{ required "ingress.clusterDomain must be set!" $.Values.ingress.clusterDomain }}
{{- end }}
{{- end }}

{{- define "k8s.annotations" }}
{{- $annotations := dict }}
{{- if ne .Values.logging.collect true }}
{{- $_ := set $annotations "com.meisterplan.logs/collect" (printf "%v" .Values.logging.collect) }}
{{- end }}
{{- if ne .Values.logging.retention "default" }}
{{- $_ := set $annotations "com.meisterplan.logs/retention" .Values.logging.retention }}
{{- end }}
{{- if $annotations }}
{{ toYaml $annotations }}
{{- end }}
{{- end }}

{{- define "get-platform" }}
{{- $activePlatforms := 0 }}
{{- range $platformName, $platformConfig := .Values.platform }}
{{- if $platformConfig.enabled }}
{{- $activePlatforms = add $activePlatforms 1 }}
{{- $platformName }}
{{- end }}
{{- end }}
{{- if eq $activePlatforms 0 }}
{{ fail "You have not activated any platform which this chart is currently not designed to do. Enable the one that applies!" }}
{{- end }}
{{- if gt $activePlatforms 1 }}
{{ fail "You have activated more than 1 platform which this chart is currently not designed to do. Disable the others or deploy multiple releases!" }}
{{- end }}
{{- end }}
162 changes: 162 additions & 0 deletions charts/meisterplan-service/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
kind: Deployment
apiVersion: apps/v1
metadata:
name: "{{ .Release.Name }}-{{ required "Image Tag must be set!" .Values.image.tag }}"
namespace: "{{ .Release.Namespace }}"
{{- $annotations := include "k8s.annotations" . -}}
{{- if $annotations }}
annotations:
{{- $annotations | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.deployment.replicaCount }}
{{- if .Values.deployment.progressDeadlineSeconds }}
progressDeadlineSeconds: {{ .Values.deployment.progressDeadlineSeconds }}
{{- end }}
selector:
matchLabels:
app.kubernetes.io/name: {{ .Release.Name | quote }}
app.kubernetes.io/version: {{ .Values.image.tag | quote }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ .Release.Name | quote }}
app.kubernetes.io/version: {{ .Values.image.tag | quote }}
spec:
{{- if .Values.serviceAccount.enabled }}
serviceAccountName: {{ .Release.Name }}
{{- end }}
{{- if .Values.podConfiguration.timeouts.terminationTimeBeforeKillSeconds }}
terminationGracePeriodSeconds: {{ .Values.podConfiguration.timeouts.terminationTimeBeforeKillSeconds }}
{{- end }}
topologySpreadConstraints:
{{- /* this causes pods to distribute evenly by applying these rules:
- schedule at most <maxSkew> pod more or less than the optimum per <topologyKey>
- schedule on at least <minDomains> topologies
- nodeTaintsPolicy is Ignore by default which would include unplacable nodes for calculation
*/}}
- topologyKey: kubernetes.io/hostname
maxSkew: 1
minDomains: 2
whenUnsatisfiable: DoNotSchedule
nodeTaintsPolicy: Honor
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- {{ .Release.Name | quote }}
- key: app.kubernetes.io/version
operator: In
values:
- {{ .Values.image.tag | quote }}
- topologyKey: topology.kubernetes.io/zone
maxSkew: 1
minDomains: 2
whenUnsatisfiable: DoNotSchedule
nodeTaintsPolicy: Honor
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- {{ .Release.Name | quote }}
- key: app.kubernetes.io/version
operator: In
values:
- {{ .Values.image.tag | quote }}
enableServiceLinks: {{ .Values.podConfiguration.enableServiceLinks }}
containers:
- name: {{ .Release.Name }}
image: "{{ required "repository must be set!" .Values.image.repository }}:{{ required "tag must be set!" .Values.image.tag }}"
imagePullPolicy: Always
ports:
- containerPort: {{ include "get-platform-service-port" . }}
protocol: TCP
name: service
{{ include "get-platform-additional-container-ports" . | indent 12 }}
{{- range $port := .Values.service.internalPorts }}
- name: {{ $port.name }}
protocol: {{ $port.protocol }}
containerPort: {{ $port.targetPort }}
{{- end }}
resources:
requests:
cpu: {{ required "CPU guarantee must be set!" .Values.podConfiguration.resources.cpu.guarantee }}
memory: {{ required "Memory requirement must be set!" .Values.podConfiguration.resources.memory }}
limits:
{{- if .Values.podConfiguration.resources.cpu.limit }}
cpu: {{ .Values.podConfiguration.resources.cpu.limit }}
{{- end }}
memory: {{ required "Memory requirement must be set!" .Values.podConfiguration.resources.memory }}

startupProbe:
exec:
command: ["check", "run", "-type", "liveness"]
periodSeconds: {{ .Values.podConfiguration.startupProbe.periodSeconds }}
failureThreshold: {{ .Values.podConfiguration.startupProbe.failAfterCount }}
livenessProbe:
exec:
command: ["check", "run", "-type", "liveness"]
failureThreshold: {{ .Values.podConfiguration.livenessProbe.failAfterCount }}
readinessProbe:
exec:
command: ["check", "run", "-type", "readiness"]
failureThreshold: {{ .Values.podConfiguration.readinessProbe.failAfterCount }}
lifecycle:
preStop:
exec:
command: ["sleep", "{{ .Values.podConfiguration.timeouts.terminationTimeBeforeSigTermSeconds }}"]
env:
- name: "CLUSTER_ID"
valueFrom:
configMapKeyRef:
name: region
key: cluster-id
- name: "ENV_NAME"
valueFrom:
configMapKeyRef:
name: region
key: env-name
- name: "DISASTER_RECOVERY_TEST"
valueFrom:
configMapKeyRef:
name: region
key: disaster-recovery-test

# Tracing environment configuration (Spring Boot 3.4+)
- name: "MANAGEMENT_OTLP_TRACING_ENDPOINT"
valueFrom:
configMapKeyRef:
name: tracing
key: otlp-endpoint

{{- if .Values.sentry.enabled }}
# Sentry
- name: 'SENTRY_DSN'
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SENTRY_DSN"
{{- end }}

{{ include "get-platform-envs" . | indent 12 }}
{{- if and .Values.podConfiguration.env.copyOwnExternalBaseURLIn (ne (include "is-ingress-public-defined" .) "false") }}
- name: "{{ .Values.podConfiguration.env.copyOwnExternalBaseURLIn }}"
value: "https://{{ include "get-ingress-primary-public-host" . }}"
{{- end }}
{{- range $key, $value := .Values.podConfiguration.env.fromSecret }}
- name: "{{ $key }}"
valueFrom:
secretKeyRef:
name: "{{ $.Release.Name }}-external-secret"
key: "{{ $key }}"
{{- end }}
{{- range $key, $value := .Values.podConfiguration.env.additional }}
- name: {{ $key }}
{{ toYaml $value | indent 14 }}
{{- end }}
{{- if .Values.podConfiguration.securityContext }}
securityContext:
{{ toYaml .Values.podConfiguration.securityContext | indent 12 }}
{{- end}}
105 changes: 105 additions & 0 deletions charts/meisterplan-service/templates/platforms/_envs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{{- define "get-platform-envs" }}
{{- $platform := include "get-platform" . }}
{{- if eq $platform "spring" }}
- name: "SPRING_PROFILES_ACTIVE"
value: "prod,tracing"
{{- if .Values.platform.spring.env.datasourceFromSecret }}
- name: "SPRING_DATASOURCE_URL"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_URL"
- name: "SPRING_DATASOURCE_USERNAME"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_USERNAME"
- name: "SPRING_DATASOURCE_PASSWORD"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_PASSWORD"
- name: "SPRING_FLYWAY_URL"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_URL"
- name: "SPRING_FLYWAY_USER"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_USERNAME"
- name: "SPRING_FLYWAY_PASSWORD"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_PASSWORD"
{{- else if .Values.platform.spring.env.datasourceFromSecretWithRLS }}
- name: "SPRING_DATASOURCE_URL"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_URL"
- name: "SPRING_DATASOURCE_USERNAME"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_USERNAME"
- name: "SPRING_DATASOURCE_PASSWORD"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_PASSWORD"
- name: "SPRING_FLYWAY_URL"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_DATASOURCE_URL"
- name: "SPRING_FLYWAY_USER"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_FLYWAY_USER"
- name: "SPRING_FLYWAY_PASSWORD"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_FLYWAY_PASSWORD"
{{- end }}
{{- if .Values.platform.spring.env.rabbitMQSecret }}
- name: "SPRING_RABBITMQ_HOST"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_RABBITMQ_HOST"
- name: "SPRING_RABBITMQ_SSL_ENABLED"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_RABBITMQ_SSL_ENABLED"
- name: "SPRING_RABBITMQ_USERNAME"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_RABBITMQ_USERNAME"
- name: "SPRING_RABBITMQ_PASSWORD"
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-external-secret
key: "SPRING_RABBITMQ_PASSWORD"
{{- end }}
{{- if .Values.platform.spring.env.oAuth2PublicKey }}
- name: "SERVICE_OAUTH2_PUBLIC_KEY"
valueFrom:
secretKeyRef:
name: "{{ .Release.Name }}-external-secret"
key: SERVICE_OAUTH2_PUBLIC_KEY
{{- end }}
{{- else if eq $platform "nodejs" }}
- name: "NODE_ENV"
value: "production"
- name: "TRACING_ENABLED"
value: "true"
{{- else }}{{ fail (printf "Platform %s: Envs not defined" $platform) }}
{{- end }}
{{- end }}
Loading