Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
29 changes: 29 additions & 0 deletions charts/go-boilerplate-ddd/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: v2
name: go-boilerplate-ddd-helm
description: A Helm chart for Go Boilerplate DDD - Lerian reference service template

type: application

home: https://github.com/LerianStudio/helm

sources:
- https://github.com/LerianStudio/helm/tree/main/charts/go-boilerplate-ddd
- https://github.com/LerianStudio/go-boilerplate-ddd

maintainers:
- name: "Lerian Studio"
email: "support@lerian.studio"

version: 1.0.0

appVersion: "1.0.0"

keywords:
- boilerplate
- reference
- template
- lerian
- ddd
- hexagonal

icon: https://avatars.githubusercontent.com/u/148895005?s=200&v=4
75 changes: 75 additions & 0 deletions charts/go-boilerplate-ddd/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "boilerplate.name" -}}
{{- default (default "go-boilerplate-ddd" .Values.nameOverride) | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name for go-boilerplate-ddd.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "boilerplate.fullname" -}}
{{- default (include "boilerplate.name" .) .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "boilerplate.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create go-boilerplate-ddd app version
*/}}
{{- define "boilerplate.defaultTag" -}}
{{- default .Chart.AppVersion .Values.boilerplate.image.tag }}
{{- end -}}

{{/*
Return valid go-boilerplate-ddd version label
*/}}
{{- define "boilerplate.versionLabelValue" -}}
{{ regexReplaceAll "[^-A-Za-z0-9_.]" (include "boilerplate.defaultTag" .) "-" | trunc 63 | trimAll "-" | trimAll "_" | trimAll "." | quote }}
{{- end -}}

{{/*
Common labels
*/}}
{{- define "boilerplate.labels" -}}
helm.sh/chart: {{ include "boilerplate.chart" .context }}
{{ include "boilerplate.selectorLabels" (dict "context" .context "component" .component "name" .name) }}
app.kubernetes.io/version: {{ include "boilerplate.versionLabelValue" .context }}
app.kubernetes.io/managed-by: {{ .context.Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "boilerplate.selectorLabels" -}}
app.kubernetes.io/name: {{ include "boilerplate.name" .context }}
app.kubernetes.io/instance: {{ .context.Release.Name }}
{{- if .component }}
app.kubernetes.io/component: {{ .component }}
{{- end }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "boilerplate.serviceAccountName" -}}
{{- if .Values.boilerplate.serviceAccount.create }}
{{- default (include "boilerplate.fullname" .) .Values.boilerplate.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.boilerplate.serviceAccount.name }}
{{- end }}
{{- end }}

{{/*
Expand the namespace of the release.
Allows overriding it for multi-namespace deployments in combined charts.
*/}}
{{- define "global.namespace" -}}
{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}}
{{- end }}
133 changes: 133 additions & 0 deletions charts/go-boilerplate-ddd/templates/bootstrap-postgres.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{{- if .Values.global.externalPostgresDefinitions.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "boilerplate.fullname" . }}-bootstrap-postgres
namespace: {{ include "global.namespace" . }}
labels:
{{- include "boilerplate.labels" (dict "context" . "component" "bootstrap" "name" "postgres") | nindent 4 }}
spec:
ttlSecondsAfterFinished: 300
completions: 1
parallelism: 1
backoffLimit: 3
template:
spec:
restartPolicy: Never
initContainers:
- name: wait-for-dependencies
image: busybox:1.37
env:
- name: DB_HOST
value: {{ .Values.global.externalPostgresDefinitions.connection.host | quote }}
- name: DB_PORT
value: {{ .Values.global.externalPostgresDefinitions.connection.port | quote }}
command:
- /bin/sh
- -c
- >
TIMEOUT=300;
ELAPSED=0;
echo "Checking $DB_HOST:$DB_PORT...";
while ! nc -z "$DB_HOST" "$DB_PORT"; do
if [ $ELAPSED -ge $TIMEOUT ]; then
echo "Timeout waiting for $DB_HOST:$DB_PORT after ${TIMEOUT}s";
exit 1;
fi;
echo "$DB_HOST:$DB_PORT is not ready yet, waiting... (${ELAPSED}s/${TIMEOUT}s)";
sleep 5;
ELAPSED=$((ELAPSED + 5));
done;
echo "$DB_HOST:$DB_PORT is ready!";
containers:
- name: psql
image: postgres:17
env:
- name: DB_HOST
value: {{ .Values.global.externalPostgresDefinitions.connection.host | quote }}
- name: DB_PORT
value: {{ .Values.global.externalPostgresDefinitions.connection.port | quote }}
- name: DB_USER_ADMIN
{{- if .Values.global.externalPostgresDefinitions.postgresAdminLogin.useExistingSecret.name }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.externalPostgresDefinitions.postgresAdminLogin.useExistingSecret.name | quote }}
key: DB_USER_ADMIN
{{- else }}
value: {{ .Values.global.externalPostgresDefinitions.postgresAdminLogin.username | quote }}
{{- end }}
- name: DB_ADMIN_PASSWORD
{{- if .Values.global.externalPostgresDefinitions.postgresAdminLogin.useExistingSecret.name }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.externalPostgresDefinitions.postgresAdminLogin.useExistingSecret.name | quote }}
key: DB_ADMIN_PASSWORD
{{- else }}
value: {{ .Values.global.externalPostgresDefinitions.postgresAdminLogin.password | quote }}
{{- end }}
- name: DB_PASSWORD_BOILERPLATE
{{- if .Values.global.externalPostgresDefinitions.boilerplateCredentials.useExistingSecret.name }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.externalPostgresDefinitions.boilerplateCredentials.useExistingSecret.name | quote }}
key: DB_PASSWORD_BOILERPLATE
{{- else }}
value: {{ .Values.global.externalPostgresDefinitions.boilerplateCredentials.password | quote }}
{{- end }}
- name: DB_DATABASE
value: postgres
command:
- /bin/sh
- -c
- |
set -euo pipefail
echo "=== Go Boilerplate DDD PostgreSQL Bootstrap ==="
echo "Host: $DB_HOST:$DB_PORT"
echo ""

