diff --git a/Chart.yaml b/Chart.yaml index 8bf3211..2b43489 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -15,4 +15,4 @@ type: library # 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.0 +version: 0.2.0 diff --git a/templates/_affinities.tpl b/templates/_affinities.tpl new file mode 100644 index 0000000..08ab89e --- /dev/null +++ b/templates/_affinities.tpl @@ -0,0 +1,107 @@ +{{/* +Pod and node affinity helpers. +Follows the Bitnami preset pattern (soft/hard) with full override support. + +Values pattern (per component): + podAffinityPreset: "" # "", "soft", or "hard" + podAntiAffinityPreset: "" # "", "soft", or "hard" + nodeAffinityPreset: + type: "" # "", "soft", or "hard" + key: "" # node label key + values: [] # node label values + affinity: {} # full override (ignores all presets when set) +*/}} + +{{/* +Return the topology key (defaults to kubernetes.io/hostname). +Usage: {{ include "comet-common.affinities.topologyKey" (dict "topologyKey" "BAR") }} +*/}} +{{- define "comet-common.affinities.topologyKey" -}} +{{ .topologyKey | default "kubernetes.io/hostname" -}} +{{- end -}} + +{{/* +Return a soft nodeAffinity definition. +Usage: {{ include "comet-common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) }} +*/}} +{{- define "comet-common.affinities.nodes.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - preference: + matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} + weight: 1 +{{- end -}} + +{{/* +Return a hard nodeAffinity definition. +Usage: {{ include "comet-common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) }} +*/}} +{{- define "comet-common.affinities.nodes.hard" -}} +requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} +{{- end -}} + +{{/* +Return a nodeAffinity definition (dispatches to soft or hard). +Usage: {{ include "comet-common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) }} +*/}} +{{- define "comet-common.affinities.nodes" -}} + {{- if eq .type "soft" }} + {{- include "comet-common.affinities.nodes.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "comet-common.affinities.nodes.hard" . -}} + {{- end -}} +{{- end -}} + +{{/* +Return a soft podAffinity/podAntiAffinity definition. +Uses comet-common.selectorLabels for matchLabels, passing componentName for app.kubernetes.io/component. +Usage: {{ include "comet-common.affinities.pods.soft" (dict "component" "FOO" "topologyKey" "BAR" "context" $) }} +*/}} +{{- define "comet-common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "comet-common.selectorLabels" (dict "componentName" $component "customLabels" (dict) "context" .context)) | nindent 10 }} + topologyKey: {{ include "comet-common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + weight: 1 +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition. +Uses comet-common.selectorLabels for matchLabels, passing componentName for app.kubernetes.io/component. +Usage: {{ include "comet-common.affinities.pods.hard" (dict "component" "FOO" "topologyKey" "BAR" "context" $) }} +*/}} +{{- define "comet-common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "comet-common.selectorLabels" (dict "componentName" $component "customLabels" (dict) "context" .context)) | nindent 8 }} + topologyKey: {{ include "comet-common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} +{{- end -}} + +{{/* +Return a podAffinity/podAntiAffinity definition (dispatches to soft or hard). +Returns empty string when type is "" (disabled). +Usage: {{ include "comet-common.affinities.pods" (dict "type" "soft" "component" "FOO" "context" $) }} +*/}} +{{- define "comet-common.affinities.pods" -}} + {{- if eq .type "soft" }} + {{- include "comet-common.affinities.pods.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "comet-common.affinities.pods.hard" . -}} + {{- end -}} +{{- end -}} diff --git a/templates/_images.tpl b/templates/_images.tpl index f16c6e4..cedf5dd 100644 --- a/templates/_images.tpl +++ b/templates/_images.tpl @@ -106,3 +106,12 @@ Return the proper image version (ingores image revision/prerelease info & fallba {{- print .chart.AppVersion -}} {{- end -}} {{- end -}} + +{{/* +Return the proper image pull policy. +Resolves: imageRoot.pullPolicy -> default "IfNotPresent" + {{ include "comet-common.images.pullPolicy" (dict "imageRoot" .Values.path.to.the.image) }} +*/}} +{{- define "comet-common.images.pullPolicy" -}} + {{- .imageRoot.pullPolicy | default "IfNotPresent" -}} +{{- end -}} diff --git a/templates/_mysql.tpl b/templates/_mysql.tpl new file mode 100644 index 0000000..989db53 --- /dev/null +++ b/templates/_mysql.tpl @@ -0,0 +1,147 @@ +{{/* +MySQL configuration helpers. +All helpers read from .Values.global.mysql.* which are propagated by Helm's +global value mechanism from the umbrella chart. + +Usage: + {{ include "comet-common.mysql.host" . }} + {{ include "comet-common.mysql.url" . }} + {{ include "comet-common.mysql.readOnlyUrl" . }} +*/}} + +{{/* ---- Basic Connection Settings ---- */}} + +{{- define "comet-common.mysql.host" -}} + {{- .Values.global.mysql.mysqlHost | default "comet-ml-mysql" -}} +{{- end -}} + +{{- define "comet-common.mysql.port" -}} + {{- .Values.global.mysql.mysqlPort | default 3306 -}} +{{- end -}} + +{{- define "comet-common.mysql.database" -}} + {{- .Values.global.mysql.mysqlDatabase | default "comet" -}} +{{- end -}} + +{{- define "comet-common.mysql.username" -}} + {{- .Values.global.mysql.mysqlUser | default "" -}} +{{- end -}} + +{{- define "comet-common.mysql.password" -}} + {{- .Values.global.mysql.mysqlPassword | default "" -}} +{{- end -}} + +{{/* ---- Admin Credentials (fallback to regular creds) ---- */}} + +{{- define "comet-common.mysql.adminUsername" -}} + {{- default (include "comet-common.mysql.username" .) .Values.global.mysql.mysqlAdminUser -}} +{{- end -}} + +{{- define "comet-common.mysql.adminPassword" -}} + {{- default (include "comet-common.mysql.password" .) .Values.global.mysql.mysqlAdminPassword -}} +{{- end -}} + +{{/* ---- TLS / Security ---- */}} + +{{- define "comet-common.mysql.useTLS" -}} + {{- .Values.global.mysql.useTLS | default false -}} +{{- end -}} + +{{- define "comet-common.mysql.requireTLS" -}} + {{- .Values.global.mysql.requireTLS | default false -}} +{{- end -}} + +{{- define "comet-common.mysql.verifyServerCert" -}} + {{- .Values.global.mysql.verifyServerCert | default false -}} +{{- end -}} + +{{- define "comet-common.mysql.allowPublicKeyRetrieval" -}} + {{- .Values.global.mysql.allowPublicKeyRetrieval | default false -}} +{{- end -}} + +{{/* ---- AWS / IAM ---- */}} + +{{- define "comet-common.mysql.awsRegion" -}} + {{- .Values.global.mysql.awsRegion | default "us-east-1" -}} +{{- end -}} + +{{- define "comet-common.mysql.iamEnabled" -}} + {{- .Values.global.mysql.mysqlIamEnabled | default false -}} +{{- end -}} + +{{- define "comet-common.mysql.iamServiceAccountEnabled" -}} + {{- .Values.global.mysql.mysqlIamServiceAccountEnabled | default false -}} +{{- end -}} + +{{/* ---- Connection Pool ---- */}} + +{{- define "comet-common.mysql.maxPoolSize" -}} + {{- .Values.global.mysql.maxPoolSize | default 50 -}} +{{- end -}} + +{{/* ---- Replicated MySQL (read-write / read-only) ---- */}} + +{{- define "comet-common.mysql.rw.host" -}} + {{- default (include "comet-common.mysql.host" .) .Values.global.mysql.replicated.rw.host -}} +{{- end -}} + +{{- define "comet-common.mysql.rw.username" -}} + {{- default (include "comet-common.mysql.username" .) .Values.global.mysql.replicated.rw.user -}} +{{- end -}} + +{{- define "comet-common.mysql.rw.password" -}} + {{- default (include "comet-common.mysql.password" .) .Values.global.mysql.replicated.rw.password -}} +{{- end -}} + +{{- define "comet-common.mysql.ro.host" -}} + {{- default (include "comet-common.mysql.host" .) .Values.global.mysql.replicated.ro.host -}} +{{- end -}} + +{{- define "comet-common.mysql.ro.username" -}} + {{- default (include "comet-common.mysql.username" .) .Values.global.mysql.replicated.ro.user -}} +{{- end -}} + +{{- define "comet-common.mysql.ro.password" -}} + {{- default (include "comet-common.mysql.password" .) .Values.global.mysql.replicated.ro.password -}} +{{- end -}} + +{{/* ---- Connection URL Builders ---- */}} + +{{/* +MySQL connection URL builder (replicated-aware). +When replicated.enabled is true, uses read-write credentials; otherwise uses primary. +Usage: {{ include "comet-common.mysql.url" . }} +*/}} +{{- define "comet-common.mysql.url" }} + {{- $port := include "comet-common.mysql.port" . -}} + {{- $database := include "comet-common.mysql.database" . -}} + {{- if .Values.global.mysql.replicated.enabled -}} + {{- $host := include "comet-common.mysql.rw.host" . -}} + {{- $user := include "comet-common.mysql.rw.username" . -}} + {{- $password := include "comet-common.mysql.rw.password" . -}} + {{- printf "mysql://%s:%s@%s:%s/%s" $user $password $host $port $database -}} + {{- else -}} + {{- $host := include "comet-common.mysql.host" . -}} + {{- $user := include "comet-common.mysql.username" . -}} + {{- $password := include "comet-common.mysql.password" . -}} + {{- printf "mysql://%s:%s@%s:%s/%s" $user $password $host $port $database -}} + {{- end -}} +{{- end }} + +{{/* +MySQL read-only connection URL builder. +Returns read-only URL when replicated.enabled, otherwise falls back to primary URL. +Usage: {{ include "comet-common.mysql.readOnlyUrl" . }} +*/}} +{{- define "comet-common.mysql.readOnlyUrl" }} + {{- if .Values.global.mysql.replicated.enabled -}} + {{- $port := include "comet-common.mysql.port" . -}} + {{- $database := include "comet-common.mysql.database" . -}} + {{- $host := include "comet-common.mysql.ro.host" . -}} + {{- $user := include "comet-common.mysql.ro.username" . -}} + {{- $password := include "comet-common.mysql.ro.password" . -}} + {{- printf "mysql://%s:%s@%s:%s/%s" $user $password $host $port $database -}} + {{- else -}} + {{- include "comet-common.mysql.url" . -}} + {{- end -}} +{{- end }} diff --git a/templates/_redis.tpl b/templates/_redis.tpl new file mode 100644 index 0000000..4db155e --- /dev/null +++ b/templates/_redis.tpl @@ -0,0 +1,88 @@ +{{/* +Redis configuration helpers. +All helpers read from .Values.global.redis.* which are propagated by Helm's +global value mechanism from the umbrella chart. + +Usage: + {{ include "comet-common.redis.value" (dict "root" . "key" "redisHost" "default" "comet-ml-redis-master") }} + {{ include "comet-common.redis.url" . }} + {{ include "comet-common.redis.url" (dict "root" . "db" "2" "tlsParams" true) }} +*/}} + +{{/* +Get Redis configuration value from .Values.global.redis.* +Usage: {{ include "comet-common.redis.value" (dict "root" . "key" "redisHost" "default" "comet-ml-redis-master") }} +*/}} +{{- define "comet-common.redis.value" -}} + {{- $root := .root -}} + {{- $key := .key -}} + {{- $default := .default | default "" -}} + {{- if hasKey $root.Values.global.redis $key -}} + {{- index $root.Values.global.redis $key -}} + {{- else -}} + {{- $default -}} + {{- end -}} +{{- end -}} + +{{/* +Build a Redis URL from an explicit config dict. +This is the shared implementation used by both comet-common.redis.url and chart-specific Redis helpers. +Usage: {{ include "comet-common.redis.buildUrl" (dict "host" "localhost" "port" "6379" "token" "NA" "user" "" "tls" "false" "db" "0" "tlsParams" false) }} +*/}} +{{- define "comet-common.redis.buildUrl" -}} + {{- $host := .host -}} + {{- $port := .port | toString -}} + {{- $token := .token | default "NA" | toString -}} + {{- $user := .user | default "" | toString -}} + {{- $tls := .tls | default "false" | toString -}} + {{- $db := .db | default "0" | toString -}} + {{- $tlsParams := .tlsParams | default false -}} + {{- $protocol := ternary "rediss" "redis" (eq $tls "true") -}} + {{- if or (eq $token "NA") (eq $token "") -}} + {{- if $user -}} + {{- printf "%s://%s@%s:%s/%s" $protocol $user $host $port $db -}} + {{- else -}} + {{- printf "%s://%s:%s/%s" $protocol $host $port $db -}} + {{- end -}} + {{- else -}} + {{- $url := "" -}} + {{- if $user -}} + {{- $url = printf "%s://%s:%s@%s:%s/%s" $protocol $user $token $host $port $db -}} + {{- else -}} + {{- $url = printf "%s://:%s@%s:%s/%s" $protocol $token $host $port $db -}} + {{- end -}} + {{- if and (eq $tls "true") $tlsParams -}} + {{- printf "%s?ssl_cert_reqs=none" $url -}} + {{- else -}} + {{- $url -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Generate Redis URL from global.redis.* values. +Supports both simple invocation (passing context directly) and dict-based parameters. +Usage: + {{ include "comet-common.redis.url" . }} # db=0, no TLS params + {{ include "comet-common.redis.url" (dict "root" . "db" "2") }} # db=2 + {{ include "comet-common.redis.url" (dict "root" . "db" "5" "tlsParams" true) }} # db=5 with TLS params +*/}} +{{- define "comet-common.redis.url" }} + {{- $root := . -}} + {{- $db := "0" -}} + {{- $tlsParams := false -}} + {{- if kindIs "map" . -}} + {{- $root = .root -}} + {{- $db = .db | default "0" -}} + {{- $tlsParams = .tlsParams | default false -}} + {{- end -}} + {{- include "comet-common.redis.buildUrl" (dict + "host" (include "comet-common.redis.value" (dict "root" $root "key" "redisHost" "default" "comet-redis-master")) + "port" (include "comet-common.redis.value" (dict "root" $root "key" "redisPort" "default" "6379")) + "token" (include "comet-common.redis.value" (dict "root" $root "key" "redisToken" "default" "NA")) + "user" (include "comet-common.redis.value" (dict "root" $root "key" "redisUser")) + "tls" (include "comet-common.redis.value" (dict "root" $root "key" "redisTLS" "default" "false")) + "db" $db + "tlsParams" $tlsParams + ) -}} +{{- end }} diff --git a/templates/_s3.tpl b/templates/_s3.tpl new file mode 100644 index 0000000..8b31b6d --- /dev/null +++ b/templates/_s3.tpl @@ -0,0 +1,36 @@ +{{/* +S3 / Object storage configuration helpers. +All helpers read from .Values.global.s3.* which are propagated by Helm's +global value mechanism from the umbrella chart. + +Usage: + {{ include "comet-common.s3.value" (dict "root" . "key" "bucket" "default" "comet-ml-data") }} + {{ include "comet-common.s3.url" . }} +*/}} + +{{/* +Get S3 configuration value from .Values.global.s3.* +Usage: {{ include "comet-common.s3.value" (dict "root" . "key" "keyID" "default" "") }} +*/}} +{{- define "comet-common.s3.value" -}} + {{- $root := .root -}} + {{- $key := .key -}} + {{- $default := .default | default "" -}} + {{- if hasKey $root.Values.global.s3 $key -}} + {{- index $root.Values.global.s3 $key -}} + {{- else -}} + {{- $default -}} + {{- end -}} +{{- end -}} + +{{/* +Normalize S3 URL with region. +Replaces generic "s3.amazonaws.com" with region-specific "s3.{region}.amazonaws.com". +Passes through non-AWS URLs (e.g. MinIO) unchanged. +Usage: {{ include "comet-common.s3.url" . }} +*/}} +{{- define "comet-common.s3.url" }} + {{- $s3URL := include "comet-common.s3.value" (dict "root" . "key" "url" "default" "https://s3.amazonaws.com") -}} + {{- $s3Region := include "comet-common.s3.value" (dict "root" . "key" "region" "default" "us-east-1") -}} + {{- mustRegexReplaceAll "^(http[s]?://)(s3)(\\.amazonaws\\.com)$" $s3URL (printf "https://${2}.%s${3}" $s3Region) }} +{{- end }} diff --git a/values.schema.json b/values.schema.json index 0d28d27..16bcf59 100644 --- a/values.schema.json +++ b/values.schema.json @@ -106,6 +106,169 @@ "type": "string" } } + }, + "mysql": { + "type": "object", + "description": "MySQL connection configuration (used by comet-common.mysql.* helpers)", + "properties": { + "mysqlHost": { + "type": "string", + "description": "MySQL server hostname" + }, + "mysqlPort": { + "type": "integer", + "description": "MySQL server port" + }, + "mysqlDatabase": { + "type": "string", + "description": "MySQL database name" + }, + "mysqlUser": { + "type": "string", + "description": "MySQL username" + }, + "mysqlPassword": { + "type": "string", + "description": "MySQL password" + }, + "mysqlAdminUser": { + "type": "string", + "description": "MySQL admin username (falls back to mysqlUser)" + }, + "mysqlAdminPassword": { + "type": "string", + "description": "MySQL admin password (falls back to mysqlPassword)" + }, + "useTLS": { + "type": "boolean", + "description": "Enable TLS for MySQL connections" + }, + "requireTLS": { + "type": "boolean", + "description": "Require TLS for MySQL connections" + }, + "verifyServerCert": { + "type": "boolean", + "description": "Verify MySQL server certificate" + }, + "allowPublicKeyRetrieval": { + "type": "boolean", + "description": "Allow MySQL public key retrieval" + }, + "awsRegion": { + "type": "string", + "description": "AWS region for IAM authentication" + }, + "mysqlIamEnabled": { + "type": "boolean", + "description": "Enable IAM authentication for MySQL" + }, + "mysqlIamServiceAccountEnabled": { + "type": "boolean", + "description": "Enable IAM service account authentication for MySQL" + }, + "maxPoolSize": { + "type": "integer", + "minimum": 1, + "description": "Maximum MySQL connection pool size" + }, + "replicated": { + "type": "object", + "description": "Replicated MySQL configuration (read-write / read-only)", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable replicated MySQL (separate rw/ro endpoints)" + }, + "rw": { + "type": "object", + "description": "Read-write endpoint", + "properties": { + "host": { "type": "string" }, + "user": { "type": "string" }, + "password": { "type": "string" } + } + }, + "ro": { + "type": "object", + "description": "Read-only endpoint", + "properties": { + "host": { "type": "string" }, + "user": { "type": "string" }, + "password": { "type": "string" } + } + } + } + }, + "clientCertificate": { + "type": "object", + "description": "MySQL client certificate configuration", + "properties": { + "enabled": { "type": "boolean" }, + "secretName": { "type": "string" } + } + } + } + }, + "redis": { + "type": "object", + "description": "Redis connection configuration (used by comet-common.redis.* helpers)", + "properties": { + "redisHost": { + "type": "string", + "description": "Redis server hostname" + }, + "redisPort": { + "type": "integer", + "description": "Redis server port" + }, + "redisToken": { + "type": "string", + "description": "Redis authentication token/password" + }, + "redisPassword": { + "type": "string", + "description": "Redis password (alternative to redisToken)" + }, + "redisUser": { + "type": "string", + "description": "Redis username (for ACL-based auth)" + }, + "redisTLS": { + "type": ["boolean", "string"], + "description": "Enable TLS for Redis connections" + } + } + }, + "s3": { + "type": "object", + "description": "S3/Object storage configuration (used by comet-common.s3.* helpers)", + "properties": { + "keyID": { + "type": "string", + "description": "S3 access key ID" + }, + "secretKey": { + "type": "string", + "description": "S3 secret access key" + }, + "bucket": { + "type": "string", + "description": "S3 bucket name" + }, + "region": { + "type": "string", + "description": "S3 region" + }, + "url": { + "type": "string", + "description": "S3 endpoint URL (for MinIO or custom endpoints)" + }, + "enableS3MetadataOnUpload": { + "type": ["boolean", "string"], + "description": "Enable S3 metadata on upload" + } + } } } }