echo "Checking existing PostgreSQL objects..."
DB_EXISTS=0
ROLE_EXISTS=0

if PGPASSWORD="$DB_ADMIN_PASSWORD" psql -At -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "$DB_DATABASE" -c "SELECT 1 FROM pg_database WHERE datname='go-boilerplate-ddd'" | grep -q 1; then
DB_EXISTS=1
fi
if PGPASSWORD="$DB_ADMIN_PASSWORD" psql -At -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "$DB_DATABASE" -c "SELECT 1 FROM pg_roles WHERE rolname='go-boilerplate-ddd'" | grep -q 1; then
ROLE_EXISTS=1
fi

if [ "$DB_EXISTS" = "1" ] && [ "$ROLE_EXISTS" = "1" ]; then
echo "PostgreSQL bootstrap already complete (database 'go-boilerplate-ddd' and role 'go-boilerplate-ddd' exist). Skipping creation."
else
# Create role if not exists
if [ "$ROLE_EXISTS" = "1" ]; then
echo "Role 'go-boilerplate-ddd' already exists. Skipping creation."
else
echo "Creating role 'go-boilerplate-ddd'..."
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "$DB_DATABASE" \
-v pwd="$DB_PASSWORD_BOILERPLATE" \
-c "CREATE ROLE \"go-boilerplate-ddd\" LOGIN PASSWORD :'pwd'"
fi

# Create database if not exists
if [ "$DB_EXISTS" = "1" ]; then
echo "Database 'go-boilerplate-ddd' already exists. Skipping creation."
else
echo "Creating database 'go-boilerplate-ddd'..."
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "$DB_DATABASE" -c "CREATE DATABASE \"go-boilerplate-ddd\" OWNER \"go-boilerplate-ddd\""
fi
fi

# Privileges (safe to run repeatedly)
echo "Ensuring privileges and schema permissions..."
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "$DB_DATABASE" -c "ALTER USER \"go-boilerplate-ddd\" CREATEDB" || true
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "$DB_DATABASE" -c "GRANT ALL PRIVILEGES ON DATABASE \"go-boilerplate-ddd\" TO \"go-boilerplate-ddd\""
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "go-boilerplate-ddd" -c "GRANT ALL ON SCHEMA public TO \"go-boilerplate-ddd\""
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "go-boilerplate-ddd" -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"go-boilerplate-ddd\""
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "go-boilerplate-ddd" -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO \"go-boilerplate-ddd\""
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "go-boilerplate-ddd" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO \"go-boilerplate-ddd\""
PGPASSWORD="$DB_ADMIN_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER_ADMIN" -d "go-boilerplate-ddd" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO \"go-boilerplate-ddd\""

echo ""
echo "=== Go Boilerplate DDD PostgreSQL Bootstrap completed successfully ==="
{{- end }}
60 changes: 60 additions & 0 deletions charts/go-boilerplate-ddd/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{{- if .Values.boilerplate.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "boilerplate.fullname" . }}
namespace: {{ include "global.namespace" . }}
labels:
{{- include "boilerplate.labels" (dict "context" . "component" .Values.boilerplate.name "name" .Values.boilerplate.name) | nindent 4 }}
data:
# Application Settings
ENV_NAME: {{ .Values.boilerplate.configmap.ENV_NAME | default "production" | quote }}
LOG_LEVEL: {{ .Values.boilerplate.configmap.LOG_LEVEL | default "info" | quote }}
SERVER_ADDRESS: {{ .Values.boilerplate.configmap.SERVER_ADDRESS | default ":8080" | quote }}

# CORS
CORS_ALLOWED_ORIGINS: {{ .Values.boilerplate.configmap.CORS_ALLOWED_ORIGINS | default "*" | quote }}

# Auth Plugin (Access Manager)
PLUGIN_AUTH_ENABLED: {{ .Values.boilerplate.configmap.PLUGIN_AUTH_ENABLED | default "false" | quote }}
PLUGIN_AUTH_HOST: {{ .Values.boilerplate.configmap.PLUGIN_AUTH_HOST | default "http://plugin-auth:4000" | quote }}

# PostgreSQL Database
POSTGRES_HOST: {{ .Values.boilerplate.configmap.POSTGRES_HOST | default "go-boilerplate-ddd-postgresql" | quote }}
POSTGRES_PORT: {{ .Values.boilerplate.configmap.POSTGRES_PORT | default "5432" | quote }}
POSTGRES_USER: {{ .Values.boilerplate.configmap.POSTGRES_USER | default "go-boilerplate-ddd" | quote }}
POSTGRES_NAME: {{ .Values.boilerplate.configmap.POSTGRES_NAME | default "go-boilerplate-ddd" | quote }}
POSTGRES_SSLMODE: {{ .Values.boilerplate.configmap.POSTGRES_SSLMODE | default "disable" | quote }}
MIGRATIONS_PATH: {{ .Values.boilerplate.configmap.MIGRATIONS_PATH | default "migrations" | quote }}

# Redis
REDIS_HOST: {{ .Values.boilerplate.configmap.REDIS_HOST | default "go-boilerplate-ddd-redis:6379" | quote }}
REDIS_DB: {{ .Values.boilerplate.configmap.REDIS_DB | default "0" | quote }}
REDIS_TLS: {{ .Values.boilerplate.configmap.REDIS_TLS | default "false" | quote }}

# OpenTelemetry (Observability)
ENABLE_TELEMETRY: {{ .Values.boilerplate.configmap.ENABLE_TELEMETRY | default "false" | quote }}
OTEL_RESOURCE_SERVICE_NAME: {{ .Values.boilerplate.configmap.OTEL_RESOURCE_SERVICE_NAME | default "go-boilerplate-ddd" | quote }}
OTEL_LIBRARY_NAME: {{ .Values.boilerplate.configmap.OTEL_LIBRARY_NAME | default "github.com/LerianStudio/go-boilerplate-ddd" | quote }}
OTEL_EXPORTER_OTLP_ENDPOINT: {{ .Values.boilerplate.configmap.OTEL_EXPORTER_OTLP_ENDPOINT | default "" | quote }}
OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT: {{ .Values.boilerplate.configmap.OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT | default "production" | quote }}

# Rate Limiting
RATE_LIMIT_ENABLED: {{ .Values.boilerplate.configmap.RATE_LIMIT_ENABLED | default "true" | quote }}
RATE_LIMIT_MAX: {{ .Values.boilerplate.configmap.RATE_LIMIT_MAX | default "500" | quote }}
RATE_LIMIT_WINDOW_SEC: {{ .Values.boilerplate.configmap.RATE_LIMIT_WINDOW_SEC | default "60" | quote }}

# Swagger Documentation
SWAGGER_ENABLED: {{ .Values.boilerplate.configmap.SWAGGER_ENABLED | default "true" | quote }}
SWAGGER_TITLE: {{ .Values.boilerplate.configmap.SWAGGER_TITLE | default "Go Boilerplate DDD" | quote }}

# Multi-tenant (disabled by default)
MULTI_TENANT_ENABLED: {{ .Values.boilerplate.configmap.MULTI_TENANT_ENABLED | default "false" | quote }}

# Extra Env Vars
{{- with .Values.boilerplate.extraEnvVars }}
{{- range . }}
{{ .name }}: {{ .value | quote }}
{{- end }}
{{- end }}
{{- end }}
94 changes: 94 additions & 0 deletions charts/go-boilerplate-ddd/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{{- if .Values.boilerplate.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "boilerplate.fullname" . }}
namespace: {{ include "global.namespace" . }}
labels:
{{- include "boilerplate.labels" (dict "context" . "component" .Values.boilerplate.name "name" .Values.boilerplate.name) | nindent 4 }}
spec:
revisionHistoryLimit: {{ .Values.boilerplate.revisionHistoryLimit | default 10 }}
strategy:
type: {{ .Values.boilerplate.deploymentUpdate.type }}
{{- if eq .Values.boilerplate.deploymentUpdate.type "RollingUpdate" }}
rollingUpdate:
maxSurge: {{ .Values.boilerplate.deploymentUpdate.maxSurge }}
maxUnavailable: {{ .Values.boilerplate.deploymentUpdate.maxUnavailable }}
{{- end }}
{{- if not .Values.boilerplate.autoscaling.enabled }}
replicas: {{ .Values.boilerplate.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "boilerplate.selectorLabels" (dict "context" . "name" .Values.boilerplate.name) | nindent 6 }}
template:
metadata:
{{- with .Values.boilerplate.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "boilerplate.labels" (dict "context" . "component" .Values.boilerplate.name "name" .Values.boilerplate.name) | nindent 8 }}
spec:
{{- with .Values.boilerplate.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "boilerplate.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.boilerplate.podSecurityContext | nindent 8 }}
containers:
- name: {{ include "boilerplate.fullname" . }}
securityContext:
{{- toYaml .Values.boilerplate.securityContext | nindent 12 }}
Comment on lines +38 to +43
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider defining secure defaults for security contexts.

The template correctly references podSecurityContext and securityContext from values, but if these aren't defined with secure defaults, containers may run with elevated privileges.

For a distroless nonroot image (as mentioned in the PR objectives), consider ensuring the values file includes:

🛡️ Recommended security context defaults for values.yaml
boilerplate:
  podSecurityContext:
    runAsNonRoot: true
    runAsUser: 65532
    runAsGroup: 65532
    fsGroup: 65532
    seccompProfile:
      type: RuntimeDefault

  securityContext:
    allowPrivilegeEscalation: false
    readOnlyRootFilesystem: true
    capabilities:
      drop:
        - ALL

This aligns with the PR objective of using "distroless nonroot image (UID 65532), readOnlyRootFilesystem" and addresses the Trivy findings (KSV-0001, KSV-0030, KSV-0104, KSV-0118).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@charts/go-boilerplate-ddd/templates/deployment.yaml` around lines 38 - 43,
The template uses .Values.boilerplate.podSecurityContext and
.Values.boilerplate.securityContext but has no secure defaults; update the Helm
chart values.yaml to include secure defaults for boilerplate.podSecurityContext
(runAsNonRoot: true, runAsUser/runAsGroup/fsGroup set to 65532, seccompProfile:
RuntimeDefault) and boilerplate.securityContext (allowPrivilegeEscalation:
false, readOnlyRootFilesystem: true, capabilities.drop: [ALL]) so the Deployment
(container named via include "boilerplate.fullname") will run as a distroless
nonroot user with minimal privileges when no overrides are provided.

image: "{{ .Values.boilerplate.image.repository }}:{{ .Values.boilerplate.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.boilerplate.image.pullPolicy }}
envFrom:
- secretRef:
name: {{ if .Values.boilerplate.useExistingSecret }}{{ .Values.boilerplate.existingSecretName }}{{ else }}{{ include "boilerplate.fullname" . }}{{ end }}
- configMapRef:
name: {{ include "boilerplate.fullname" . }}
{{- if eq (toString .Values.boilerplate.configmap.ENABLE_TELEMETRY) "true" }}
env:
- name: "HOST_IP"
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: "OTEL_EXPORTER_OTLP_ENDPOINT"
value: "$(HOST_IP):4317"
{{- end }}
ports:
- name: http
containerPort: {{ .Values.boilerplate.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 15
periodSeconds: 20
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
resources:
{{- toYaml .Values.boilerplate.resources | nindent 12 }}
{{- with .Values.boilerplate.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.boilerplate.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.boilerplate.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
Loading
Loading